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