RSS 视频源增强 - 需要获取不包含在源中的项目的持续时间
我有一个可用的 DNLA 设备(Xbox360、PSP...)C# 中的 RSS 视频源阅读器。 它解析 .opml 文件以获取 feed uri。
有时 RSS 提要项目没有持续时间值,因此当没有持续时间值时,我会硬编码一个默认持续时间值。
我想获取视频文件的真实时长。
我的想法是使用 httpWebRequest 获取字节流并查找文件二进制元数据中的信息(如果可用)。 我认为可以做到,但找不到类似的例子。
该过程必须很快并且不需要获取整个视频文件,因为仅需要持续时间值来构建菜单。 我希望以这种方式处理的文件是 .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 任何帮助,将不胜感激。
I have a working DNLA device (Xbox360, PSP...) RSS Video feed reader in C#. It parses .opml files to get the feed uri.
Sometimes an RSS feed item will not have a duration value, so I am hard coding a default duration value when it doesn't.
I want to get the true duration of the video file.
My idea is to use httpWebRequest to get a byte stream and seek out the information in the files binary metaData, if available. I'm thinking it could be done, but can find no similar examples.
The process must be fast and does not need to get the whole video file, because the duration value is only needed to build the menu. The files I am expecting to process this way are .flv, .m4v, and .mp4's. the example shown below is for a .flv file:
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);
}
}
}
Can this be done? I'm including an .flv uri from abc News RSS feed to use as an example:
http://video-cdn.abcnew.go.com/090713_ann
_skinnydip.flv
Any help would be appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你的代码看起来很有前途。 困难的部分是为每种预期的文件格式编写解析器。 文件也很可能不包含您需要的元数据。
您还可以考虑使用请求范围告诉服务器您只需要文件的一部分。 只要服务器支持,这应该会加快速度。
Your code looks promising. The hard part will be writing parsers for each of the expected file format. There's also a good chance that the files won't have the metadata you need.
You might also look into using the request range to tell the server that you only need a part of the file. That should speed it up as long as the server supports it.
知道了! 至少对于 Flash 文件来说! 在用户代理字符串中还包含了一条给管理员的小消息!
got it! For Flash files at least! included a little message for the Admin in the user agent string too!