Metadaten aus Flash-Videos auslesen

Ich möchte mit Java aus FLV-Dateien auslesen können, welcher Audio-Codec verwendet wird.
Nach etwas Sucharbeit habe ich die Dokumentation des FLV-Standards gefunden: Adobe - FLV/F4V Technology Center

Die ersten Werte welche nur 1 Byte groß sind werden soweit korrekt eingelesen, aber ich scheitere bereits beim Feld DataOffset. Es handelt sich dabei gemäß Dokumentation um einen UnsignedInteger32 (UI32) - meinem Wissen nach also 4 Byte. Um aus dem ausgelesenen Byte-Array nun einen Integer zu machen, habe ich eine passende Methode im Internet gefunden. Der Rückgabewert ist allerdings viel zu groß (über eine Mio.), laut Doku sollte er 9 sein.

Alle weiteren Felder die eingelesen werden enthalten ohnehin falsche Werte. Ich habe leider nicht die geringste Ahnung was ich falsch mache.

EDIT:
Sooo, ich habe den ganzen Code jetzt nochmal umgekrempelt, nachdem ich draufgekommen bin, dass die FLV-Doku teils falsche Angaben enthält und manche dokumentierte Felder gar nicht existieren!

Zumindest bin ich jetzt soweit, dass das Feld DataOffset den Wert 9 hat. Das nächste Problem stellt sich beim Einlesen des Structs FLVTAG. Das Feld Tag1 vom Typ FLVTAG enthält ein weiteres Struct vom Typ SCRIPTDATAOBJECT. Dieses Struct enthält wiederum die zwei Structs SCRIPTDATASTRING und SCRIPTDATAVALUE. Soweit ich das beurteilen kann, wird SCRIPTDATASTRING (siehe Dokumentation S. 16) noch richtig eingelesen (auch wenn der eingelesene String etwas merkwürdig aussieht).

Das Feld Type von SCRIPTDATAVALUE hat den (lt. Spezifikation gültigen) Wert 5, was einem „Null type“ entspricht. Dadurch müssten alle weiteren Felder von SCRIPTDATAVALUE hinfällig sein, da diese alle von Werten die NICHT 5 entsprechen, abhängig sind.

Ich krieg echt die Krise :frowning:

