我在C#中有一个工作的DNLA设备(Xbox360,PSP ...)RSS视频源阅读器。它解析.opml文件以获取feed uri。
有时RSS提要项不会有持续时间值,因此我很难编写默认持续时间值。
我想获得视频文件的真实持续时间。
我的想法是使用httpWebRequest获取字节流并在文件二进制metaData中查找信息(如果可用)。我认为可以做到,但没有找到类似的例子。
该过程必须快速且不需要获取整个视频文件,因为持续时间值仅用于构建菜单。我期望以这种方式处理的文件是.flv,.m4v和.mp4。下面显示的示例适用于.flv文件:
using System;
using System.IO;
using System.Text;
using System.Net;
namespace myRSSVideoReader
{
public static class FlvMetadataReader
{
private const int BufferLength = 1000;
/// <summary>
/// Reads the meta information (if present) in an FLV
/// </summary>
/// <param name="uri">The path to the FLV file</returns>
public static MediaMetadataInfo GetMetadataInfo(string uri)
{
bool hasMetaData = false;
double duration = 0;
double width = 0;
double height = 0;
double videoDataRate = 0;
double audioDataRate = 0;
double frameRate = 0;
DateTime creationDate = DateTime.MinValue;
WebRequest req = HttpWebRequest.Create(uri);
WebResponse res = req.GetResponse();
Stream s = res.GetResponseStream(); //Source
MemoryStream ms = new MemoryStream((int)(res as HttpWebResponse).ContentLength); //Destination
byte[] b = new byte[BufferLength]; //Buffer
int cnt = 0;
do
{
//Read up to 1000 bytes from the response stream
cnt = s.Read(b, 0, BufferLength);
//Write the number of bytes actually read
ms.Write(b, 0, cnt);
}
while (cnt > 0);
try
{
// read where "onMetaData"
byte[] bytes = new byte[10];
ms.Seek(27, SeekOrigin.Begin);
int result = ms.Read(bytes, 0, 10);
// if "onMetaData" exists then proceed to read the attributes
string onMetaData = ByteArrayToString(bytes);
if (onMetaData == "onMetaData")
{
hasMetaData = true;
// 16 bytes past "onMetaData" is the data for "duration"
duration = GetNextDouble(ms, 16, 8);
// 8 bytes past "duration" is the data for "width"
width = GetNextDouble(ms, 8, 8);
// 9 bytes past "width" is the data for "height"
height = GetNextDouble(ms, 9, 8);
// 16 bytes past "height" is the data for "videoDataRate"
videoDataRate = GetNextDouble(ms, 16, 8);
// 16 bytes past "videoDataRate" is the data for "audioDataRate"
audioDataRate = GetNextDouble(ms, 16, 8);
// 12 bytes past "audioDataRate" is the data for "frameRate"
frameRate = GetNextDouble(ms, 12, 8);
// read in bytes for creationDate manually
ms.Seek(17, SeekOrigin.Current);
byte[] seekBytes = new byte[24];
result = ms.Read(seekBytes, 0, 24);
string dateString = ByteArrayToString(seekBytes);
// create .NET readable date string
// cut off Day of Week
dateString = dateString.Substring(4);
// grab 1) month and day, 2) year, 3) time
dateString = dateString.Substring(0, 6) + " " + dateString.Substring(16, 4) + " " + dateString.Substring(7, 8);
// .NET 2.0 has DateTime.TryParse
try
{
creationDate = Convert.ToDateTime(dateString);
}
catch(Exception)
{
// no error handling
}
}
}
catch (Exception)
{
// no error handling
}
finally
{
ms.Close();
ms.Dispose();
}
Uri newUri = new Uri(uri);
string filename = Path.GetFileName(newUri.AbsoluteUri);
return new MediaMetadataInfo(hasMetaData, filename, duration, width, height, videoDataRate, audioDataRate, frameRate, creationDate);
}
private static Double GetNextDouble(MemoryStream ms, int offset, int length)
{
// move the desired number of places in the array
ms.Seek(offset, SeekOrigin.Current);
// create byte array
byte[] bytes = new byte[length];
// read bytes
int result = ms.Read(bytes, 0, length);
// convert to double (all flass values are written in reverse order)
return ByteArrayToDouble(bytes, true);
}
private static String ByteArrayToString(byte[] bytes)
{
string byteString = string.Empty;
foreach (byte b in bytes)
{
byteString += Convert.ToChar(b).ToString();
}
return byteString;
}
private static Double ByteArrayToDouble(byte[] bytes, bool readInReverse)
{
if (bytes.Length != 8)
throw new Exception("bytes must be exactly 8 in Length");
if (readInReverse)
Array.Reverse(bytes);
return BitConverter.ToDouble(bytes, 0);
}
}
}
可以这样做吗?我包括来自abc News RSS feed的.flv uri用作例子:
http://video-cdn.abcnew.go.com/090713 _ann
_ skinnydip.flv
任何帮助将不胜感激。
答案 0 :(得分:0)
您的代码看起来很有前途。困难的部分是为每种预期的文件格式编写解析器。文件也很可能没有你需要的元数据。
您也可以考虑使用request range告诉服务器您只需要文件的一部分。只要服务器支持它,它就应该加快速度。
答案 1 :(得分:0)
using System;
using System.IO;
using System.Net;
namespace myRSSVideoReader
{
public static class FlvMetadataReader
{
static string onMetaData = "";
static string bytesToFile = "";
/// <summary>
/// Reads the meta information (if present) in an FLV
/// </summary>
/// <param name="uri">The uri to the FLV file</returns>
public static MediaMetadataInfo GetMetadataInfo(string uri)
{
//needed for the file name only
Uri newUri = new Uri(uri);
Stream strm = null;
StreamReader MyReader = null;
bool hasMetaData = false;
double duration = 0;
double width = 0;
double height = 0;
double videoDataRate = 0;
double audioDataRate = 0;
double frameRate = 0;
DateTime creationDate = DateTime.MinValue;
int range = 800;
try
{
//byte[] result;
byte[] buffer = new byte[range];
strm = GetURLStream(uri, range);
if (strm != null)
{
using (MemoryStream fileStream = new MemoryStream())
{
int count = 0;
do
{
count = strm.Read(buffer, 0, buffer.Length);
fileStream.Write(buffer, 0, count);
}
while (count != 0);
// read where "onMetaData" in flash, this indicates we've got maetadata
byte[] bytes = new byte[1000];
fileStream.Seek(27, SeekOrigin.Begin);
int result = fileStream.Read(bytes, 0, 1000);
// if "onMetaData" exists then proceed to read the attributes
bytesToFile = ByteArrayToString(bytes);
onMetaData = bytesToFile.Substring(0, 10);
if (onMetaData == "onMetaData")
{
hasMetaData = true;
duration = GetNextDouble(bytes, bytesToFile.IndexOf("duration") + 9, 8);
duration = Math.Round(duration);
width = GetNextDouble(bytes, bytesToFile.IndexOf("width") + 6, 8);
height = GetNextDouble(bytes, bytesToFile.IndexOf("height") + 7, 8);
videoDataRate = GetNextDouble(bytes, bytesToFile.IndexOf("videodatarate") + 14, 8);
audioDataRate = GetNextDouble(bytes, bytesToFile.IndexOf("audiodatarate") + 14, 8);
frameRate = GetNextDouble(bytes, bytesToFile.IndexOf("framerate") + 10, 8);
}
fileStream.Close();
fileStream.Dispose();
}
}
}
catch {}
finally
{
// do some cleanup
if (MyReader != null)
MyReader.Close();
if (strm != null)
strm.Close();
}
string filename = Path.GetFileName(newUri.AbsoluteUri);
return new MediaMetadataInfo(hasMetaData, filename, duration, width, height, videoDataRate, audioDataRate, frameRate, creationDate);
}
/* ------------------------------------------------------------- */
private static Stream GetURLStream(string strURL, int range)
{
WebRequest req;
WebResponse res = null;
Stream respStream;
try
{
req = WebRequest.Create(strURL);
((HttpWebRequest)req).UserAgent = "myRSSVideoReader/1.0.0.12 (compatible; http://www.myrssvideoreader.com; Your RSS feeds need duration value;)";
((HttpWebRequest)req).AddRange(0, range * 2);
res = req.GetResponse();
respStream = res.GetResponseStream();
return respStream;
}
catch (Exception)
{
res.Close();
return null;
}
}
/* ------------------------------------------------------------- */
private static Double GetNextDouble(Byte[] b, int offset, int length)
{
MemoryStream ms = new MemoryStream(b);
// move the desired number of places in the array
ms.Seek(offset, SeekOrigin.Current);
// create byte array
byte[] bytes = new byte[length];
// read bytes
int result = ms.Read(bytes, 0, length);
// convert to double (all flass values are written in reverse order)
return ByteArrayToDouble(bytes, true);
}
/* ------------------------------------------------------------- */
private static String ByteArrayToString(byte[] bytes)
{
string byteString = string.Empty;
foreach (byte b in bytes)
{
byteString += Convert.ToChar(b).ToString();
}
return byteString;
}
/* ------------------------------------------------------------- */
private static Double ByteArrayToDouble(byte[] bytes, bool readInReverse)
{
if (bytes.Length != 8)
throw new Exception("bytes must be exactly 8 in Length");
if (readInReverse)
Array.Reverse(bytes);
return BitConverter.ToDouble(bytes, 0);
}
}
}