Completed
Pull Request — master (#2)
by Stephen
09:27
created

getid3_matroska   D

Complexity

Total Complexity 359

Size/Duplication

Total Lines 1536
Duplicated Lines 6.38 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 98
loc 1536
rs 4
c 0
b 0
f 0
wmc 359
lcom 1
cbo 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like getid3_matroska often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use getid3_matroska, and based on these observations, apply Extract Interface, too.

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