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