[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ID3/ -> module.audio-video.matroska.php (source)

   1  <?php
   2  
   3  /////////////////////////////////////////////////////////////////
   4  /// getID3() by James Heinrich <info@getid3.org>               //
   5  //  available at https://github.com/JamesHeinrich/getID3       //
   6  //            or https://www.getid3.org                        //
   7  //            or http://getid3.sourceforge.net                 //
   8  //  see readme.txt for more details                            //
   9  /////////////////////////////////////////////////////////////////
  10  //                                                             //
  11  // module.audio-video.matriska.php                             //
  12  // module for analyzing Matroska containers                    //
  13  // dependencies: NONE                                          //
  14  //                                                            ///
  15  /////////////////////////////////////////////////////////////////
  16  
  17  if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
  18      exit;
  19  }
  20  
  21  define('EBML_ID_CHAPTERS',                  0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation.
  22  define('EBML_ID_SEEKHEAD',                  0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
  23  define('EBML_ID_TAGS',                      0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found <http://www.matroska.org/technical/specs/tagging/index.html>.
  24  define('EBML_ID_INFO',                      0x0549A966); // [15][49][A9][66] -- Contains miscellaneous general information and statistics on the file.
  25  define('EBML_ID_TRACKS',                    0x0654AE6B); // [16][54][AE][6B] -- A top-level block of information with many tracks described.
  26  define('EBML_ID_SEGMENT',                   0x08538067); // [18][53][80][67] -- This element contains all other top-level (level 1) elements. Typically a Matroska file is composed of 1 segment.
  27  define('EBML_ID_ATTACHMENTS',               0x0941A469); // [19][41][A4][69] -- Contain attached files.
  28  define('EBML_ID_EBML',                      0x0A45DFA3); // [1A][45][DF][A3] -- Set the EBML characteristics of the data to follow. Each EBML document has to start with this.
  29  define('EBML_ID_CUES',                      0x0C53BB6B); // [1C][53][BB][6B] -- A top-level element to speed seeking access. All entries are local to the segment.
  30  define('EBML_ID_CLUSTER',                   0x0F43B675); // [1F][43][B6][75] -- The lower level element containing the (monolithic) Block structure.
  31  define('EBML_ID_LANGUAGE',                    0x02B59C); //     [22][B5][9C] -- Specifies the language of the track in the Matroska languages form.
  32  define('EBML_ID_TRACKTIMECODESCALE',          0x03314F); //     [23][31][4F] -- The scale to apply on this track to work at normal speed in relation with other tracks (mostly used to adjust video speed when the audio length differs).
  33  define('EBML_ID_DEFAULTDURATION',             0x03E383); //     [23][E3][83] -- Number of nanoseconds (i.e. not scaled) per frame.
  34  define('EBML_ID_CODECNAME',                   0x058688); //     [25][86][88] -- A human-readable string specifying the codec.
  35  define('EBML_ID_CODECDOWNLOADURL',            0x06B240); //     [26][B2][40] -- A URL to download about the codec used.
  36  define('EBML_ID_TIMECODESCALE',               0x0AD7B1); //     [2A][D7][B1] -- Timecode scale in nanoseconds (1.000.000 means all timecodes in the segment are expressed in milliseconds).
  37  define('EBML_ID_COLOURSPACE',                 0x0EB524); //     [2E][B5][24] -- Same value as in AVI (32 bits).
  38  define('EBML_ID_GAMMAVALUE',                  0x0FB523); //     [2F][B5][23] -- Gamma Value.
  39  define('EBML_ID_CODECSETTINGS',               0x1A9697); //     [3A][96][97] -- A string describing the encoding setting used.
  40  define('EBML_ID_CODECINFOURL',                0x1B4040); //     [3B][40][40] -- A URL to find information about the codec used.
  41  define('EBML_ID_PREVFILENAME',                0x1C83AB); //     [3C][83][AB] -- An escaped filename corresponding to the previous segment.
  42  define('EBML_ID_PREVUID',                     0x1CB923); //     [3C][B9][23] -- A unique ID to identify the previous chained segment (128 bits).
  43  define('EBML_ID_NEXTFILENAME',                0x1E83BB); //     [3E][83][BB] -- An escaped filename corresponding to the next segment.
  44  define('EBML_ID_NEXTUID',                     0x1EB923); //     [3E][B9][23] -- A unique ID to identify the next chained segment (128 bits).
  45  define('EBML_ID_CONTENTCOMPALGO',               0x0254); //         [42][54] -- The compression algorithm used. Algorithms that have been specified so far are:
  46  define('EBML_ID_CONTENTCOMPSETTINGS',           0x0255); //         [42][55] -- Settings that might be needed by the decompressor. For Header Stripping (ContentCompAlgo=3), the bytes that were removed from the beggining of each frames of the track.
  47  define('EBML_ID_DOCTYPE',                       0x0282); //         [42][82] -- A string that describes the type of document that follows this EBML header ('matroska' in our case).
  48  define('EBML_ID_DOCTYPEREADVERSION',            0x0285); //         [42][85] -- The minimum DocType version an interpreter has to support to read this file.
  49  define('EBML_ID_EBMLVERSION',                   0x0286); //         [42][86] -- The version of EBML parser used to create the file.
  50  define('EBML_ID_DOCTYPEVERSION',                0x0287); //         [42][87] -- The version of DocType interpreter used to create the file.
  51  define('EBML_ID_EBMLMAXIDLENGTH',               0x02F2); //         [42][F2] -- The maximum length of the IDs you'll find in this file (4 or less in Matroska).
  52  define('EBML_ID_EBMLMAXSIZELENGTH',             0x02F3); //         [42][F3] -- The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid.
  53  define('EBML_ID_EBMLREADVERSION',               0x02F7); //         [42][F7] -- The minimum EBML version a parser has to support to read this file.
  54  define('EBML_ID_CHAPLANGUAGE',                  0x037C); //         [43][7C] -- The languages corresponding to the string, in the bibliographic ISO-639-2 form.
  55  define('EBML_ID_CHAPCOUNTRY',                   0x037E); //         [43][7E] -- The countries corresponding to the string, same 2 octets as in Internet domains.
  56  define('EBML_ID_SEGMENTFAMILY',                 0x0444); //         [44][44] -- A randomly generated unique ID that all segments related to each other must use (128 bits).
  57  define('EBML_ID_DATEUTC',                       0x0461); //         [44][61] -- Date of the origin of timecode (value 0), i.e. production date.
  58  define('EBML_ID_TAGLANGUAGE',                   0x047A); //         [44][7A] -- Specifies the language of the tag specified, in the Matroska languages form.
  59  define('EBML_ID_TAGDEFAULT',                    0x0484); //         [44][84] -- Indication to know if this is the default/original language to use for the given tag.
  60  define('EBML_ID_TAGBINARY',                     0x0485); //         [44][85] -- The values of the Tag if it is binary. Note that this cannot be used in the same SimpleTag as TagString.
  61  define('EBML_ID_TAGSTRING',                     0x0487); //         [44][87] -- The value of the Tag.
  62  define('EBML_ID_DURATION',                      0x0489); //         [44][89] -- Duration of the segment (based on TimecodeScale).
  63  define('EBML_ID_CHAPPROCESSPRIVATE',            0x050D); //         [45][0D] -- Some optional data attached to the ChapProcessCodecID information. For ChapProcessCodecID = 1, it is the "DVD level" equivalent.
  64  define('EBML_ID_CHAPTERFLAGENABLED',            0x0598); //         [45][98] -- Specify wether the chapter is enabled. It can be enabled/disabled by a Control Track. When disabled, the movie should skip all the content between the TimeStart and TimeEnd of this chapter.
  65  define('EBML_ID_TAGNAME',                       0x05A3); //         [45][A3] -- The name of the Tag that is going to be stored.
  66  define('EBML_ID_EDITIONENTRY',                  0x05B9); //         [45][B9] -- Contains all information about a segment edition.
  67  define('EBML_ID_EDITIONUID',                    0x05BC); //         [45][BC] -- A unique ID to identify the edition. It's useful for tagging an edition.
  68  define('EBML_ID_EDITIONFLAGHIDDEN',             0x05BD); //         [45][BD] -- If an edition is hidden (1), it should not be available to the user interface (but still to Control Tracks).
  69  define('EBML_ID_EDITIONFLAGDEFAULT',            0x05DB); //         [45][DB] -- If a flag is set (1) the edition should be used as the default one.
  70  define('EBML_ID_EDITIONFLAGORDERED',            0x05DD); //         [45][DD] -- Specify if the chapters can be defined multiple times and the order to play them is enforced.
  71  define('EBML_ID_FILEDATA',                      0x065C); //         [46][5C] -- The data of the file.
  72  define('EBML_ID_FILEMIMETYPE',                  0x0660); //         [46][60] -- MIME type of the file.
  73  define('EBML_ID_FILENAME',                      0x066E); //         [46][6E] -- Filename of the attached file.
  74  define('EBML_ID_FILEREFERRAL',                  0x0675); //         [46][75] -- A binary value that a track/codec can refer to when the attachment is needed.
  75  define('EBML_ID_FILEDESCRIPTION',               0x067E); //         [46][7E] -- A human-friendly name for the attached file.
  76  define('EBML_ID_FILEUID',                       0x06AE); //         [46][AE] -- Unique ID representing the file, as random as possible.
  77  define('EBML_ID_CONTENTENCALGO',                0x07E1); //         [47][E1] -- The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values:
  78  define('EBML_ID_CONTENTENCKEYID',               0x07E2); //         [47][E2] -- For public key algorithms this is the ID of the public key the data was encrypted with.
  79  define('EBML_ID_CONTENTSIGNATURE',              0x07E3); //         [47][E3] -- A cryptographic signature of the contents.
  80  define('EBML_ID_CONTENTSIGKEYID',               0x07E4); //         [47][E4] -- This is the ID of the private key the data was signed with.
  81  define('EBML_ID_CONTENTSIGALGO',                0x07E5); //         [47][E5] -- The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
  82  define('EBML_ID_CONTENTSIGHASHALGO',            0x07E6); //         [47][E6] -- The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
  83  define('EBML_ID_MUXINGAPP',                     0x0D80); //         [4D][80] -- Muxing application or library ("libmatroska-0.4.3").
  84  define('EBML_ID_SEEK',                          0x0DBB); //         [4D][BB] -- Contains a single seek entry to an EBML element.
  85  define('EBML_ID_CONTENTENCODINGORDER',          0x1031); //         [50][31] -- Tells when this modification was used during encoding/muxing starting with 0 and counting upwards. The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to be unique over all ContentEncodingOrder elements in the segment.
  86  define('EBML_ID_CONTENTENCODINGSCOPE',          0x1032); //         [50][32] -- A bit field that describes which elements have been modified in this way. Values (big endian) can be OR'ed. Possible values:
  87  define('EBML_ID_CONTENTENCODINGTYPE',           0x1033); //         [50][33] -- A value describing what kind of transformation has been done. Possible values:
  88  define('EBML_ID_CONTENTCOMPRESSION',            0x1034); //         [50][34] -- Settings describing the compression used. Must be present if the value of ContentEncodingType is 0 and absent otherwise. Each block must be decompressable even if no previous block is available in order not to prevent seeking.
  89  define('EBML_ID_CONTENTENCRYPTION',             0x1035); //         [50][35] -- Settings describing the encryption used. Must be present if the value of ContentEncodingType is 1 and absent otherwise.
  90  define('EBML_ID_CUEREFNUMBER',                  0x135F); //         [53][5F] -- Number of the referenced Block of Track X in the specified Cluster.
  91  define('EBML_ID_NAME',                          0x136E); //         [53][6E] -- A human-readable track name.
  92  define('EBML_ID_CUEBLOCKNUMBER',                0x1378); //         [53][78] -- Number of the Block in the specified Cluster.
  93  define('EBML_ID_TRACKOFFSET',                   0x137F); //         [53][7F] -- A value to add to the Block's Timecode. This can be used to adjust the playback offset of a track.
  94  define('EBML_ID_SEEKID',                        0x13AB); //         [53][AB] -- The binary ID corresponding to the element name.
  95  define('EBML_ID_SEEKPOSITION',                  0x13AC); //         [53][AC] -- The position of the element in the segment in octets (0 = first level 1 element).
  96  define('EBML_ID_STEREOMODE',                    0x13B8); //         [53][B8] -- Stereo-3D video mode.
  97  define('EBML_ID_OLDSTEREOMODE',                 0x13B9); //         [53][B9] -- Bogus StereoMode value used in old versions of libmatroska. DO NOT USE. (0: mono, 1: right eye, 2: left eye, 3: both eyes).
  98  define('EBML_ID_PIXELCROPBOTTOM',               0x14AA); //         [54][AA] -- The number of video pixels to remove at the bottom of the image (for HDTV content).
  99  define('EBML_ID_DISPLAYWIDTH',                  0x14B0); //         [54][B0] -- Width of the video frames to display.
 100  define('EBML_ID_DISPLAYUNIT',                   0x14B2); //         [54][B2] -- Type of the unit for DisplayWidth/Height (0: pixels, 1: centimeters, 2: inches).
 101  define('EBML_ID_ASPECTRATIOTYPE',               0x14B3); //         [54][B3] -- Specify the possible modifications to the aspect ratio (0: free resizing, 1: keep aspect ratio, 2: fixed).
 102  define('EBML_ID_DISPLAYHEIGHT',                 0x14BA); //         [54][BA] -- Height of the video frames to display.
 103  define('EBML_ID_PIXELCROPTOP',                  0x14BB); //         [54][BB] -- The number of video pixels to remove at the top of the image.
 104  define('EBML_ID_PIXELCROPLEFT',                 0x14CC); //         [54][CC] -- The number of video pixels to remove on the left of the image.
 105  define('EBML_ID_PIXELCROPRIGHT',                0x14DD); //         [54][DD] -- The number of video pixels to remove on the right of the image.
 106  define('EBML_ID_FLAGFORCED',                    0x15AA); //         [55][AA] -- Set if that track MUST be used during playback. There can be many forced track for a kind (audio, video or subs), the player should select the one which language matches the user preference or the default + forced track. Overlay MAY happen between a forced and non-forced track of the same kind.
 107  define('EBML_ID_MAXBLOCKADDITIONID',            0x15EE); //         [55][EE] -- The maximum value of BlockAddID. A value 0 means there is no BlockAdditions for this track.
 108  define('EBML_ID_WRITINGAPP',                    0x1741); //         [57][41] -- Writing application ("mkvmerge-0.3.3").
 109  define('EBML_ID_CLUSTERSILENTTRACKS',           0x1854); //         [58][54] -- The list of tracks that are not used in that part of the stream. It is useful when using overlay tracks on seeking. Then you should decide what track to use.
 110  define('EBML_ID_CLUSTERSILENTTRACKNUMBER',      0x18D7); //         [58][D7] -- One of the track number that are not used from now on in the stream. It could change later if not specified as silent in a further Cluster.
 111  define('EBML_ID_ATTACHEDFILE',                  0x21A7); //         [61][A7] -- An attached file.
 112  define('EBML_ID_CONTENTENCODING',               0x2240); //         [62][40] -- Settings for one content encoding like compression or encryption.
 113  define('EBML_ID_BITDEPTH',                      0x2264); //         [62][64] -- Bits per sample, mostly used for PCM.
 114  define('EBML_ID_CODECPRIVATE',                  0x23A2); //         [63][A2] -- Private data only known to the codec.
 115  define('EBML_ID_TARGETS',                       0x23C0); //         [63][C0] -- Contain all UIDs where the specified meta data apply. It is void to describe everything in the segment.
 116  define('EBML_ID_CHAPTERPHYSICALEQUIV',          0x23C3); //         [63][C3] -- Specify the physical equivalent of this ChapterAtom like "DVD" (60) or "SIDE" (50), see complete list of values.
 117  define('EBML_ID_TAGCHAPTERUID',                 0x23C4); //         [63][C4] -- A unique ID to identify the Chapter(s) the tags belong to. If the value is 0 at this level, the tags apply to all chapters in the Segment.
 118  define('EBML_ID_TAGTRACKUID',                   0x23C5); //         [63][C5] -- A unique ID to identify the Track(s) the tags belong to. If the value is 0 at this level, the tags apply to all tracks in the Segment.
 119  define('EBML_ID_TAGATTACHMENTUID',              0x23C6); //         [63][C6] -- A unique ID to identify the Attachment(s) the tags belong to. If the value is 0 at this level, the tags apply to all the attachments in the Segment.
 120  define('EBML_ID_TAGEDITIONUID',                 0x23C9); //         [63][C9] -- A unique ID to identify the EditionEntry(s) the tags belong to. If the value is 0 at this level, the tags apply to all editions in the Segment.
 121  define('EBML_ID_TARGETTYPE',                    0x23CA); //         [63][CA] -- An informational string that can be used to display the logical level of the target like "ALBUM", "TRACK", "MOVIE", "CHAPTER", etc (see TargetType).
 122  define('EBML_ID_TRACKTRANSLATE',                0x2624); //         [66][24] -- The track identification for the given Chapter Codec.
 123  define('EBML_ID_TRACKTRANSLATETRACKID',         0x26A5); //         [66][A5] -- The binary value used to represent this track in the chapter codec data. The format depends on the ChapProcessCodecID used.
 124  define('EBML_ID_TRACKTRANSLATECODEC',           0x26BF); //         [66][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
 125  define('EBML_ID_TRACKTRANSLATEEDITIONUID',      0x26FC); //         [66][FC] -- Specify an edition UID on which this translation applies. When not specified, it means for all editions found in the segment.
 126  define('EBML_ID_SIMPLETAG',                     0x27C8); //         [67][C8] -- Contains general information about the target.
 127  define('EBML_ID_TARGETTYPEVALUE',               0x28CA); //         [68][CA] -- A number to indicate the logical level of the target (see TargetType).
 128  define('EBML_ID_CHAPPROCESSCOMMAND',            0x2911); //         [69][11] -- Contains all the commands associated to the Atom.
 129  define('EBML_ID_CHAPPROCESSTIME',               0x2922); //         [69][22] -- Defines when the process command should be handled (0: during the whole chapter, 1: before starting playback, 2: after playback of the chapter).
 130  define('EBML_ID_CHAPTERTRANSLATE',              0x2924); //         [69][24] -- A tuple of corresponding ID used by chapter codecs to represent this segment.
 131  define('EBML_ID_CHAPPROCESSDATA',               0x2933); //         [69][33] -- Contains the command information. The data should be interpreted depending on the ChapProcessCodecID value. For ChapProcessCodecID = 1, the data correspond to the binary DVD cell pre/post commands.
 132  define('EBML_ID_CHAPPROCESS',                   0x2944); //         [69][44] -- Contains all the commands associated to the Atom.
 133  define('EBML_ID_CHAPPROCESSCODECID',            0x2955); //         [69][55] -- Contains the type of the codec used for the processing. A value of 0 means native Matroska processing (to be defined), a value of 1 means the DVD command set is used. More codec IDs can be added later.
 134  define('EBML_ID_CHAPTERTRANSLATEID',            0x29A5); //         [69][A5] -- The binary value used to represent this segment in the chapter codec data. The format depends on the ChapProcessCodecID used.
 135  define('EBML_ID_CHAPTERTRANSLATECODEC',         0x29BF); //         [69][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
 136  define('EBML_ID_CHAPTERTRANSLATEEDITIONUID',    0x29FC); //         [69][FC] -- Specify an edition UID on which this correspondance applies. When not specified, it means for all editions found in the segment.
 137  define('EBML_ID_CONTENTENCODINGS',              0x2D80); //         [6D][80] -- Settings for several content encoding mechanisms like compression or encryption.
 138  define('EBML_ID_MINCACHE',                      0x2DE7); //         [6D][E7] -- The minimum number of frames a player should be able to cache during playback. If set to 0, the reference pseudo-cache system is not used.
 139  define('EBML_ID_MAXCACHE',                      0x2DF8); //         [6D][F8] -- The maximum cache size required to store referenced frames in and the current frame. 0 means no cache is needed.
 140  define('EBML_ID_CHAPTERSEGMENTUID',             0x2E67); //         [6E][67] -- A segment to play in place of this chapter. Edition ChapterSegmentEditionUID should be used for this segment, otherwise no edition is used.
 141  define('EBML_ID_CHAPTERSEGMENTEDITIONUID',      0x2EBC); //         [6E][BC] -- The edition to play from the segment linked in ChapterSegmentUID.
 142  define('EBML_ID_TRACKOVERLAY',                  0x2FAB); //         [6F][AB] -- Specify that this track is an overlay track for the Track specified (in the u-integer). That means when this track has a gap (see SilentTracks) the overlay track should be used instead. The order of multiple TrackOverlay matters, the first one is the one that should be used. If not found it should be the second, etc.
 143  define('EBML_ID_TAG',                           0x3373); //         [73][73] -- Element containing elements specific to Tracks/Chapters.
 144  define('EBML_ID_SEGMENTFILENAME',               0x3384); //         [73][84] -- A filename corresponding to this segment.
 145  define('EBML_ID_SEGMENTUID',                    0x33A4); //         [73][A4] -- A randomly generated unique ID to identify the current segment between many others (128 bits).
 146  define('EBML_ID_CHAPTERUID',                    0x33C4); //         [73][C4] -- A unique ID to identify the Chapter.
 147  define('EBML_ID_TRACKUID',                      0x33C5); //         [73][C5] -- A unique ID to identify the Track. This should be kept the same when making a direct stream copy of the Track to another file.
 148  define('EBML_ID_ATTACHMENTLINK',                0x3446); //         [74][46] -- The UID of an attachment that is used by this codec.
 149  define('EBML_ID_CLUSTERBLOCKADDITIONS',         0x35A1); //         [75][A1] -- Contain additional blocks to complete the main one. An EBML parser that has no knowledge of the Block structure could still see and use/skip these data.
 150  define('EBML_ID_CHANNELPOSITIONS',              0x347B); //         [7D][7B] -- Table of horizontal angles for each successive channel, see appendix.
 151  define('EBML_ID_OUTPUTSAMPLINGFREQUENCY',       0x38B5); //         [78][B5] -- Real output sampling frequency in Hz (used for SBR techniques).
 152  define('EBML_ID_TITLE',                         0x3BA9); //         [7B][A9] -- General name of the segment.
 153  define('EBML_ID_CHAPTERDISPLAY',                  0x00); //             [80] -- Contains all possible strings to use for the chapter display.
 154  define('EBML_ID_TRACKTYPE',                       0x03); //             [83] -- A set of track types coded on 8 bits (1: video, 2: audio, 3: complex, 0x10: logo, 0x11: subtitle, 0x12: buttons, 0x20: control).
 155  define('EBML_ID_CHAPSTRING',                      0x05); //             [85] -- Contains the string to use as the chapter atom.
 156  define('EBML_ID_CODECID',                         0x06); //             [86] -- An ID corresponding to the codec, see the codec page for more info.
 157  define('EBML_ID_FLAGDEFAULT',                     0x08); //             [88] -- Set if that track (audio, video or subs) SHOULD be used if no language found matches the user preference.
 158  define('EBML_ID_CHAPTERTRACKNUMBER',              0x09); //             [89] -- UID of the Track to apply this chapter too. In the absense of a control track, choosing this chapter will select the listed Tracks and deselect unlisted tracks. Absense of this element indicates that the Chapter should be applied to any currently used Tracks.
 159  define('EBML_ID_CLUSTERSLICES',                   0x0E); //             [8E] -- Contains slices description.
 160  define('EBML_ID_CHAPTERTRACK',                    0x0F); //             [8F] -- List of tracks on which the chapter applies. If this element is not present, all tracks apply
 161  define('EBML_ID_CHAPTERTIMESTART',                0x11); //             [91] -- Timecode of the start of Chapter (not scaled).
 162  define('EBML_ID_CHAPTERTIMEEND',                  0x12); //             [92] -- Timecode of the end of Chapter (timecode excluded, not scaled).
 163  define('EBML_ID_CUEREFTIME',                      0x16); //             [96] -- Timecode of the referenced Block.
 164  define('EBML_ID_CUEREFCLUSTER',                   0x17); //             [97] -- Position of the Cluster containing the referenced Block.
 165  define('EBML_ID_CHAPTERFLAGHIDDEN',               0x18); //             [98] -- If a chapter is hidden (1), it should not be available to the user interface (but still to Control Tracks).
 166  define('EBML_ID_FLAGINTERLACED',                  0x1A); //             [9A] -- Set if the video is interlaced.
 167  define('EBML_ID_CLUSTERBLOCKDURATION',            0x1B); //             [9B] -- The duration of the Block (based on TimecodeScale). This element is mandatory when DefaultDuration is set for the track. When not written and with no DefaultDuration, the value is assumed to be the difference between the timecode of this Block and the timecode of the next Block in "display" order (not coding order). This element can be useful at the end of a Track (as there is not other Block available), or when there is a break in a track like for subtitle tracks.
 168  define('EBML_ID_FLAGLACING',                      0x1C); //             [9C] -- Set if the track may contain blocks using lacing.
 169  define('EBML_ID_CHANNELS',                        0x1F); //             [9F] -- Numbers of channels in the track.
 170  define('EBML_ID_CLUSTERBLOCKGROUP',               0x20); //             [A0] -- Basic container of information containing a single Block or BlockVirtual, and information specific to that Block/VirtualBlock.
 171  define('EBML_ID_CLUSTERBLOCK',                    0x21); //             [A1] -- Block containing the actual data to be rendered and a timecode relative to the Cluster Timecode.
 172  define('EBML_ID_CLUSTERBLOCKVIRTUAL',             0x22); //             [A2] -- A Block with no data. It must be stored in the stream at the place the real Block should be in display order.
 173  define('EBML_ID_CLUSTERSIMPLEBLOCK',              0x23); //             [A3] -- Similar to Block but without all the extra information, mostly used to reduced overhead when no extra feature is needed.
 174  define('EBML_ID_CLUSTERCODECSTATE',               0x24); //             [A4] -- The new codec state to use. Data interpretation is private to the codec. This information should always be referenced by a seek entry.
 175  define('EBML_ID_CLUSTERBLOCKADDITIONAL',          0x25); //             [A5] -- Interpreted by the codec as it wishes (using the BlockAddID).
 176  define('EBML_ID_CLUSTERBLOCKMORE',                0x26); //             [A6] -- Contain the BlockAdditional and some parameters.
 177  define('EBML_ID_CLUSTERPOSITION',                 0x27); //             [A7] -- Position of the Cluster in the segment (0 in live broadcast streams). It might help to resynchronise offset on damaged streams.
 178  define('EBML_ID_CODECDECODEALL',                  0x2A); //             [AA] -- The codec can decode potentially damaged data.
 179  define('EBML_ID_CLUSTERPREVSIZE',                 0x2B); //             [AB] -- Size of the previous Cluster, in octets. Can be useful for backward playing.
 180  define('EBML_ID_TRACKENTRY',                      0x2E); //             [AE] -- Describes a track with all elements.
 181  define('EBML_ID_CLUSTERENCRYPTEDBLOCK',           0x2F); //             [AF] -- Similar to SimpleBlock but the data inside the Block are Transformed (encrypt and/or signed).
 182  define('EBML_ID_PIXELWIDTH',                      0x30); //             [B0] -- Width of the encoded video frames in pixels.
 183  define('EBML_ID_CUETIME',                         0x33); //             [B3] -- Absolute timecode according to the segment time base.
 184  define('EBML_ID_SAMPLINGFREQUENCY',               0x35); //             [B5] -- Sampling frequency in Hz.
 185  define('EBML_ID_CHAPTERATOM',                     0x36); //             [B6] -- Contains the atom information to use as the chapter atom (apply to all tracks).
 186  define('EBML_ID_CUETRACKPOSITIONS',               0x37); //             [B7] -- Contain positions for different tracks corresponding to the timecode.
 187  define('EBML_ID_FLAGENABLED',                     0x39); //             [B9] -- Set if the track is used.
 188  define('EBML_ID_PIXELHEIGHT',                     0x3A); //             [BA] -- Height of the encoded video frames in pixels.
 189  define('EBML_ID_CUEPOINT',                        0x3B); //             [BB] -- Contains all information relative to a seek point in the segment.
 190  define('EBML_ID_CRC32',                           0x3F); //             [BF] -- The CRC is computed on all the data of the Master element it's in, regardless of its position. It's recommended to put the CRC value at the beggining of the Master element for easier reading. All level 1 elements should include a CRC-32.
 191  define('EBML_ID_CLUSTERBLOCKADDITIONID',          0x4B); //             [CB] -- The ID of the BlockAdditional element (0 is the main Block).
 192  define('EBML_ID_CLUSTERLACENUMBER',               0x4C); //             [CC] -- The reverse number of the frame in the lace (0 is the last frame, 1 is the next to last, etc). While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback.
 193  define('EBML_ID_CLUSTERFRAMENUMBER',              0x4D); //             [CD] -- The number of the frame to generate from this lace with this delay (allow you to generate many frames from the same Block/Frame).
 194  define('EBML_ID_CLUSTERDELAY',                    0x4E); //             [CE] -- The (scaled) delay to apply to the element.
 195  define('EBML_ID_CLUSTERDURATION',                 0x4F); //             [CF] -- The (scaled) duration to apply to the element.
 196  define('EBML_ID_TRACKNUMBER',                     0x57); //             [D7] -- The track number as used in the Block Header (using more than 127 tracks is not encouraged, though the design allows an unlimited number).
 197  define('EBML_ID_CUEREFERENCE',                    0x5B); //             [DB] -- The Clusters containing the required referenced Blocks.
 198  define('EBML_ID_VIDEO',                           0x60); //             [E0] -- Video settings.
 199  define('EBML_ID_AUDIO',                           0x61); //             [E1] -- Audio settings.
 200  define('EBML_ID_CLUSTERTIMESLICE',                0x68); //             [E8] -- Contains extra time information about the data contained in the Block. While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback.
 201  define('EBML_ID_CUECODECSTATE',                   0x6A); //             [EA] -- The position of the Codec State corresponding to this Cue element. 0 means that the data is taken from the initial Track Entry.
 202  define('EBML_ID_CUEREFCODECSTATE',                0x6B); //             [EB] -- The position of the Codec State corresponding to this referenced element. 0 means that the data is taken from the initial Track Entry.
 203  define('EBML_ID_VOID',                            0x6C); //             [EC] -- Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use.
 204  define('EBML_ID_CLUSTERTIMECODE',                 0x67); //             [E7] -- Absolute timecode of the cluster (based on TimecodeScale).
 205  define('EBML_ID_CLUSTERBLOCKADDID',               0x6E); //             [EE] -- An ID to identify the BlockAdditional level.
 206  define('EBML_ID_CUECLUSTERPOSITION',              0x71); //             [F1] -- The position of the Cluster containing the required Block.
 207  define('EBML_ID_CUETRACK',                        0x77); //             [F7] -- The track for which a position is given.
 208  define('EBML_ID_CLUSTERREFERENCEPRIORITY',        0x7A); //             [FA] -- This frame is referenced and has the specified cache priority. In cache only a frame of the same or higher priority can replace this frame. A value of 0 means the frame is not referenced.
 209  define('EBML_ID_CLUSTERREFERENCEBLOCK',           0x7B); //             [FB] -- Timecode of another frame used as a reference (ie: B or P frame). The timecode is relative to the block it's attached to.
 210  define('EBML_ID_CLUSTERREFERENCEVIRTUAL',         0x7D); //             [FD] -- Relative position of the data that should be in position of the virtual block.
 211  
 212  
 213  /**
 214   * Matroska constants
 215   */
 216  define('MATROSKA_DEFAULT_TIMECODESCALE', 1000000);
 217  
 218  /**
 219   * Matroska scan modes are internal state flags for how much of the file we are scanning
 220   */
 221  define('MATROSKA_SCAN_HEADER', 0);
 222  define('MATROSKA_SCAN_WHOLE_FILE', 1);
 223  define('MATROSKA_SCAN_FIRST_CLUSTER', 2);
 224  define('MATROSKA_SCAN_LAST_CLUSTER', 3);
 225  
 226  /**
 227  * @tutorial http://www.matroska.org/technical/specs/index.html
 228  *
 229  * @todo Rewrite EBML parser to reduce it's size and honor default element values
 230  * @todo After rewrite implement stream size calculation, that will provide additional useful info and enable AAC/FLAC audio bitrate detection
 231  */
 232  class getid3_matroska extends getid3_handler
 233  {
 234      /**
 235       * If true, do not return information about CLUSTER chunks, since there's a lot of them
 236       * and they're not usually useful [default: TRUE].
 237       *
 238       * @var bool
 239       */
 240      public $hide_clusters    = true;
 241  
 242      /**
 243       * True to parse the whole file, not only header [default: FALSE].
 244       *
 245       * @var bool
 246       */
 247      public $parse_whole_file = false;
 248  
 249      /*
 250       * Private parser settings/placeholders.
 251       */
 252      private $EBMLbuffer        = '';
 253      private $EBMLbuffer_offset = 0;
 254      private $EBMLbuffer_length = 0;
 255      private $current_offset    = 0;
 256      private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID);
 257      private $scan_mode = MATROSKA_SCAN_HEADER;
 258  
 259      /**
 260       * @return bool
 261       */
 262  	public function Analyze()
 263      {
 264          $info = &$this->getid3->info;
 265          $this->scan_mode = $this->parse_whole_file ? MATROSKA_SCAN_WHOLE_FILE : MATROSKA_SCAN_HEADER;
 266  
 267          // parse container
 268          try {
 269              $this->parseEBML($info);
 270          } catch (Exception $e) {
 271              $this->error('EBML parser: '.$e->getMessage());
 272          }
 273  
 274          $this->playtimeFromMetadata($info);
 275  
 276          // If there was no duration metadata, this might be an incomplete file or a streaming file
 277          // We need Cluster information so we can use their timecodes to estimate playtime.
 278          if (!isset($info['playtime_seconds']) && !$this->parse_whole_file) {
 279              // Scan the start and end of file for Clusters to estimate duration
 280              $this->scanStartEndForClusters($info);
 281          }
 282  
 283          if (isset($info['matroska']['cluster']) && is_array($info['matroska']['cluster'])) {
 284              if (!isset($info['playtime_seconds']) && !empty($info['matroska']['cluster'])) {
 285                  // estimate playtime using clusters if not yet known
 286                  $this->calculatePlaytimeFromClusters($info);
 287              }
 288  
 289              // Remove cluster information from output if hide_clusters is true
 290              // These could have been set during scanStartEndForClusters()
 291              if ($this->hide_clusters) {
 292                  unset($info['matroska']['cluster']);
 293              }
 294          }
 295  
 296          // extract tags
 297          if (isset($info['matroska']['tags']) && is_array($info['matroska']['tags'])) {
 298              foreach ($info['matroska']['tags'] as $key => $infoarray) {
 299                  $this->ExtractCommentsSimpleTag($infoarray);
 300              }
 301          }
 302  
 303          // process tracks
 304          if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
 305              foreach ($info['matroska']['tracks']['tracks'] as $key => $trackarray) {
 306  
 307                  $track_info = array();
 308                  $track_info['dataformat'] = self::CodecIDtoCommonName($trackarray['CodecID']);
 309                  $track_info['default'] = (isset($trackarray['FlagDefault']) ? $trackarray['FlagDefault'] : true);
 310                  if (isset($trackarray['Name'])) { $track_info['name'] = $trackarray['Name']; }
 311  
 312                  switch ($trackarray['TrackType']) {
 313  
 314                      case 1: // Video
 315                          $track_info['resolution_x'] = $trackarray['PixelWidth'];
 316                          $track_info['resolution_y'] = $trackarray['PixelHeight'];
 317                          $track_info['display_unit'] = self::displayUnit(isset($trackarray['DisplayUnit']) ? $trackarray['DisplayUnit'] : 0);
 318                          $track_info['display_x']    = (isset($trackarray['DisplayWidth']) ? $trackarray['DisplayWidth'] : $trackarray['PixelWidth']);
 319                          $track_info['display_y']    = (isset($trackarray['DisplayHeight']) ? $trackarray['DisplayHeight'] : $trackarray['PixelHeight']);
 320  
 321                          if (isset($trackarray['PixelCropBottom']))  { $track_info['crop_bottom'] = $trackarray['PixelCropBottom']; }
 322                          if (isset($trackarray['PixelCropTop']))     { $track_info['crop_top']    = $trackarray['PixelCropTop']; }
 323                          if (isset($trackarray['PixelCropLeft']))    { $track_info['crop_left']   = $trackarray['PixelCropLeft']; }
 324                          if (isset($trackarray['PixelCropRight']))   { $track_info['crop_right']  = $trackarray['PixelCropRight']; }
 325                          if (!empty($trackarray['DefaultDuration'])) { $track_info['frame_rate']  = round(1000000000 / $trackarray['DefaultDuration'], 3); }
 326                          if (isset($trackarray['CodecName']))        { $track_info['codec']       = $trackarray['CodecName']; }
 327  
 328                          switch ($trackarray['CodecID']) {
 329                              case 'V_MS/VFW/FOURCC':
 330                                  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
 331  
 332                                  $parsed = getid3_riff::ParseBITMAPINFOHEADER($trackarray['CodecPrivate']);
 333                                  $track_info['codec'] = getid3_riff::fourccLookup($parsed['fourcc']);
 334                                  $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
 335                                  break;
 336  
 337                              /*case 'V_MPEG4/ISO/AVC':
 338                                  $h264['profile']    = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 1, 1));
 339                                  $h264['level']      = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 3, 1));
 340                                  $rn                 = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 4, 1));
 341                                  $h264['NALUlength'] = ($rn & 3) + 1;
 342                                  $rn                 = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 5, 1));
 343                                  $nsps               = ($rn & 31);
 344                                  $offset             = 6;
 345                                  for ($i = 0; $i < $nsps; $i ++) {
 346                                      $length        = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2));
 347                                      $h264['SPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length);
 348                                      $offset       += 2 + $length;
 349                                  }
 350                                  $npps               = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 1));
 351                                  $offset            += 1;
 352                                  for ($i = 0; $i < $npps; $i ++) {
 353                                      $length        = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2));
 354                                      $h264['PPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length);
 355                                      $offset       += 2 + $length;
 356                                  }
 357                                  $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $h264;
 358                                  break;*/
 359                          }
 360  
 361                          if (isset($trackarray['TrackUID'])) {
 362                              $info['video']['streams'][$trackarray['TrackUID']] = $track_info;
 363                          } else {
 364                              $this->warning('Missing mandatory TrackUID for video track');
 365                          }
 366                          break;
 367  
 368                      case 2: // Audio
 369                          $track_info['sample_rate'] = (isset($trackarray['SamplingFrequency']) ? $trackarray['SamplingFrequency'] : 8000.0);
 370                          $track_info['channels']    = (isset($trackarray['Channels']) ? $trackarray['Channels'] : 1);
 371                          $track_info['language']    = (isset($trackarray['Language']) ? $trackarray['Language'] : 'eng');
 372                          if (isset($trackarray['BitDepth']))  { $track_info['bits_per_sample'] = $trackarray['BitDepth']; }
 373                          if (isset($trackarray['CodecName'])) { $track_info['codec']           = $trackarray['CodecName']; }
 374  
 375                          switch ($trackarray['CodecID']) {
 376                              case 'A_PCM/INT/LIT':
 377                              case 'A_PCM/INT/BIG':
 378                                  $track_info['bitrate'] = $track_info['sample_rate'] * $track_info['channels'] * $trackarray['BitDepth'];
 379                                  break;
 380  
 381                              case 'A_AC3':
 382                              case 'A_EAC3':
 383                              case 'A_DTS':
 384                              case 'A_MPEG/L3':
 385                              case 'A_MPEG/L2':
 386                              case 'A_FLAC':
 387                                  $module_dataformat = ($track_info['dataformat'] == 'mp2' ? 'mp3' : ($track_info['dataformat'] == 'eac3' ? 'ac3' : $track_info['dataformat']));
 388                                  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.$module_dataformat.'.php', __FILE__, true);
 389  
 390                                  if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) {
 391                                      $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set');
 392                                      break;
 393                                  }
 394  
 395                                  // create temp instance
 396                                  $getid3_temp = new getID3();
 397                                  if ($track_info['dataformat'] != 'flac') {
 398                                      $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
 399                                  }
 400                                  $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
 401                                  if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') {
 402                                      $getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length'];
 403                                  }
 404  
 405                                  // analyze
 406                                  $class = 'getid3_'.$module_dataformat;
 407                                  $header_data_key = $track_info['dataformat'][0] == 'm' ? 'mpeg' : $track_info['dataformat'];
 408                                  $getid3_audio = new $class($getid3_temp, __CLASS__);
 409                                  if ($track_info['dataformat'] == 'flac') {
 410                                      $getid3_audio->AnalyzeString($trackarray['CodecPrivate']);
 411                                  }
 412                                  else {
 413                                      $getid3_audio->Analyze();
 414                                  }
 415                                  if (!empty($getid3_temp->info[$header_data_key])) {
 416                                      $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key];
 417                                      if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
 418                                          foreach ($getid3_temp->info['audio'] as $sub_key => $value) {
 419                                              $track_info[$sub_key] = $value;
 420                                          }
 421                                      }
 422                                  }
 423                                  else {
 424                                      $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because '.$class.'::Analyze() failed at offset '.$getid3_temp->info['avdataoffset']);
 425                                  }
 426  
 427                                  // copy errors and warnings
 428                                  if (!empty($getid3_temp->info['error'])) {
 429                                      foreach ($getid3_temp->info['error'] as $newerror) {
 430                                          $this->warning($class.'() says: ['.$newerror.']');
 431                                      }
 432                                  }
 433                                  if (!empty($getid3_temp->info['warning'])) {
 434                                      foreach ($getid3_temp->info['warning'] as $newerror) {
 435                                          $this->warning($class.'() says: ['.$newerror.']');
 436                                      }
 437                                  }
 438                                  unset($getid3_temp, $getid3_audio);
 439                                  break;
 440  
 441                              case 'A_AAC':
 442                              case 'A_AAC/MPEG2/LC':
 443                              case 'A_AAC/MPEG2/LC/SBR':
 444                              case 'A_AAC/MPEG4/LC':
 445                              case 'A_AAC/MPEG4/LC/SBR':
 446                                  $this->warning($trackarray['CodecID'].' audio data contains no header, audio/video bitrates can\'t be calculated');
 447                                  break;
 448  
 449                              case 'A_VORBIS':
 450                                  if (!isset($trackarray['CodecPrivate'])) {
 451                                      $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data not set');
 452                                      break;
 453                                  }
 454                                  $vorbis_offset = strpos($trackarray['CodecPrivate'], 'vorbis', 1);
 455                                  if ($vorbis_offset === false) {
 456                                      $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data does not contain "vorbis" keyword');
 457                                      break;
 458                                  }
 459                                  $vorbis_offset -= 1;
 460  
 461                                  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
 462  
 463                                  // create temp instance
 464                                  $getid3_temp = new getID3();
 465  
 466                                  // analyze
 467                                  $getid3_ogg = new getid3_ogg($getid3_temp);
 468                                  $oggpageinfo['page_seqno'] = 0;
 469                                  $getid3_ogg->ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $oggpageinfo);
 470                                  if (!empty($getid3_temp->info['ogg'])) {
 471                                      $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg'];
 472                                      if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
 473                                          foreach ($getid3_temp->info['audio'] as $sub_key => $value) {
 474                                              $track_info[$sub_key] = $value;
 475                                          }
 476                                      }
 477                                  }
 478  
 479                                  // copy errors and warnings
 480                                  if (!empty($getid3_temp->info['error'])) {
 481                                      foreach ($getid3_temp->info['error'] as $newerror) {
 482                                          $this->warning('getid3_ogg() says: ['.$newerror.']');
 483                                      }
 484                                  }
 485                                  if (!empty($getid3_temp->info['warning'])) {
 486                                      foreach ($getid3_temp->info['warning'] as $newerror) {
 487                                          $this->warning('getid3_ogg() says: ['.$newerror.']');
 488                                      }
 489                                  }
 490  
 491                                  if (!empty($getid3_temp->info['ogg']['bitrate_nominal'])) {
 492                                      $track_info['bitrate'] = $getid3_temp->info['ogg']['bitrate_nominal'];
 493                                  }
 494                                  unset($getid3_temp, $getid3_ogg, $oggpageinfo, $vorbis_offset);
 495                                  break;
 496  
 497                              case 'A_MS/ACM':
 498                                  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
 499  
 500                                  $parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']);
 501                                  foreach ($parsed as $sub_key => $value) {
 502                                      if ($sub_key != 'raw') {
 503                                          $track_info[$sub_key] = $value;
 504                                      }
 505                                  }
 506                                  $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
 507                                  break;
 508  
 509                              default:
 510                                  $this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"');
 511                                  break;
 512                          }
 513                          if (isset($trackarray['TrackUID'])) {
 514                              $info['audio']['streams'][$trackarray['TrackUID']] = $track_info;
 515                          } else {
 516                              $this->warning('Missing mandatory TrackUID for audio track');
 517                          }
 518                          break;
 519                  }
 520              }
 521  
 522              if (!empty($info['video']['streams'])) {
 523                  $info['video'] = self::getDefaultStreamInfo($info['video']['streams']);
 524              }
 525              if (!empty($info['audio']['streams'])) {
 526                  $info['audio'] = self::getDefaultStreamInfo($info['audio']['streams']);
 527              }
 528          }
 529  
 530          // process attachments
 531          if (isset($info['matroska']['attachments']) && $this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE) {
 532              foreach ($info['matroska']['attachments'] as $i => $entry) {
 533                  if (strpos($entry['FileMimeType'], 'image/') === 0 && !empty($entry['FileData'])) {
 534                      $info['matroska']['comments']['picture'][] = array('data' => $entry['FileData'], 'image_mime' => $entry['FileMimeType'], 'filename' => $entry['FileName']);
 535                  }
 536              }
 537          }
 538  
 539          // determine mime type
 540          if (!empty($info['video']['streams'])) {
 541              $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'video/webm' : 'video/x-matroska');
 542          } elseif (!empty($info['audio']['streams'])) {
 543              $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'audio/webm' : 'audio/x-matroska');
 544          } elseif (isset($info['mime_type'])) {
 545              unset($info['mime_type']);
 546          }
 547  
 548          // use _STATISTICS_TAGS if available to set audio/video bitrates
 549          if (!empty($info['matroska']['tags'])) {
 550              $_STATISTICS_byTrackUID = array();
 551              foreach ($info['matroska']['tags'] as $key1 => $value1) {
 552                  if (!empty($value1['Targets']['TagTrackUID'][0]) && !empty($value1['SimpleTag'])) {
 553                      foreach ($value1['SimpleTag'] as $key2 => $value2) {
 554                          if (!empty($value2['TagName']) && isset($value2['TagString'])) {
 555                              $_STATISTICS_byTrackUID[$value1['Targets']['TagTrackUID'][0]][$value2['TagName']] = $value2['TagString'];
 556                          }
 557                      }
 558                  }
 559              }
 560              foreach (array('audio','video') as $avtype) {
 561                  if (!empty($info[$avtype]['streams'])) {
 562                      foreach ($info[$avtype]['streams'] as $trackUID => $trackdata) {
 563                          if (!isset($trackdata['bitrate']) && !empty($_STATISTICS_byTrackUID[$trackUID]['BPS'])) {
 564                              $info[$avtype]['streams'][$trackUID]['bitrate'] = (int) $_STATISTICS_byTrackUID[$trackUID]['BPS'];
 565                              @$info[$avtype]['bitrate'] += $info[$avtype]['streams'][$trackUID]['bitrate'];
 566                          }
 567                      }
 568                  }
 569              }
 570          }
 571  
 572          return true;
 573      }
 574  
 575      /**
 576       * @param array $info
 577       */
 578  	private function parseEBML(&$info) {
 579          // http://www.matroska.org/technical/specs/index.html#EBMLBasics
 580          $this->current_offset = $info['avdataoffset'];
 581  
 582          while ($this->getEBMLelement($top_element, $info['avdataend'])) {
 583              switch ($top_element['id']) {
 584  
 585                  case EBML_ID_EBML:
 586                      $info['matroska']['header']['offset'] = $top_element['offset'];
 587                      $info['matroska']['header']['length'] = $top_element['length'];
 588  
 589                      while ($this->getEBMLelement($element_data, $top_element['end'], true)) {
 590                          switch ($element_data['id']) {
 591  
 592                              case EBML_ID_EBMLVERSION:
 593                              case EBML_ID_EBMLREADVERSION:
 594                              case EBML_ID_EBMLMAXIDLENGTH:
 595                              case EBML_ID_EBMLMAXSIZELENGTH:
 596                              case EBML_ID_DOCTYPEVERSION:
 597                              case EBML_ID_DOCTYPEREADVERSION:
 598                                  $element_data['data'] = getid3_lib::BigEndian2Int($element_data['data']);
 599                                  break;
 600  
 601                              case EBML_ID_DOCTYPE:
 602                                  $element_data['data'] = getid3_lib::trimNullByte($element_data['data']);
 603                                  $info['matroska']['doctype'] = $element_data['data'];
 604                                  $info['fileformat'] = $element_data['data'];
 605                                  break;
 606  
 607                              default:
 608                                  $this->unhandledElement('header', __LINE__, $element_data);
 609                                  break;
 610                          }
 611  
 612                          unset($element_data['offset'], $element_data['end']);
 613                          $info['matroska']['header']['elements'][] = $element_data;
 614                      }
 615                      break;
 616  
 617                  case EBML_ID_SEGMENT:
 618                      $info['matroska']['segment'][0]['offset'] = $top_element['offset'];
 619                      $info['matroska']['segment'][0]['length'] = $top_element['length'];
 620  
 621                      while ($this->getEBMLelement($element_data, $top_element['end'])) {
 622                          if ($element_data['id'] != EBML_ID_CLUSTER || !$this->hide_clusters) { // collect clusters only if required
 623                              $info['matroska']['segments'][] = $element_data;
 624                          }
 625                          switch ($element_data['id']) {
 626  
 627                              case EBML_ID_SEEKHEAD: // Contains the position of other level 1 elements.
 628  
 629                                  while ($this->getEBMLelement($seek_entry, $element_data['end'])) {
 630                                      switch ($seek_entry['id']) {
 631  
 632                                          case EBML_ID_SEEK: // Contains a single seek entry to an EBML element
 633                                              while ($this->getEBMLelement($sub_seek_entry, $seek_entry['end'], true)) {
 634  
 635                                                  switch ($sub_seek_entry['id']) {
 636  
 637                                                      case EBML_ID_SEEKID:
 638                                                          $seek_entry['target_id']   = self::EBML2Int($sub_seek_entry['data']);
 639                                                          $seek_entry['target_name'] = self::EBMLidName($seek_entry['target_id']);
 640                                                          break;
 641  
 642                                                      case EBML_ID_SEEKPOSITION:
 643                                                          $seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($sub_seek_entry['data']);
 644                                                          break;
 645  
 646                                                      default:
 647                                                          $this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry);                                                }
 648                                                          break;
 649                                              }
 650                                              if (!isset($seek_entry['target_id'])) {
 651                                                  $this->warning('seek_entry[target_id] unexpectedly not set at '.$seek_entry['offset']);
 652                                                  break;
 653                                              }
 654                                              if (($seek_entry['target_id'] != EBML_ID_CLUSTER) || !$this->hide_clusters) { // collect clusters only if required
 655                                                  $info['matroska']['seek'][] = $seek_entry;
 656                                              }
 657                                              break;
 658  
 659                                          default:
 660                                              $this->unhandledElement('seekhead', __LINE__, $seek_entry);
 661                                              break;
 662                                      }
 663                                  }
 664                                  break;
 665  
 666                              case EBML_ID_TRACKS: // A top-level block of information with many tracks described.
 667                                  $info['matroska']['tracks'] = $element_data;
 668  
 669                                  while ($this->getEBMLelement($track_entry, $element_data['end'])) {
 670                                      switch ($track_entry['id']) {
 671  
 672                                          case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements.
 673  
 674                                              while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) {
 675                                                  switch ($subelement['id']) {
 676  
 677                                                      case EBML_ID_TRACKUID:
 678                                                          $track_entry[$subelement['id_name']] = getid3_lib::PrintHexBytes($subelement['data'], true, false);
 679                                                          break;
 680                                                      case EBML_ID_TRACKNUMBER:
 681                                                      case EBML_ID_TRACKTYPE:
 682                                                      case EBML_ID_MINCACHE:
 683                                                      case EBML_ID_MAXCACHE:
 684                                                      case EBML_ID_MAXBLOCKADDITIONID:
 685                                                      case EBML_ID_DEFAULTDURATION: // nanoseconds per frame
 686                                                          $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
 687                                                          break;
 688  
 689                                                      case EBML_ID_TRACKTIMECODESCALE:
 690                                                          $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']);
 691                                                          break;
 692  
 693                                                      case EBML_ID_CODECID:
 694                                                      case EBML_ID_LANGUAGE:
 695                                                      case EBML_ID_NAME:
 696                                                      case EBML_ID_CODECNAME:
 697                                                          $track_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
 698                                                          break;
 699  
 700                                                      case EBML_ID_CODECPRIVATE:
 701                                                          $track_entry[$subelement['id_name']] = $this->readEBMLelementData($subelement['length'], true);
 702                                                          break;
 703  
 704                                                      case EBML_ID_FLAGENABLED:
 705                                                      case EBML_ID_FLAGDEFAULT:
 706                                                      case EBML_ID_FLAGFORCED:
 707                                                      case EBML_ID_FLAGLACING:
 708                                                      case EBML_ID_CODECDECODEALL:
 709                                                          $track_entry[$subelement['id_name']] = (bool) getid3_lib::BigEndian2Int($subelement['data']);
 710                                                          break;
 711  
 712                                                      case EBML_ID_VIDEO:
 713  
 714                                                          while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
 715                                                              switch ($sub_subelement['id']) {
 716  
 717                                                                  case EBML_ID_PIXELWIDTH:
 718                                                                  case EBML_ID_PIXELHEIGHT:
 719                                                                  case EBML_ID_PIXELCROPBOTTOM:
 720                                                                  case EBML_ID_PIXELCROPTOP:
 721                                                                  case EBML_ID_PIXELCROPLEFT:
 722                                                                  case EBML_ID_PIXELCROPRIGHT:
 723                                                                  case EBML_ID_DISPLAYWIDTH:
 724                                                                  case EBML_ID_DISPLAYHEIGHT:
 725                                                                  case EBML_ID_DISPLAYUNIT:
 726                                                                  case EBML_ID_ASPECTRATIOTYPE:
 727                                                                  case EBML_ID_STEREOMODE:
 728                                                                  case EBML_ID_OLDSTEREOMODE:
 729                                                                      $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
 730                                                                      break;
 731  
 732                                                                  case EBML_ID_FLAGINTERLACED:
 733                                                                      $track_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']);
 734                                                                      break;
 735  
 736                                                                  case EBML_ID_GAMMAVALUE:
 737                                                                      $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']);
 738                                                                      break;
 739  
 740                                                                  case EBML_ID_COLOURSPACE:
 741                                                                      $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
 742                                                                      break;
 743  
 744                                                                  default:
 745                                                                      $this->unhandledElement('track.video', __LINE__, $sub_subelement);
 746                                                                      break;
 747                                                              }
 748                                                          }
 749                                                          break;
 750  
 751                                                      case EBML_ID_AUDIO:
 752  
 753                                                          while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
 754                                                              switch ($sub_subelement['id']) {
 755  
 756                                                                  case EBML_ID_CHANNELS:
 757                                                                  case EBML_ID_BITDEPTH:
 758                                                                      $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
 759                                                                      break;
 760  
 761                                                                  case EBML_ID_SAMPLINGFREQUENCY:
 762                                                                  case EBML_ID_OUTPUTSAMPLINGFREQUENCY:
 763                                                                      $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']);
 764                                                                      break;
 765  
 766                                                                  case EBML_ID_CHANNELPOSITIONS:
 767                                                                      $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
 768                                                                      break;
 769  
 770                                                                  default:
 771                                                                      $this->unhandledElement('track.audio', __LINE__, $sub_subelement);
 772                                                                      break;
 773                                                              }
 774                                                          }
 775                                                          break;
 776  
 777                                                      case EBML_ID_CONTENTENCODINGS:
 778  
 779                                                          while ($this->getEBMLelement($sub_subelement, $subelement['end'])) {
 780                                                              switch ($sub_subelement['id']) {
 781  
 782                                                                  case EBML_ID_CONTENTENCODING:
 783  
 784                                                                      while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CONTENTCOMPRESSION, EBML_ID_CONTENTENCRYPTION))) {
 785                                                                          switch ($sub_sub_subelement['id']) {
 786  
 787                                                                              case EBML_ID_CONTENTENCODINGORDER:
 788                                                                              case EBML_ID_CONTENTENCODINGSCOPE:
 789                                                                              case EBML_ID_CONTENTENCODINGTYPE:
 790                                                                                  $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
 791                                                                                  break;
 792  
 793                                                                              case EBML_ID_CONTENTCOMPRESSION:
 794  
 795                                                                                  while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
 796                                                                                      switch ($sub_sub_sub_subelement['id']) {
 797  
 798                                                                                          case EBML_ID_CONTENTCOMPALGO:
 799                                                                                              $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
 800                                                                                              break;
 801  
 802                                                                                          case EBML_ID_CONTENTCOMPSETTINGS:
 803                                                                                              $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
 804                                                                                              break;
 805  
 806                                                                                          default:
 807                                                                                              $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
 808                                                                                              break;
 809                                                                                      }
 810                                                                                  }
 811                                                                                  break;
 812  
 813                                                                              case EBML_ID_CONTENTENCRYPTION:
 814  
 815                                                                                  while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
 816                                                                                      switch ($sub_sub_sub_subelement['id']) {
 817  
 818                                                                                          case EBML_ID_CONTENTENCALGO:
 819                                                                                          case EBML_ID_CONTENTSIGALGO:
 820                                                                                          case EBML_ID_CONTENTSIGHASHALGO:
 821                                                                                              $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
 822                                                                                              break;
 823  
 824                                                                                          case EBML_ID_CONTENTENCKEYID:
 825                                                                                          case EBML_ID_CONTENTSIGNATURE:
 826                                                                                          case EBML_ID_CONTENTSIGKEYID:
 827                                                                                              $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
 828                                                                                              break;
 829  
 830                                                                                          default:
 831                                                                                              $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
 832                                                                                              break;
 833                                                                                      }
 834                                                                                  }
 835                                                                                  break;
 836  
 837                                                                              default:
 838                                                                                  $this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement);
 839                                                                                  break;
 840                                                                          }
 841                                                                      }
 842                                                                      break;
 843  
 844                                                                  default:
 845                                                                      $this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement);
 846                                                                      break;
 847                                                              }
 848                                                          }
 849                                                          break;
 850  
 851                                                      default:
 852                                                          $this->unhandledElement('track', __LINE__, $subelement);
 853                                                          break;
 854                                                  }
 855                                              }
 856  
 857                                              $info['matroska']['tracks']['tracks'][] = $track_entry;
 858                                              break;
 859  
 860                                          default:
 861                                              $this->unhandledElement('tracks', __LINE__, $track_entry);
 862                                              break;
 863                                      }
 864                                  }
 865                                  break;
 866  
 867                              case EBML_ID_INFO: // Contains miscellaneous general information and statistics on the file.
 868                                  $info_entry = array();
 869  
 870                                  while ($this->getEBMLelement($subelement, $element_data['end'], true)) {
 871                                      switch ($subelement['id']) {
 872  
 873                                          case EBML_ID_TIMECODESCALE:
 874                                              $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
 875                                              break;
 876  
 877                                          case EBML_ID_DURATION:
 878                                              $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']);
 879                                              break;
 880  
 881                                          case EBML_ID_DATEUTC:
 882                                              $info_entry[$subelement['id_name']]         = getid3_lib::BigEndian2Int($subelement['data']);
 883                                              $info_entry[$subelement['id_name'].'_unix'] = self::EBMLdate2unix($info_entry[$subelement['id_name']]);
 884                                              break;
 885  
 886                                          case EBML_ID_SEGMENTUID:
 887                                          case EBML_ID_PREVUID:
 888                                          case EBML_ID_NEXTUID:
 889                                              $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
 890                                              break;
 891  
 892                                          case EBML_ID_SEGMENTFAMILY:
 893                                              $info_entry[$subelement['id_name']][] = getid3_lib::trimNullByte($subelement['data']);
 894                                              break;
 895  
 896                                          case EBML_ID_SEGMENTFILENAME:
 897                                          case EBML_ID_PREVFILENAME:
 898                                          case EBML_ID_NEXTFILENAME:
 899                                          case EBML_ID_TITLE:
 900                                          case EBML_ID_MUXINGAPP:
 901                                          case EBML_ID_WRITINGAPP:
 902                                              $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
 903                                              $info['matroska']['comments'][strtolower($subelement['id_name'])][] = $info_entry[$subelement['id_name']];
 904                                              break;
 905  
 906                                          case EBML_ID_CHAPTERTRANSLATE:
 907                                              $chaptertranslate_entry = array();
 908  
 909                                              while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
 910                                                  switch ($sub_subelement['id']) {
 911  
 912                                                      case EBML_ID_CHAPTERTRANSLATEEDITIONUID:
 913                                                          $chaptertranslate_entry[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data']);
 914                                                          break;
 915  
 916                                                      case EBML_ID_CHAPTERTRANSLATECODEC:
 917                                                          $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
 918                                                          break;
 919  
 920                                                      case EBML_ID_CHAPTERTRANSLATEID:
 921                                                          $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
 922                                                          break;
 923  
 924                                                      default:
 925                                                          $this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement);
 926                                                          break;
 927                                                  }
 928                                              }
 929                                              $info_entry[$subelement['id_name']] = $chaptertranslate_entry;
 930                                              break;
 931  
 932                                          default:
 933                                              $this->unhandledElement('info', __LINE__, $subelement);
 934                                              break;
 935                                      }
 936                                  }
 937                                  $info['matroska']['info'][] = $info_entry;
 938                                  break;
 939  
 940                              case EBML_ID_CUES: // A top-level element to speed seeking access. All entries are local to the segment. Should be mandatory for non "live" streams.
 941                                  if ($this->hide_clusters) { // do not parse cues if hide clusters is "ON" till they point to clusters anyway
 942                                      $this->current_offset = $element_data['end'];
 943                                      break;
 944                                  }
 945                                  $cues_entry = array();
 946  
 947                                  while ($this->getEBMLelement($subelement, $element_data['end'])) {
 948                                      switch ($subelement['id']) {
 949  
 950                                          case EBML_ID_CUEPOINT:
 951                                              $cuepoint_entry = array();
 952  
 953                                              while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CUETRACKPOSITIONS))) {
 954                                                  switch ($sub_subelement['id']) {
 955  
 956                                                      case EBML_ID_CUETRACKPOSITIONS:
 957                                                          $cuetrackpositions_entry = array();
 958  
 959                                                          while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
 960                                                              switch ($sub_sub_subelement['id']) {
 961  
 962                                                                  case EBML_ID_CUETRACK:
 963                                                                  case EBML_ID_CUECLUSTERPOSITION:
 964                                                                  case EBML_ID_CUEBLOCKNUMBER:
 965                                                                  case EBML_ID_CUECODECSTATE:
 966                                                                      $cuetrackpositions_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
 967                                                                      break;
 968  
 969                                                                  default:
 970                                                                      $this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement);
 971                                                                      break;
 972                                                              }
 973                                                          }
 974                                                          $cuepoint_entry[$sub_subelement['id_name']][] = $cuetrackpositions_entry;
 975                                                          break;
 976  
 977                                                      case EBML_ID_CUETIME:
 978                                                          $cuepoint_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
 979                                                          break;
 980  
 981                                                      default:
 982                                                          $this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement);
 983                                                          break;
 984                                                  }
 985                                              }
 986                                              $cues_entry[] = $cuepoint_entry;
 987                                              break;
 988  
 989                                          default:
 990                                              $this->unhandledElement('cues', __LINE__, $subelement);
 991                                              break;
 992                                      }
 993                                  }
 994                                  $info['matroska']['cues'] = $cues_entry;
 995                                  break;
 996  
 997                              case EBML_ID_TAGS: // Element containing elements specific to Tracks/Chapters.
 998                                  $tags_entry = array();
 999  
