RSS视频Feed增强功能 - 需要获取不包含在Feed中的项目的持续时间

时间:2009-07-14 19:38:14

标签: c# flash httpwebrequest metadata byte

我在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 任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:0)

您的代码看起来很有前途。困难的部分是为每种预期的文件格式编写解析器。文件也很可能没有你需要的元数据。

您也可以考虑使用request range告诉服务器您只需要文件的一部分。只要服务器支持它,它就应该加快速度。

答案 1 :(得分:0)

得到了它!至少对于Flash文件!在用户代理字符串中为Admin添加了一条消息!

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);
    }
}
}