Улучшение RSS-канала - необходимо получить продолжительность для элементов, которые не включают его в канал - PullRequest
0 голосов
/ 14 июля 2009

У меня есть работающее устройство DNLA (Xbox360, PSP ...) RSS-ридер для чтения видео в C #. Он анализирует файлы .opml, чтобы получить 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);
        }
    }
}

Можно ли это сделать? Я включаю .flv uri из новостной ленты abc, чтобы использовать в качестве примера: http://video -cdn.abcnew.go.com / 090713 _ann _skinnydip.flv Любая помощь будет оценена.

Ответы [ 2 ]

0 голосов
/ 16 июля 2009

понял! По крайней мере, для файлов Flash! включил небольшое сообщение для Admin в строку user agent!

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);
    }
}
}
0 голосов
/ 15 июля 2009

Ваш код выглядит многообещающе. Трудной частью будет написание синтаксических анализаторов для каждого ожидаемого формата файла. Также есть хороший шанс, что в файлах не будет метаданных, которые вам нужны.

Вы также можете использовать диапазон запросов , чтобы сообщить серверу, что вам нужна только часть файла. Это должно ускорить его, пока сервер его поддерживает.

...