2009-07-14 5 views
0

Ich habe ein funktionierendes DNLA-Gerät (Xbox360, PSP ...) RSS Video-Feed-Reader in C#. Es analysiert .OPML-Dateien, um den Feed-URI zu erhalten.RSS Video Feed Enhancement - müssen Dauer auf Elemente, die es nicht in Feed enthalten

Manchmal hat ein RSS-Feed-Element keinen Wert für die Dauer, daher programmiere ich einen Standarddauerwert hart, wenn dies nicht der Fall ist.

Ich möchte die tatsächliche Dauer der Videodatei erhalten.

Meine Idee ist es, httpWebRequest zu verwenden, um einen Byte-Stream zu erhalten und die Informationen in den Dateien binäre MetaData, falls verfügbar, zu suchen. Ich denke, es könnte getan werden, kann aber keine ähnlichen Beispiele finden.

Der Prozess muss schnell sein und muss nicht die gesamte Videodatei abrufen, da der Wert für die Dauer nur zum Erstellen des Menüs benötigt wird. Die Dateien, die ich auf diese Weise verarbeiten soll, sind .flv, .m4v und .mp4. Das folgende Beispiel zeigt eine .flv-Datei:

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

Kann dies getan werden? Ich bin ein .flv uri von abc News RSS-Feed, um als ein Beispiel zu verwenden: http://video-cdn.abcnew.go.com/090713_ann _skinnydip.flv Jede Hilfe wäre willkommen.

Antwort

0

hab es geschafft! Für Flash-Dateien mindestens! hat auch eine kleine Nachricht für den Admin in die User-Agent-Zeichenkette eingefügt!

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

Ihr Code sieht vielversprechend aus. Der schwierige Teil wird Parser für jedes der erwarteten Dateiformate schreiben. Es besteht auch die Möglichkeit, dass die Dateien nicht die benötigten Metadaten enthalten.

Sie könnten auch in die Verwendung der request range suchen, um dem Server mitzuteilen, dass Sie nur einen Teil der Datei benötigen. Das sollte es beschleunigen, solange der Server es unterstützt.

+0

Ja, ich finde das heraus. Viele Mediendateien haben keine Metadaten. Also bin ich an der gleichen Stelle, die ich war, bevor ich es überprüft habe, aber für die Dateien, die ... –

+0

Der Trick für mp4, um Dauer zu erhalten, benötigen Sie den letzten Zeitstempel (der erste ist 0:00:00) aus den Bytes. Es gibt einige Byteverschiebungen, aber es funktioniert und ist schnell. Das ist nur für die Dauer. Sagen Sie, wenn Sie wollen, Höhe, Breite, das ist in der Kopfzeile. Auch die httpWebRequest mit einem Minus AddRange (-1024), Sie erhalten Daten aus der Länge der Datei rückwärts 1024 als Ausgangspunkt: bis zum Ende der Datei. Cool. –