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