source: spip-zone/_plugins_/getID3/trunk/getid3/module.audio-video.matroska.php @ 92618

Last change on this file since 92618 was 92618, checked in by brunobergot@…, 4 years ago

version 1.3.8 : maj de la lib getID3 en version 1.9.10 + maj copyright

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