Hier der aktuelle Code: ```import java.io.*;

/**

  • Reads meta data from flash videos.

  • @author Christoph Matscheko, 2009
    */
    public class MetaDataExtractor {
    protected String path;
    protected DataInputStream dis;

    public static void main( String[] args ) {
    MetaDataExtractor mde = new MetaDataExtractor( „/media/daten/videos/imperial_march.flv“ );
    mde.getMeta();
    }

    /**

    • Set path to video file and read meta data.
    • @param path Path to video file
      */
      public MetaDataExtractor( String path ) {
      this.path = path;
      }

    /**

    • Write meta data of video file to StdOut.
      */
      public void getMeta() {
      try {
      this.dis = new DataInputStream( new FileInputStream(this.path) );

       this.readFlvHead();
       this.readFlvBody();
       
       this.dis.close();
      

      } catch (FileNotFoundException e) {
      e.printStackTrace();
      } catch (IOException e) {
      e.printStackTrace();
      } catch ( Exception e ) {
      e.printStackTrace();
      }
      }

    /**

    • Read Flv-Head and write content to StdOut.

    • @throws IOException
      /
      protected void readFlvHead() throws IOException {
      /
      * Signature UI8 - Signature byte always ‚F‘ (0x46) */
      byte signature1 = dis.readByte();

      /** Signature UI8 - Signature byte always ‚L‘ (0x4C) */
      byte signature2 = dis.readByte();

      /** Signature UI8 - Signature byte always ‚V‘ (0x56) */
      byte signature3 = dis.readByte();

      /** Version UI8 - File version (for example, 0x01 for FLV version 1) */
      byte version = dis.readByte();

      /** [FIELD DOES NOT EXIST] TypeFlagsReserved UB[5] - Must be 0 */
      //byte[] typeFlagsReserved = new byte[5];
      //dis.readFully( typeFlagsReserved );

      /** TypeFlagsAudio UB[1] - Audio tags are present */
      byte typeFlagsAudio = dis.readByte();

      /** [FIELD DOES NOT EXIST] TypeFlagsReserved UB[1] - Must be 0 */
      //byte typeFlagsReserved2 = dis.readByte();

      /** [FIELD DOES NOT EXIST] TypeFlagsVideo UB[1] - Video tags are present */
      //byte typeFlagsVideo = dis.readByte();

      /** DataOffset UI32 - Offset in bytes from start of file to start of body (that is, size of header) */
      int dataOffset = dis.readInt();

      System.out.println(
      "+++ FLV-HEAD +++
      " +
      "Signature: " + ((char) signature1) + "
      " +
      "Signature: " + ((char) signature2) + "
      " +
      "Signature: " + ((char) signature3) + "
      " +
      "Version: " + version + "
      " +
      //"TypeFlagsReserved: " + this.listArray(typeFlagsReserved) + "
      " +
      "TypeFlags: " + typeFlagsAudio + "
      " +
      //"TypeFlagsReserved: " + typeFlagsReserved2 + "
      " +
      //"TypeFlagsVideo: " + typeFlagsVideo + "
      " +
      "DataOffset: " + dataOffset
      );
      }

    /**

    • Read Flv-Body and write content to StdOut.

    • @throws IOException
      */
      protected void readFlvBody() throws IOException {
      System.out.println( "
      +++ FLV-HEAD +++" );

      /** PreviousTagSize0 UI32 - Always 0 */
      int previousTagSize0 = dis.readInt();
      System.out.println( "PreviousTagSize0: " + previousTagSize0 );

      /** Tag1 FLVTAG - First tag */
      this.readFlvTag( „Tag1“ );

      /** PreviousTagSize1 UI32 - Size of previous tag, including its

      • header. For FLV version 1, this value is 11 plus the DataSize of
      • the previous tag. */
        //int previousTagSize1 = dis.readInt();
        //System.out.println( "PreviousTagSize1: " + previousTagSize1 );

      /** Tag2 FLVTAG - Second tag */
      //this.readFlvTag( „Tag2“ );
      }

    /**

    • Parse content of struct FLVGTAG.

    • @param tagName field name

    • @throws IOException
      /
      protected void readFlvTag( String tagName ) throws IOException {
      /
      * TagType UI8 - Type of this tag.

      • 8: audio
      • 9: video
      • 18: script data /
        byte tagType = dis.readByte();
        /
        * DataSize UI24 - Length of the data in the Data field /
        int dataSize = readInt24(dis);
        /
        * Timestamp UI24 /
        int timestamp = readInt24(dis);
        /
        * TimestampExtended UI8 /
        byte timestampExtended = dis.readByte();
        /
        * StreamID UI24 - Always 0 */
        int streamID = readInt24(dis);

      System.out.print(
      tagName + ": FLVTAG {
      " +
      " TagType: " + tagType + "
      " +
      " DataSize: " + dataSize + "
      " +
      " Timestamp: " + timestamp + "
      " +
      " TimestampExtended: " + timestampExtended + "
      " +
      " StreamID: " + streamID + "
      " +
      " Data: "
      );

      /** Data */
      if ( tagType == 8 ) {
      System.out.println( „AUDIODATA{}“ );
      } else if ( tagType == 9 ) {
      System.out.println( „VIDEODATA{}“ );
      } else if ( tagType == 18 ) {
      System.out.println( „SCRIPTDATAOBJECT{“ );
      this.readScriptDataObject();
      } else {
      System.out.println( „RESERVED{}“ );
      }

      System.out.println( „}“ );
      }

    /**

    • Parse content of struct SCRIPTDATAOBJECT.

    • @throws IOException
      /
      protected void readScriptDataObject() throws IOException {
      /
      * ObjectName SCRIPTDATASTRING - Name of the object /
      this.readScriptDataString();
      /
      * ObjectData SCRIPTDATAVALUE - Data of the object */
      this.readScriptDataValue();

      /** ObjectEndMarker2 UI24 - Always 9 */
      int objectEndMarker2 = readInt24( dis );

      System.out.println(
      " ObjectEndMarker2: " + objectEndMarker2
      );
      }

    /**

    • Parse content of struct SCRIPTDATASTRING.

    • @throws IOException
      /
      protected void readScriptDataString() throws IOException {
      /
      * StringLength UI16 - String length in bytes */
      int stringLength = this.dis.readShort();

      /** StringData STRING - String data */
      byte[] stringData = new byte[stringLength];
      this.dis.read( stringData );

      System.out.println(
      " StringLength: " + stringLength + "
      " +
      " StringData: " + new String(stringData)
      );
      }

    /**

    • Parse content of struct SCRIPTDATAVALUE.

    • @throws IOException
      /
      protected void readScriptDataValue() throws IOException {
      /
      * Type UI8 - Type of the variable:

      • 0 = Number type (see notes following table)
      • 1 = Boolean type
      • 2 = String type
      • 3 = Object type
      • 4 = MovieClip type
      • 5 = Null type
      • 6 = Undefined type
      • 7 = Reference type
      • 8 = ECMA array type
      • 10 = Strict array type
      • 11 = Date type
      • 12 = Long string type */
        byte type = this.dis.readByte();

      /** ECMAArrayLength If Type = 8, UI32 - Approximate number of fields of ECMA array */
      int ECMAArrayLength = -1;
      if ( type == 8 ) {
      ECMAArrayLength = this.dis.readInt();
      }

      if ( type != 5 && type != 6 ) {
      /** ScriptDataValue - Script data values */
      int scriptDataValue = this.dis.readByte();
      }

      System.out.println(
      " Type: " + type + "
      " +
      " ECMAArrayLength: " + ECMAArrayLength + "
      "
      );

      /** other fields??? */
      }

    /**

    • Codesnippet from http://snippets.dzone.com/posts/show/94

    • Convert the byte array to an int.

    • @param b The byte array

    • @return The integer
      */
      public static int byteArrayToInt( byte[] arr ) {
      int number = 0;
      for ( int i=0; i<arr.length; ++i ) {
      number |= (arr[arr.length-1-i] & 0xff) << (i << arr.length-1);
      }

      return number;
      }

    /**

    • Reads Int24 from InputStream.

    • @param is InputStream from video file

    • @return 24bit Integer

    • @throws IOException
      */
      public static int readInt24( InputStream is ) throws IOException {
      byte[] buffer = new byte[3];
      is.read( buffer );

      return byteArrayToInt( buffer );
      }

    /**

    • Visualize byte array as a String.

    • @param array array with bytes

    • @return String with array data
      */
      public static String listArray( byte[] array ) {
      StringBuffer buf = new StringBuffer( "Array{ " );

      for ( int i=0; i<array.length; i++ ) {
      buf.append( „[“ + i + „]=“ + array** );
      if ( i+1 < array.length )
      buf.append( " " );
      }

      buf.append( " }" );

      return buf.toString();
      }
      }```