1000                                  while ($this->getEBMLelement($subelement, $element_data['end'], false)) {
1001                                      switch ($subelement['id']) {
1002  
1003                                          case EBML_ID_TAG:
1004                                              $tag_entry = array();
1005  
1006                                              while ($this->getEBMLelement($sub_subelement, $subelement['end'], false)) {
1007                                                  switch ($sub_subelement['id']) {
1008  
1009                                                      case EBML_ID_TARGETS:
1010                                                          $targets_entry = array();
1011  
1012                                                          while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
1013                                                              switch ($sub_sub_subelement['id']) {
1014  
1015                                                                  case EBML_ID_TARGETTYPEVALUE:
1016                                                                      $targets_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
1017                                                                      $targets_entry[strtolower($sub_sub_subelement['id_name']).'_long'] = self::TargetTypeValue($targets_entry[$sub_sub_subelement['id_name']]);
1018                                                                      break;
1019  
1020                                                                  case EBML_ID_TARGETTYPE:
1021                                                                      $targets_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
1022                                                                      break;
1023  
1024                                                                  case EBML_ID_TAGTRACKUID:
1025                                                                  case EBML_ID_TAGEDITIONUID:
1026                                                                  case EBML_ID_TAGCHAPTERUID:
1027                                                                  case EBML_ID_TAGATTACHMENTUID:
1028                                                                      $targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::PrintHexBytes($sub_sub_subelement['data'], true, false);
1029                                                                      break;
1030  
1031                                                                  default:
1032                                                                      $this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement);
1033                                                                      break;
1034                                                              }
1035                                                          }
1036                                                          $tag_entry[$sub_subelement['id_name']] = $targets_entry;
1037                                                          break;
1038  
1039                                                      case EBML_ID_SIMPLETAG:
1040                                                          $tag_entry[$sub_subelement['id_name']][] = $this->HandleEMBLSimpleTag($sub_subelement['end']);
1041                                                          break;
1042  
1043                                                      default:
1044                                                          $this->unhandledElement('tags.tag', __LINE__, $sub_subelement);
1045                                                          break;
1046                                                  }
1047                                              }
1048                                              $tags_entry[] = $tag_entry;
1049                                              break;
1050  
1051                                          default:
1052                                              $this->unhandledElement('tags', __LINE__, $subelement);
1053                                              break;
1054                                      }
1055                                  }
1056                                  $info['matroska']['tags'] = $tags_entry;
1057                                  break;
1058  
1059                              case EBML_ID_ATTACHMENTS: // Contain attached files.
1060  
1061                                  while ($this->getEBMLelement($subelement, $element_data['end'])) {
1062                                      switch ($subelement['id']) {
1063  
1064                                          case EBML_ID_ATTACHEDFILE:
1065                                              $attachedfile_entry = array();
1066  
1067                                              while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_FILEDATA))) {
1068                                                  switch ($sub_subelement['id']) {
1069  
1070                                                      case EBML_ID_FILEDESCRIPTION:
1071                                                      case EBML_ID_FILENAME:
1072                                                      case EBML_ID_FILEMIMETYPE:
1073                                                          $attachedfile_entry[$sub_subelement['id_name']] = $sub_subelement['data'];
1074                                                          break;
1075  
1076                                                      case EBML_ID_FILEDATA:
1077                                                          $attachedfile_entry['data_offset'] = $this->current_offset;
1078                                                          $attachedfile_entry['data_length'] = $sub_subelement['length'];
1079  
1080                                                          $attachedfile_entry[$sub_subelement['id_name']] = $this->saveAttachment(
1081                                                              $attachedfile_entry['FileName'],
1082                                                              $attachedfile_entry['data_offset'],
1083                                                              $attachedfile_entry['data_length']);
1084  
1085                                                          $this->current_offset = $sub_subelement['end'];
1086                                                          break;
1087  
1088                                                      case EBML_ID_FILEUID:
1089                                                          $attachedfile_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1090                                                          break;
1091  
1092                                                      default:
1093                                                          $this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement);
1094                                                          break;
1095                                                  }
1096                                              }
1097                                              $info['matroska']['attachments'][] = $attachedfile_entry;
1098                                              break;
1099  
1100                                          default:
1101                                              $this->unhandledElement('attachments', __LINE__, $subelement);
1102                                              break;
1103                                      }
1104                                  }
1105                                  break;
1106  
1107                              case EBML_ID_CHAPTERS:
1108  
1109                                  while ($this->getEBMLelement($subelement, $element_data['end'])) {
1110                                      switch ($subelement['id']) {
1111  
1112                                          case EBML_ID_EDITIONENTRY:
1113                                              $editionentry_entry = array();
1114  
1115                                              while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CHAPTERATOM))) {
1116                                                  switch ($sub_subelement['id']) {
1117  
1118                                                      case EBML_ID_EDITIONUID:
1119                                                          $editionentry_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1120                                                          break;
1121  
1122                                                      case EBML_ID_EDITIONFLAGHIDDEN:
1123                                                      case EBML_ID_EDITIONFLAGDEFAULT:
1124                                                      case EBML_ID_EDITIONFLAGORDERED:
1125                                                          $editionentry_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']);
1126                                                          break;
1127  
1128                                                      case EBML_ID_CHAPTERATOM:
1129                                                          $chapteratom_entry = array();
1130  
1131                                                          while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CHAPTERTRACK, EBML_ID_CHAPTERDISPLAY))) {
1132                                                              switch ($sub_sub_subelement['id']) {
1133  
1134                                                                  case EBML_ID_CHAPTERSEGMENTUID:
1135                                                                  case EBML_ID_CHAPTERSEGMENTEDITIONUID:
1136                                                                      $chapteratom_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
1137                                                                      break;
1138  
1139                                                                  case EBML_ID_CHAPTERFLAGENABLED:
1140                                                                  case EBML_ID_CHAPTERFLAGHIDDEN:
1141                                                                      $chapteratom_entry[$sub_sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
1142                                                                      break;
1143  
1144                                                                  case EBML_ID_CHAPTERUID:
1145                                                                  case EBML_ID_CHAPTERTIMESTART:
1146                                                                  case EBML_ID_CHAPTERTIMEEND:
1147                                                                      $chapteratom_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
1148                                                                      break;
1149  
1150                                                                  case EBML_ID_CHAPTERTRACK:
1151                                                                      $chaptertrack_entry = array();
1152  
1153                                                                      while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
1154                                                                          switch ($sub_sub_sub_subelement['id']) {
1155  
1156                                                                              case EBML_ID_CHAPTERTRACKNUMBER:
1157                                                                                  $chaptertrack_entry[$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
1158                                                                                  break;
1159  
1160                                                                              default:
1161                                                                                  $this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement);
1162                                                                                  break;
1163                                                                          }
1164                                                                      }
1165                                                                      $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chaptertrack_entry;
1166                                                                      break;
1167  
1168                                                                  case EBML_ID_CHAPTERDISPLAY:
1169                                                                      $chapterdisplay_entry = array();
1170  
1171                                                                      while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
1172                                                                          switch ($sub_sub_sub_subelement['id']) {
1173  
1174                                                                              case EBML_ID_CHAPSTRING:
1175                                                                              case EBML_ID_CHAPLANGUAGE:
1176                                                                              case EBML_ID_CHAPCOUNTRY:
1177                                                                                  $chapterdisplay_entry[$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
1178                                                                                  break;
1179  
1180                                                                              default:
1181                                                                                  $this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement);
1182                                                                                  break;
1183                                                                          }
1184                                                                      }
1185                                                                      $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chapterdisplay_entry;
1186                                                                      break;
1187  
1188                                                                  default:
1189                                                                      $this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement);
1190                                                                      break;
1191                                                              }
1192                                                          }
1193                                                          $editionentry_entry[$sub_subelement['id_name']][] = $chapteratom_entry;
1194                                                          break;
1195  
1196                                                      default:
1197                                                          $this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement);
1198                                                          break;
1199                                                  }
1200                                              }
1201                                              $info['matroska']['chapters'][] = $editionentry_entry;
1202                                              break;
1203  
1204                                          default:
1205                                              $this->unhandledElement('chapters', __LINE__, $subelement);
1206                                              break;
1207                                      }
1208                                  }
1209                                  break;
1210  
1211                              case EBML_ID_CLUSTER: // The lower level element containing the (monolithic) Block structure.
1212                                  $cluster_entry = array();
1213  
1214                                  while ($this->getEBMLelement($subelement, $element_data['end'], array(EBML_ID_CLUSTERSILENTTRACKS, EBML_ID_CLUSTERBLOCKGROUP, EBML_ID_CLUSTERSIMPLEBLOCK))) {
1215                                      switch ($subelement['id']) {
1216  
1217                                          case EBML_ID_CLUSTERTIMECODE:
1218                                          case EBML_ID_CLUSTERPOSITION:
1219                                          case EBML_ID_CLUSTERPREVSIZE:
1220                                              $cluster_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
1221                                              break;
1222  
1223                                          case EBML_ID_CLUSTERSILENTTRACKS:
1224                                              $cluster_silent_tracks = array();
1225  
1226                                              while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
1227                                                  switch ($sub_subelement['id']) {
1228  
1229                                                      case EBML_ID_CLUSTERSILENTTRACKNUMBER:
1230                                                          $cluster_silent_tracks[] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1231                                                          break;
1232  
1233                                                      default:
1234                                                          $this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement);
1235                                                          break;
1236                                                  }
1237                                              }
1238                                              $cluster_entry[$subelement['id_name']][] = $cluster_silent_tracks;
1239                                              break;
1240  
1241                                          case EBML_ID_CLUSTERBLOCKGROUP:
1242                                              $cluster_block_group = array('offset' => $this->current_offset);
1243  
1244                                              while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CLUSTERBLOCK))) {
1245                                                  switch ($sub_subelement['id']) {
1246  
1247                                                      case EBML_ID_CLUSTERBLOCK:
1248                                                          $cluster_block_group[$sub_subelement['id_name']] = $this->HandleEMBLClusterBlock($sub_subelement, EBML_ID_CLUSTERBLOCK, $info);
1249                                                          break;
1250  
1251                                                      case EBML_ID_CLUSTERREFERENCEPRIORITY: // unsigned-int
1252                                                      case EBML_ID_CLUSTERBLOCKDURATION:     // unsigned-int
1253                                                          $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1254                                                          break;
1255  
1256                                                      case EBML_ID_CLUSTERREFERENCEBLOCK:    // signed-int
1257                                                          $cluster_block_group[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data'], false, true);
1258                                                          break;
1259  
1260                                                      case EBML_ID_CLUSTERCODECSTATE:
1261                                                          $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
1262                                                          break;
1263  
1264                                                      default:
1265                                                          $this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement);
1266                                                          break;
1267                                                  }
1268                                              }
1269                                              $cluster_entry[$subelement['id_name']][] = $cluster_block_group;
1270                                              break;
1271  
1272                                          case EBML_ID_CLUSTERSIMPLEBLOCK:
1273                                              $cluster_entry[$subelement['id_name']][] = $this->HandleEMBLClusterBlock($subelement, EBML_ID_CLUSTERSIMPLEBLOCK, $info);
1274                                              break;
1275  
1276                                          default:
1277                                              $this->unhandledElement('cluster', __LINE__, $subelement);
1278                                              break;
1279                                      }
1280                                      $this->current_offset = $subelement['end'];
1281                                  }
1282  
1283                                  if (!$this->hide_clusters || $this->playtimeFromMetadata($info) === false) {
1284                                      $info['matroska']['cluster'][] = $cluster_entry;
1285                                  }
1286                                  if ($this->scan_mode === MATROSKA_SCAN_FIRST_CLUSTER) {
1287                                      // Stop parsing after finding first cluster
1288                                      return;
1289                                  }
1290  
1291                                  // check to see if all the data we need exists already, if so, break out of the loop
1292                                  if ($this->scan_mode === MATROSKA_SCAN_HEADER) {
1293                                      if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
1294                                          if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
1295                                              if (count($info['matroska']['track_data_offsets']) == count($info['matroska']['tracks']['tracks'])) {
1296                                                  return;
1297                                              }
1298                                          }
1299                                      }
1300                                  }
1301                                  break;
1302  
1303                              default:
1304                                  $this->unhandledElement('segment', __LINE__, $element_data);
1305                                  break;
1306                          }
1307                      }
1308                      break;
1309  
1310                  default:
1311                      $this->unhandledElement('root', __LINE__, $top_element);
1312                      break;
1313              }
1314          }
1315      }
1316  
1317      /**
1318       * @param int $min_data
1319       *
1320       * @return bool
1321       */
1322  	private function EnsureBufferHasEnoughData($min_data=1024) {
1323          if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) {
1324              $read_bytes = max($min_data, $this->getid3->fread_buffer_size());
1325  
1326              try {
1327                  $this->fseek($this->current_offset);
1328                  $this->EBMLbuffer_offset = $this->current_offset;
1329                  $this->EBMLbuffer        = $this->fread($read_bytes);
1330                  $this->EBMLbuffer_length = strlen($this->EBMLbuffer);
1331              } catch (getid3_exception $e) {
1332                  $this->warning('EBML parser: '.$e->getMessage());
1333                  return false;
1334              }
1335  
1336              if ($this->EBMLbuffer_length == 0 && $this->feof()) {
1337                  return $this->error('EBML parser: ran out of file at offset '.$this->current_offset);
1338              }
1339          }
1340          return true;
1341      }
1342  
1343      /**
1344       * @return int|float|false
1345       */
1346  	private function readEBMLint() {
1347          $actual_offset = $this->current_offset - $this->EBMLbuffer_offset;
1348  
1349          // get length of integer
1350          $first_byte_int = ord($this->EBMLbuffer[$actual_offset]);
1351          if       (0x80 & $first_byte_int) {
1352              $length = 1;
1353          } elseif (0x40 & $first_byte_int) {
1354              $length = 2;
1355          } elseif (0x20 & $first_byte_int) {
1356              $length = 3;
1357          } elseif (0x10 & $first_byte_int) {
1358              $length = 4;
1359          } elseif (0x08 & $first_byte_int) {
1360              $length = 5;
1361          } elseif (0x04 & $first_byte_int) {
1362              $length = 6;
1363          } elseif (0x02 & $first_byte_int) {
1364              $length = 7;
1365          } elseif (0x01 & $first_byte_int) {
1366              $length = 8;
1367          } else {
1368              throw new Exception('invalid EBML integer (leading 0x00) at '.$this->current_offset);
1369          }
1370  
1371          // read
1372          $int_value = self::EBML2Int(substr($this->EBMLbuffer, $actual_offset, $length));
1373          $this->current_offset += $length;
1374  
1375          return $int_value;
1376      }
1377  
1378      /**
1379       * @param int  $length
1380       * @param bool $check_buffer
1381       *
1382       * @return string|false
1383       */
1384  	private function readEBMLelementData($length, $check_buffer=false) {
1385          if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) {
1386              return false;
1387          }
1388          $data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length);
1389          $this->current_offset += $length;
1390          return $data;
1391      }
1392  
1393      /**
1394       * @param array      $element
1395       * @param int        $parent_end
1396       * @param array|bool $get_data
1397       *
1398       * @return bool
1399       */
1400  	private function getEBMLelement(&$element, $parent_end, $get_data=false) {
1401          if ($this->current_offset >= $parent_end) {
1402              return false;
1403          }
1404  
1405          if (!$this->EnsureBufferHasEnoughData()) {
1406              $this->current_offset = PHP_INT_MAX; // do not exit parser right now, allow to finish current loop to gather maximum information
1407              return false;
1408          }
1409  
1410          $element = array();
1411  
1412          // set offset
1413          $element['offset'] = $this->current_offset;
1414  
1415          // get ID
1416          $element['id'] = $this->readEBMLint();
1417  
1418          // get name
1419          $element['id_name'] = self::EBMLidName($element['id']);
1420  
1421          // get length
1422          $element['length'] = $this->readEBMLint();
1423  
1424          // get end offset
1425          $element['end'] = $this->current_offset + $element['length'];
1426  
1427          // get raw data
1428          $dont_parse = (in_array($element['id'], $this->unuseful_elements) || $element['id_name'] == dechex($element['id']));
1429          if (($get_data === true || (is_array($get_data) && !in_array($element['id'], $get_data))) && !$dont_parse) {
1430              $element['data'] = $this->readEBMLelementData($element['length'], $element);
1431          }
1432  
1433          return true;
1434      }
1435  
1436      /**
1437       * @param string $type
1438       * @param int    $line
1439       * @param array  $element
1440       */
1441  	private function unhandledElement($type, $line, $element) {
1442          // warn only about unknown and missed elements, not about unuseful
1443          if (!in_array($element['id'], $this->unuseful_elements)) {
1444              $this->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']);
1445          }
1446  
1447          // increase offset for unparsed elements
1448          if (!isset($element['data'])) {
1449              $this->current_offset = $element['end'];
1450          }
1451      }
1452  
1453      /**
1454       * @param array $SimpleTagArray
1455       *
1456       * @return bool
1457       */
1458  	private function ExtractCommentsSimpleTag($SimpleTagArray) {
1459          if (!empty($SimpleTagArray['SimpleTag'])) {
1460              foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) {
1461                  if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) {
1462                      $this->getid3->info['matroska']['comments'][strtolower($SimpleTagData['TagName'])][] = $SimpleTagData['TagString'];
1463                  }
1464                  if (!empty($SimpleTagData['SimpleTag'])) {
1465                      $this->ExtractCommentsSimpleTag($SimpleTagData);
1466                  }
1467              }
1468          }
1469  
1470          return true;
1471      }
1472  
1473      /**
1474       * @param int $parent_end
1475       *
1476       * @return array
1477       */
1478  	private function HandleEMBLSimpleTag($parent_end) {
1479          $simpletag_entry = array();
1480  
1481          while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) {
1482              switch ($element['id']) {
1483  
1484                  case EBML_ID_TAGNAME:
1485                  case EBML_ID_TAGLANGUAGE:
1486                  case EBML_ID_TAGSTRING:
1487                  case EBML_ID_TAGBINARY:
1488                      $simpletag_entry[$element['id_name']] = $element['data'];
1489                      break;
1490  
1491                  case EBML_ID_SIMPLETAG:
1492                      $simpletag_entry[$element['id_name']][] = $this->HandleEMBLSimpleTag($element['end']);
1493                      break;
1494  
1495                  case EBML_ID_TAGDEFAULT:
1496                      $simpletag_entry[$element['id_name']] = (bool)getid3_lib::BigEndian2Int($element['data']);
1497                      break;
1498  
1499                  default:
1500                      $this->unhandledElement('tag.simpletag', __LINE__, $element);
1501                      break;
1502              }
1503          }
1504  
1505          return $simpletag_entry;
1506      }
1507  
1508      /**
1509       * @param array $element
1510       * @param int   $block_type
1511       * @param array $info
1512       *
1513       * @return array
1514       */
1515  	private function HandleEMBLClusterBlock($element, $block_type, &$info) {
1516          // http://www.matroska.org/technical/specs/index.html#block_structure
1517          // http://www.matroska.org/technical/specs/index.html#simpleblock_structure
1518  
1519          $block_data = array();
1520          $block_data['tracknumber'] = $this->readEBMLint();
1521          $block_data['timecode']    = getid3_lib::BigEndian2Int($this->readEBMLelementData(2), false, true);
1522          $block_data['flags_raw']   = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
1523  
1524          if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
1525              $block_data['flags']['keyframe']  = (($block_data['flags_raw'] & 0x80) >> 7);
1526              //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0x70) >> 4);
1527          }
1528          else {
1529              //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0xF0) >> 4);
1530          }
1531          $block_data['flags']['invisible'] = (bool)(($block_data['flags_raw'] & 0x08) >> 3);
1532          $block_data['flags']['lacing']    =       (($block_data['flags_raw'] & 0x06) >> 1);  // 00=no lacing; 01=Xiph lacing; 11=EBML lacing; 10=fixed-size lacing
1533          if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
1534              $block_data['flags']['discardable'] = (($block_data['flags_raw'] & 0x01));
1535          }
1536          else {
1537              //$block_data['flags']['reserved2'] = (($block_data['flags_raw'] & 0x01) >> 0);
1538          }
1539          $block_data['flags']['lacing_type'] = self::BlockLacingType($block_data['flags']['lacing']);
1540  
1541          // Lace (when lacing bit is set)
1542          if ($block_data['flags']['lacing'] > 0) {
1543              $block_data['lace_frames'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)) + 1; // Number of frames in the lace-1 (uint8)
1544              if ($block_data['flags']['lacing'] != 0x02) {
1545                  for ($i = 1; $i < $block_data['lace_frames']; $i ++) { // Lace-coded size of each frame of the lace, except for the last one (multiple uint8). *This is not used with Fixed-size lacing as it is calculated automatically from (total size of lace) / (number of frames in lace).
1546                      if ($block_data['flags']['lacing'] == 0x03) { // EBML lacing
1547                          $block_data['lace_frames_size'][$i] = $this->readEBMLint(); // TODO: read size correctly, calc size for the last frame. For now offsets are deteminded OK with readEBMLint() and that's the most important thing.
1548                      }
1549                      else { // Xiph lacing
1550                          $block_data['lace_frames_size'][$i] = 0;
1551                          do {
1552                              $size = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
1553                              $block_data['lace_frames_size'][$i] += $size;
1554                          }
1555                          while ($size == 255);
1556                      }
1557                  }
1558                  if ($block_data['flags']['lacing'] == 0x01) { // calc size of the last frame only for Xiph lacing, till EBML sizes are now anyway determined incorrectly
1559                      $block_data['lace_frames_size'][] = $element['end'] - $this->current_offset - array_sum($block_data['lace_frames_size']);
1560                  }
1561              }
1562          }
1563  
1564          if (!isset($info['matroska']['track_data_offsets'][$block_data['tracknumber']])) {
1565              $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['offset'] = $this->current_offset;
1566              $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'] = $element['end'] - $this->current_offset;
1567              //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] = 0;
1568          }
1569          //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] += $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'];
1570          //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['duration']      = $block_data['timecode'] * ((isset($info['matroska']['info'][0]['TimecodeScale']) ? $info['matroska']['info'][0]['TimecodeScale'] : 1000000) / 1000000000);
1571  
1572          // set offset manually
1573          $this->current_offset = $element['end'];
1574  
1575          return $block_data;
1576      }
1577  
1578      /**
1579       * @param string $EBMLstring
1580       *
1581       * @return int|float|false
1582       */
1583  	private static function EBML2Int($EBMLstring) {
1584          // http://matroska.org/specs/
1585  
1586          // Element ID coded with an UTF-8 like system:
1587          // 1xxx xxxx                                  - Class A IDs (2^7 -2 possible values) (base 0x8X)
1588          // 01xx xxxx  xxxx xxxx                       - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX)
1589          // 001x xxxx  xxxx xxxx  xxxx xxxx            - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX)
1590          // 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX)
1591          // Values with all x at 0 and 1 are reserved (hence the -2).
1592  
1593          // Data size, in octets, is also coded with an UTF-8 like system :
1594          // 1xxx xxxx                                                                              - value 0 to  2^7-2
1595          // 01xx xxxx  xxxx xxxx                                                                   - value 0 to 2^14-2
1596          // 001x xxxx  xxxx xxxx  xxxx xxxx                                                        - value 0 to 2^21-2
1597          // 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                             - value 0 to 2^28-2
1598          // 0000 1xxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                  - value 0 to 2^35-2
1599          // 0000 01xx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                       - value 0 to 2^42-2
1600          // 0000 001x  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx            - value 0 to 2^49-2
1601          // 0000 0001  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - value 0 to 2^56-2
1602  
1603          $first_byte_int = ord($EBMLstring[0]);
1604          if (0x80 & $first_byte_int) {
1605              $EBMLstring[0] = chr($first_byte_int & 0x7F);
1606          } elseif (0x40 & $first_byte_int) {
1607              $EBMLstring[0] = chr($first_byte_int & 0x3F);
1608          } elseif (0x20 & $first_byte_int) {
1609              $EBMLstring[0] = chr($first_byte_int & 0x1F);
1610          } elseif (0x10 & $first_byte_int) {
1611              $EBMLstring[0] = chr($first_byte_int & 0x0F);
1612          } elseif (0x08 & $first_byte_int) {
1613              $EBMLstring[0] = chr($first_byte_int & 0x07);
1614          } elseif (0x04 & $first_byte_int) {
1615              $EBMLstring[0] = chr($first_byte_int & 0x03);
1616          } elseif (0x02 & $first_byte_int) {
1617              $EBMLstring[0] = chr($first_byte_int & 0x01);
1618          } elseif (0x01 & $first_byte_int) {
1619              $EBMLstring[0] = chr($first_byte_int & 0x00);
1620          }
1621  
1622          return getid3_lib::BigEndian2Int($EBMLstring);
1623      }
1624  
1625      /**
1626       * @param int $EBMLdatestamp
1627       *
1628       * @return float
1629       */
1630  	private static function EBMLdate2unix($EBMLdatestamp) {
1631          // Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC)
1632          // 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC
1633          return round(($EBMLdatestamp / 1000000000) + 978307200);
1634      }
1635  
1636      /**
1637       * @param int $target_type
1638       *
1639       * @return string|int
1640       */
1641  	public static function TargetTypeValue($target_type) {
1642          // http://www.matroska.org/technical/specs/tagging/index.html
1643          static $TargetTypeValue = array();
1644          if (empty($TargetTypeValue)) {
1645              $TargetTypeValue[10] = 'A: ~ V:shot';                                           // the lowest hierarchy found in music or movies
1646              $TargetTypeValue[20] = 'A:subtrack/part/movement ~ V:scene';                    // corresponds to parts of a track for audio (like a movement)
1647              $TargetTypeValue[30] = 'A:track/song ~ V:chapter';                              // the common parts of an album or a movie
1648              $TargetTypeValue[40] = 'A:part/session ~ V:part/session';                       // when an album or episode has different logical parts
1649              $TargetTypeValue[50] = 'A:album/opera/concert ~ V:movie/episode/concert';       // the most common grouping level of music and video (equals to an episode for TV series)
1650              $TargetTypeValue[60] = 'A:edition/issue/volume/opus ~ V:season/sequel/volume';  // a list of lower levels grouped together
1651              $TargetTypeValue[70] = 'A:collection ~ V:collection';                           // the high hierarchy consisting of many different lower items
1652          }
1653          return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type);
1654      }
1655  
1656      /**
1657       * @param int $lacingtype
1658       *
1659       * @return string|int
1660       */
1661  	public static function BlockLacingType($lacingtype) {
1662          // http://matroska.org/technical/specs/index.html#block_structure
1663          static $BlockLacingType = array();
1664          if (empty($BlockLacingType)) {
1665              $BlockLacingType[0x00] = 'no lacing';
1666              $BlockLacingType[0x01] = 'Xiph lacing';
1667              $BlockLacingType[0x02] = 'fixed-size lacing';
1668              $BlockLacingType[0x03] = 'EBML lacing';
1669          }
1670          return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype);
1671      }
1672  
1673      /**
1674       * @param string $codecid
1675       *
1676       * @return string
1677       */
1678  	public static function CodecIDtoCommonName($codecid) {
1679          // http://www.matroska.org/technical/specs/codecid/index.html
1680          static $CodecIDlist = array();
1681          if (empty($CodecIDlist)) {
1682              $CodecIDlist['A_AAC']            = 'aac';
1683              $CodecIDlist['A_AAC/MPEG2/LC']   = 'aac';
1684              $CodecIDlist['A_AC3']            = 'ac3';
1685              $CodecIDlist['A_EAC3']           = 'eac3';
1686              $CodecIDlist['A_DTS']            = 'dts';
1687              $CodecIDlist['A_FLAC']           = 'flac';
1688              $CodecIDlist['A_MPEG/L1']        = 'mp1';
1689              $CodecIDlist['A_MPEG/L2']        = 'mp2';
1690              $CodecIDlist['A_MPEG/L3']        = 'mp3';
1691              $CodecIDlist['A_PCM/INT/LIT']    = 'pcm';       // PCM Integer Little Endian
1692              $CodecIDlist['A_PCM/INT/BIG']    = 'pcm';       // PCM Integer Big Endian
1693              $CodecIDlist['A_QUICKTIME/QDMC'] = 'quicktime'; // Quicktime: QDesign Music
1694              $CodecIDlist['A_QUICKTIME/QDM2'] = 'quicktime'; // Quicktime: QDesign Music v2
1695              $CodecIDlist['A_VORBIS']         = 'vorbis';
1696              $CodecIDlist['V_MPEG1']          = 'mpeg';
1697              $CodecIDlist['V_THEORA']         = 'theora';
1698              $CodecIDlist['V_REAL/RV40']      = 'real';
1699              $CodecIDlist['V_REAL/RV10']      = 'real';
1700              $CodecIDlist['V_REAL/RV20']      = 'real';
1701              $CodecIDlist['V_REAL/RV30']      = 'real';
1702              $CodecIDlist['V_QUICKTIME']      = 'quicktime'; // Quicktime
1703              $CodecIDlist['V_MPEG4/ISO/AP']   = 'mpeg4';
1704              $CodecIDlist['V_MPEG4/ISO/ASP']  = 'mpeg4';
1705              $CodecIDlist['V_MPEG4/ISO/AVC']  = 'h264';
1706              $CodecIDlist['V_MPEG4/ISO/SP']   = 'mpeg4';
1707              $CodecIDlist['V_VP8']            = 'vp8';
1708              $CodecIDlist['V_MS/VFW/FOURCC']  = 'vcm'; // Microsoft (TM) Video Codec Manager (VCM)
1709              $CodecIDlist['A_MS/ACM']         = 'acm'; // Microsoft (TM) Audio Codec Manager (ACM)
1710          }
1711          return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid);
1712      }
1713  
1714      /**
1715       * @param int $value
1716       *
1717       * @return string
1718       */
1719  	private static function EBMLidName($value) {
1720          static $EBMLidList = array();
1721          if (empty($EBMLidList)) {
1722              $EBMLidList[EBML_ID_ASPECTRATIOTYPE]            = 'AspectRatioType';
1723              $EBMLidList[EBML_ID_ATTACHEDFILE]               = 'AttachedFile';
1724              $EBMLidList[EBML_ID_ATTACHMENTLINK]             = 'AttachmentLink';
1725              $EBMLidList[EBML_ID_ATTACHMENTS]                = 'Attachments';
1726              $EBMLidList[EBML_ID_AUDIO]                      = 'Audio';
1727              $EBMLidList[EBML_ID_BITDEPTH]                   = 'BitDepth';
1728              $EBMLidList[EBML_ID_CHANNELPOSITIONS]           = 'ChannelPositions';
1729              $EBMLidList[EBML_ID_CHANNELS]                   = 'Channels';
1730              $EBMLidList[EBML_ID_CHAPCOUNTRY]                = 'ChapCountry';
1731              $EBMLidList[EBML_ID_CHAPLANGUAGE]               = 'ChapLanguage';
1732              $EBMLidList[EBML_ID_CHAPPROCESS]                = 'ChapProcess';
1733              $EBMLidList[EBML_ID_CHAPPROCESSCODECID]         = 'ChapProcessCodecID';
1734              $EBMLidList[EBML_ID_CHAPPROCESSCOMMAND]         = 'ChapProcessCommand';
1735              $EBMLidList[EBML_ID_CHAPPROCESSDATA]            = 'ChapProcessData';
1736              $EBMLidList[EBML_ID_CHAPPROCESSPRIVATE]         = 'ChapProcessPrivate';
1737              $EBMLidList[EBML_ID_CHAPPROCESSTIME]            = 'ChapProcessTime';
1738              $EBMLidList[EBML_ID_CHAPSTRING]                 = 'ChapString';
1739              $EBMLidList[EBML_ID_CHAPTERATOM]                = 'ChapterAtom';
1740              $EBMLidList[EBML_ID_CHAPTERDISPLAY]             = 'ChapterDisplay';
1741              $EBMLidList[EBML_ID_CHAPTERFLAGENABLED]         = 'ChapterFlagEnabled';
1742              $EBMLidList[EBML_ID_CHAPTERFLAGHIDDEN]          = 'ChapterFlagHidden';
1743              $EBMLidList[EBML_ID_CHAPTERPHYSICALEQUIV]       = 'ChapterPhysicalEquiv';
1744              $EBMLidList[EBML_ID_CHAPTERS]                   = 'Chapters';
1745              $EBMLidList[EBML_ID_CHAPTERSEGMENTEDITIONUID]   = 'ChapterSegmentEditionUID';
1746              $EBMLidList[EBML_ID_CHAPTERSEGMENTUID]          = 'ChapterSegmentUID';
1747              $EBMLidList[EBML_ID_CHAPTERTIMEEND]             = 'ChapterTimeEnd';
1748              $EBMLidList[EBML_ID_CHAPTERTIMESTART]           = 'ChapterTimeStart';
1749              $EBMLidList[EBML_ID_CHAPTERTRACK]               = 'ChapterTrack';
1750              $EBMLidList[EBML_ID_CHAPTERTRACKNUMBER]         = 'ChapterTrackNumber';
1751              $EBMLidList[EBML_ID_CHAPTERTRANSLATE]           = 'ChapterTranslate';
1752              $EBMLidList[EBML_ID_CHAPTERTRANSLATECODEC]      = 'ChapterTranslateCodec';
1753              $EBMLidList[EBML_ID_CHAPTERTRANSLATEEDITIONUID] = 'ChapterTranslateEditionUID';
1754              $EBMLidList[EBML_ID_CHAPTERTRANSLATEID]         = 'ChapterTranslateID';
1755              $EBMLidList[EBML_ID_CHAPTERUID]                 = 'ChapterUID';
1756              $EBMLidList[EBML_ID_CLUSTER]                    = 'Cluster';
1757              $EBMLidList[EBML_ID_CLUSTERBLOCK]               = 'ClusterBlock';
1758              $EBMLidList[EBML_ID_CLUSTERBLOCKADDID]          = 'ClusterBlockAddID';
1759              $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONAL]     = 'ClusterBlockAdditional';
1760              $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONID]     = 'ClusterBlockAdditionID';
1761              $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONS]      = 'ClusterBlockAdditions';
1762              $EBMLidList[EBML_ID_CLUSTERBLOCKDURATION]       = 'ClusterBlockDuration';
1763              $EBMLidList[EBML_ID_CLUSTERBLOCKGROUP]          = 'ClusterBlockGroup';
1764              $EBMLidList[EBML_ID_CLUSTERBLOCKMORE]           = 'ClusterBlockMore';
1765              $EBMLidList[EBML_ID_CLUSTERBLOCKVIRTUAL]        = 'ClusterBlockVirtual';
1766              $EBMLidList[EBML_ID_CLUSTERCODECSTATE]          = 'ClusterCodecState';
1767              $EBMLidList[EBML_ID_CLUSTERDELAY]               = 'ClusterDelay';
1768              $EBMLidList[EBML_ID_CLUSTERDURATION]            = 'ClusterDuration';
1769              $EBMLidList[EBML_ID_CLUSTERENCRYPTEDBLOCK]      = 'ClusterEncryptedBlock';
1770              $EBMLidList[EBML_ID_CLUSTERFRAMENUMBER]         = 'ClusterFrameNumber';
1771              $EBMLidList[EBML_ID_CLUSTERLACENUMBER]          = 'ClusterLaceNumber';
1772              $EBMLidList[EBML_ID_CLUSTERPOSITION]            = 'ClusterPosition';
1773              $EBMLidList[EBML_ID_CLUSTERPREVSIZE]            = 'ClusterPrevSize';
1774              $EBMLidList[EBML_ID_CLUSTERREFERENCEBLOCK]      = 'ClusterReferenceBlock';
1775              $EBMLidList[EBML_ID_CLUSTERREFERENCEPRIORITY]   = 'ClusterReferencePriority';
1776              $EBMLidList[EBML_ID_CLUSTERREFERENCEVIRTUAL]    = 'ClusterReferenceVirtual';
1777              $EBMLidList[EBML_ID_CLUSTERSILENTTRACKNUMBER]   = 'ClusterSilentTrackNumber';
1778              $EBMLidList[EBML_ID_CLUSTERSILENTTRACKS]        = 'ClusterSilentTracks';
1779              $EBMLidList[EBML_ID_CLUSTERSIMPLEBLOCK]         = 'ClusterSimpleBlock';
1780              $EBMLidList[EBML_ID_CLUSTERTIMECODE]            = 'ClusterTimecode';
1781              $EBMLidList[EBML_ID_CLUSTERTIMESLICE]           = 'ClusterTimeSlice';
1782              $EBMLidList[EBML_ID_CODECDECODEALL]             = 'CodecDecodeAll';
1783              $EBMLidList[EBML_ID_CODECDOWNLOADURL]           = 'CodecDownloadURL';
1784              $EBMLidList[EBML_ID_CODECID]                    = 'CodecID';
1785              $EBMLidList[EBML_ID_CODECINFOURL]               = 'CodecInfoURL';
1786              $EBMLidList[EBML_ID_CODECNAME]                  = 'CodecName';
1787              $EBMLidList[EBML_ID_CODECPRIVATE]               = 'CodecPrivate';
1788              $EBMLidList[EBML_ID_CODECSETTINGS]              = 'CodecSettings';
1789              $EBMLidList[EBML_ID_COLOURSPACE]                = 'ColourSpace';
1790              $EBMLidList[EBML_ID_CONTENTCOMPALGO]            = 'ContentCompAlgo';
1791              $EBMLidList[EBML_ID_CONTENTCOMPRESSION]         = 'ContentCompression';
1792              $EBMLidList[EBML_ID_CONTENTCOMPSETTINGS]        = 'ContentCompSettings';
1793              $EBMLidList[EBML_ID_CONTENTENCALGO]             = 'ContentEncAlgo';
1794              $EBMLidList[EBML_ID_CONTENTENCKEYID]            = 'ContentEncKeyID';
1795              $EBMLidList[EBML_ID_CONTENTENCODING]            = 'ContentEncoding';
1796              $EBMLidList[EBML_ID_CONTENTENCODINGORDER]       = 'ContentEncodingOrder';
1797              $EBMLidList[EBML_ID_CONTENTENCODINGS]           = 'ContentEncodings';
1798              $EBMLidList[EBML_ID_CONTENTENCODINGSCOPE]       = 'ContentEncodingScope';
1799              $EBMLidList[EBML_ID_CONTENTENCODINGTYPE]        = 'ContentEncodingType';
1800              $EBMLidList[EBML_ID_CONTENTENCRYPTION]          = 'ContentEncryption';
1801              $EBMLidList[EBML_ID_CONTENTSIGALGO]             = 'ContentSigAlgo';
1802              $EBMLidList[EBML_ID_CONTENTSIGHASHALGO]         = 'ContentSigHashAlgo';
1803              $EBMLidList[EBML_ID_CONTENTSIGKEYID]            = 'ContentSigKeyID';
1804              $EBMLidList[EBML_ID_CONTENTSIGNATURE]           = 'ContentSignature';
1805              $EBMLidList[EBML_ID_CRC32]                      = 'CRC32';
1806              $EBMLidList[EBML_ID_CUEBLOCKNUMBER]             = 'CueBlockNumber';
1807              $EBMLidList[EBML_ID_CUECLUSTERPOSITION]         = 'CueClusterPosition';
1808              $EBMLidList[EBML_ID_CUECODECSTATE]              = 'CueCodecState';
1809              $EBMLidList[EBML_ID_CUEPOINT]                   = 'CuePoint';
1810              $EBMLidList[EBML_ID_CUEREFCLUSTER]              = 'CueRefCluster';
1811              $EBMLidList[EBML_ID_CUEREFCODECSTATE]           = 'CueRefCodecState';
1812              $EBMLidList[EBML_ID_CUEREFERENCE]               = 'CueReference';
1813              $EBMLidList[EBML_ID_CUEREFNUMBER]               = 'CueRefNumber';
1814              $EBMLidList[EBML_ID_CUEREFTIME]                 = 'CueRefTime';
1815              $EBMLidList[EBML_ID_CUES]                       = 'Cues';
1816              $EBMLidList[EBML_ID_CUETIME]                    = 'CueTime';
1817              $EBMLidList[EBML_ID_CUETRACK]                   = 'CueTrack';
1818              $EBMLidList[EBML_ID_CUETRACKPOSITIONS]          = 'CueTrackPositions';
1819              $EBMLidList[EBML_ID_DATEUTC]                    = 'DateUTC';
1820              $EBMLidList[EBML_ID_DEFAULTDURATION]            = 'DefaultDuration';
1821              $EBMLidList[EBML_ID_DISPLAYHEIGHT]              = 'DisplayHeight';
1822              $EBMLidList[EBML_ID_DISPLAYUNIT]                = 'DisplayUnit';
1823              $EBMLidList[EBML_ID_DISPLAYWIDTH]               = 'DisplayWidth';
1824              $EBMLidList[EBML_ID_DOCTYPE]                    = 'DocType';
1825              $EBMLidList[EBML_ID_DOCTYPEREADVERSION]         = 'DocTypeReadVersion';
1826              $EBMLidList[EBML_ID_DOCTYPEVERSION]             = 'DocTypeVersion';
1827              $EBMLidList[EBML_ID_DURATION]                   = 'Duration';
1828              $EBMLidList[EBML_ID_EBML]                       = 'EBML';
1829              $EBMLidList[EBML_ID_EBMLMAXIDLENGTH]            = 'EBMLMaxIDLength';
1830              $EBMLidList[EBML_ID_EBMLMAXSIZELENGTH]          = 'EBMLMaxSizeLength';
1831              $EBMLidList[EBML_ID_EBMLREADVERSION]            = 'EBMLReadVersion';
1832              $EBMLidList[EBML_ID_EBMLVERSION]                = 'EBMLVersion';
1833              $EBMLidList[EBML_ID_EDITIONENTRY]               = 'EditionEntry';
1834              $EBMLidList[EBML_ID_EDITIONFLAGDEFAULT]         = 'EditionFlagDefault';
1835              $EBMLidList[EBML_ID_EDITIONFLAGHIDDEN]          = 'EditionFlagHidden';
1836              $EBMLidList[EBML_ID_EDITIONFLAGORDERED]         = 'EditionFlagOrdered';
1837              $EBMLidList[EBML_ID_EDITIONUID]                 = 'EditionUID';
1838              $EBMLidList[EBML_ID_FILEDATA]                   = 'FileData';
1839              $EBMLidList[EBML_ID_FILEDESCRIPTION]            = 'FileDescription';
1840              $EBMLidList[EBML_ID_FILEMIMETYPE]               = 'FileMimeType';
1841              $EBMLidList[EBML_ID_FILENAME]                   = 'FileName';
1842              $EBMLidList[EBML_ID_FILEREFERRAL]               = 'FileReferral';
1843              $EBMLidList[EBML_ID_FILEUID]                    = 'FileUID';
1844              $EBMLidList[EBML_ID_FLAGDEFAULT]                = 'FlagDefault';
1845              $EBMLidList[EBML_ID_FLAGENABLED]                = 'FlagEnabled';
1846              $EBMLidList[EBML_ID_FLAGFORCED]                 = 'FlagForced';
1847              $EBMLidList[EBML_ID_FLAGINTERLACED]             = 'FlagInterlaced';
1848              $EBMLidList[EBML_ID_FLAGLACING]                 = 'FlagLacing';
1849              $EBMLidList[EBML_ID_GAMMAVALUE]                 = 'GammaValue';
1850              $EBMLidList[EBML_ID_INFO]                       = 'Info';
1851              $EBMLidList[EBML_ID_LANGUAGE]                   = 'Language';
1852              $EBMLidList[EBML_ID_MAXBLOCKADDITIONID]         = 'MaxBlockAdditionID';
1853              $EBMLidList[EBML_ID_MAXCACHE]                   = 'MaxCache';
1854              $EBMLidList[EBML_ID_MINCACHE]                   = 'MinCache';
1855              $EBMLidList[EBML_ID_MUXINGAPP]                  = 'MuxingApp';
1856              $EBMLidList[EBML_ID_NAME]                       = 'Name';
1857              $EBMLidList[EBML_ID_NEXTFILENAME]               = 'NextFilename';
1858              $EBMLidList[EBML_ID_NEXTUID]                    = 'NextUID';
1859              $EBMLidList[EBML_ID_OUTPUTSAMPLINGFREQUENCY]    = 'OutputSamplingFrequency';
1860              $EBMLidList[EBML_ID_PIXELCROPBOTTOM]            = 'PixelCropBottom';
1861              $EBMLidList[EBML_ID_PIXELCROPLEFT]              = 'PixelCropLeft';
1862              $EBMLidList[EBML_ID_PIXELCROPRIGHT]             = 'PixelCropRight';
1863              $EBMLidList[EBML_ID_PIXELCROPTOP]               = 'PixelCropTop';
1864              $EBMLidList[EBML_ID_PIXELHEIGHT]                = 'PixelHeight';
1865              $EBMLidList[EBML_ID_PIXELWIDTH]                 = 'PixelWidth';
1866              $EBMLidList[EBML_ID_PREVFILENAME]               = 'PrevFilename';
1867              $EBMLidList[EBML_ID_PREVUID]                    = 'PrevUID';
1868              $EBMLidList[EBML_ID_SAMPLINGFREQUENCY]          = 'SamplingFrequency';
1869              $EBMLidList[EBML_ID_SEEK]                       = 'Seek';
1870              $EBMLidList[EBML_ID_SEEKHEAD]                   = 'SeekHead';
1871              $EBMLidList[EBML_ID_SEEKID]                     = 'SeekID';
1872              $EBMLidList[EBML_ID_SEEKPOSITION]               = 'SeekPosition';
1873              $EBMLidList[EBML_ID_SEGMENT]                    = 'Segment';
1874              $EBMLidList[EBML_ID_SEGMENTFAMILY]              = 'SegmentFamily';
1875              $EBMLidList[EBML_ID_SEGMENTFILENAME]            = 'SegmentFilename';
1876              $EBMLidList[EBML_ID_SEGMENTUID]                 = 'SegmentUID';
1877              $EBMLidList[EBML_ID_SIMPLETAG]                  = 'SimpleTag';
1878              $EBMLidList[EBML_ID_CLUSTERSLICES]              = 'ClusterSlices';
1879              $EBMLidList[EBML_ID_STEREOMODE]                 = 'StereoMode';
1880              $EBMLidList[EBML_ID_OLDSTEREOMODE]              = 'OldStereoMode';
1881              $EBMLidList[EBML_ID_TAG]                        = 'Tag';
1882              $EBMLidList[EBML_ID_TAGATTACHMENTUID]           = 'TagAttachmentUID';
1883              $EBMLidList[EBML_ID_TAGBINARY]                  = 'TagBinary';
1884              $EBMLidList[EBML_ID_TAGCHAPTERUID]              = 'TagChapterUID';
1885              $EBMLidList[EBML_ID_TAGDEFAULT]                 = 'TagDefault';
1886              $EBMLidList[EBML_ID_TAGEDITIONUID]              = 'TagEditionUID';
1887              $EBMLidList[EBML_ID_TAGLANGUAGE]                = 'TagLanguage';
1888              $EBMLidList[EBML_ID_TAGNAME]                    = 'TagName';
1889              $EBMLidList[EBML_ID_TAGTRACKUID]                = 'TagTrackUID';
1890              $EBMLidList[EBML_ID_TAGS]                       = 'Tags';
1891              $EBMLidList[EBML_ID_TAGSTRING]                  = 'TagString';
1892              $EBMLidList[EBML_ID_TARGETS]                    = 'Targets';
1893              $EBMLidList[EBML_ID_TARGETTYPE]                 = 'TargetType';
1894              $EBMLidList[EBML_ID_TARGETTYPEVALUE]            = 'TargetTypeValue';
1895              $EBMLidList[EBML_ID_TIMECODESCALE]              = 'TimecodeScale';
1896              $EBMLidList[EBML_ID_TITLE]                      = 'Title';
1897              $EBMLidList[EBML_ID_TRACKENTRY]                 = 'TrackEntry';
1898              $EBMLidList[EBML_ID_TRACKNUMBER]                = 'TrackNumber';
1899              $EBMLidList[EBML_ID_TRACKOFFSET]                = 'TrackOffset';
1900              $EBMLidList[EBML_ID_TRACKOVERLAY]               = 'TrackOverlay';
1901              $EBMLidList[EBML_ID_TRACKS]                     = 'Tracks';
1902              $EBMLidList[EBML_ID_TRACKTIMECODESCALE]         = 'TrackTimecodeScale';
1903              $EBMLidList[EBML_ID_TRACKTRANSLATE]             = 'TrackTranslate';
1904              $EBMLidList[EBML_ID_TRACKTRANSLATECODEC]        = 'TrackTranslateCodec';
1905              $EBMLidList[EBML_ID_TRACKTRANSLATEEDITIONUID]   = 'TrackTranslateEditionUID';
1906              $EBMLidList[EBML_ID_TRACKTRANSLATETRACKID]      = 'TrackTranslateTrackID';
1907              $EBMLidList[EBML_ID_TRACKTYPE]                  = 'TrackType';
1908              $EBMLidList[EBML_ID_TRACKUID]                   = 'TrackUID';
1909              $EBMLidList[EBML_ID_VIDEO]                      = 'Video';
1910              $EBMLidList[EBML_ID_VOID]                       = 'Void';
1911              $EBMLidList[EBML_ID_WRITINGAPP]                 = 'WritingApp';
1912          }
1913  
1914          return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
1915      }
1916  
1917      /**
1918       * @param int $value
1919       *
1920       * @return string
1921       */
1922  	public static function displayUnit($value) {
1923          // http://www.matroska.org/technical/specs/index.html#DisplayUnit
1924          static $units = array(
1925              0 => 'pixels',
1926              1 => 'centimeters',
1927              2 => 'inches',
1928              3 => 'Display Aspect Ratio');
1929  
1930          return (isset($units[$value]) ? $units[$value] : 'unknown');
1931      }
1932  
1933      /**
1934       * @param array $streams
1935       *
1936       * @return array
1937       */
1938  	private static function getDefaultStreamInfo($streams)
1939      {
1940          $stream = array();
1941          foreach (array_reverse($streams) as $stream) {
1942              if ($stream['default']) {
1943                  break;
1944              }
1945          }
1946  
1947          $unset = array('default', 'name');
1948          foreach ($unset as $u) {
1949              if (isset($stream[$u])) {
1950                  unset($stream[$u]);
1951              }
1952          }
1953  
1954          $info = $stream;
1955          $info['streams'] = $streams;
1956  
1957          return $info;
1958      }
1959  
1960      /**
1961       * @param array $info
1962       *
1963       * @return float|bool Duration when present in metadata or false
1964       */
1965  	private function playtimeFromMetadata(&$info) {
1966          if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
1967              foreach ($info['matroska']['info'] as $infoarray) {
1968                  if (isset($infoarray['Duration'])) {
1969                      // TimecodeScale is how many nanoseconds each Duration unit is
1970                      $info['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : MATROSKA_DEFAULT_TIMECODESCALE) / 1000000000);
1971                      return $info['playtime_seconds'];
1972                  }
1973              }
1974          }
1975          return false;
1976      }
1977  
1978      /**
1979       * @param int $offset New starting offset for the buffer
1980       *
1981       * @return void
1982       */
1983  	private function resetParserBuffer($offset) {
1984          $this->current_offset = $offset;
1985          $this->EBMLbuffer = '';
1986          $this->EBMLbuffer_offset = 0;
1987          $this->EBMLbuffer_length = 0;
1988      }
1989  
1990      /**
1991       * Scan start and end of file for cluster information when Duration is missing
1992       * Only use this if no Duration was found in the Info element and we are not in parse_whole_file mode
1993       *
1994       * @param array $info
1995       *
1996       * @return void
1997       */
1998  	private function scanStartEndForClusters(&$info) {
1999          // Scan beginning of file for first cluster
2000          $this->resetParserBuffer($info['avdataoffset']);
2001          $this->scan_mode = MATROSKA_SCAN_FIRST_CLUSTER;
2002  
2003          try {
2004              $this->parseEBML($info);
2005          } catch (Exception $e) {
2006              $this->error('EBML parser (start of file): '.$e->getMessage());
2007          }
2008  
2009          // Scan end of file for last cluster
2010          if (is_array($info['matroska']['cluster']) && !empty($info['matroska']['cluster'])) {
2011              // Scan maximum 1MB window before EOF
2012              $this->resetParserBuffer(max(0, $info['avdataend'] - (1024 * 1024)));
2013              $this->scan_mode = MATROSKA_SCAN_LAST_CLUSTER;
2014  
2015              try {
2016                  $this->parseEBML($info);
2017              } catch (Exception $e) {
2018                  $this->error('EBML parser (end of file): '.$e->getMessage());
2019              }
2020          }
2021  
2022          // Reset to header parsing mode (this method is only called during header-only parsing)
2023          $this->scan_mode = MATROSKA_SCAN_HEADER;
2024      }
2025  
2026      /**
2027       * Fetch TimecodeScale from Info element
2028       *
2029       * @param array $info
2030       *
2031       * @return int TimecodeScale value
2032       */
2033  	private function getTimecodeScale(&$info) {
2034          $timecodeScale = MATROSKA_DEFAULT_TIMECODESCALE;
2035          if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
2036              foreach ($info['matroska']['info'] as $infoarray) {
2037                  if (isset($infoarray['TimecodeScale'])) {
2038                      $timecodeScale = $infoarray['TimecodeScale'];
2039                      break;
2040                  }
2041              }
2042          }
2043          return $timecodeScale;
2044      }
2045  
2046      /**
2047       * Calculate duration from scanned cluster timecodes
2048       *
2049       * @param array $info
2050       *
2051       * @return void
2052       */
2053  	private function calculatePlaytimeFromClusters(&$info) {
2054          $minTimecode = null;
2055          $maxTimecode = null;
2056          if (isset($info['matroska']['cluster']) && is_array($info['matroska']['cluster'])) {
2057              foreach ($info['matroska']['cluster'] as $cluster) {
2058                  if (isset($cluster['ClusterTimecode'])) {
2059                      if ($minTimecode === null || $cluster['ClusterTimecode'] < $minTimecode) {
2060                          $minTimecode = $cluster['ClusterTimecode'];
2061                      }
2062                      if ($maxTimecode === null || $cluster['ClusterTimecode'] > $maxTimecode) {
2063                          $maxTimecode = $cluster['ClusterTimecode'];
2064                      }
2065                  }
2066              }
2067          }
2068          if ($maxTimecode !== null && $minTimecode !== null && $maxTimecode > $minTimecode) {
2069              $info['playtime_seconds'] = ($maxTimecode - $minTimecode) * ($this->getTimecodeScale($info) / 1000000000);
2070          }
2071      }
2072  }


Generated : Mon Jun 15 08:20:09 2026 Cross-referenced by PHPXref