[SPIP] v3.2.1-->v3.2.3
[lhc/web/www.git] / www / plugins-dist / medias / lib / getid3 / module.audio-video.matroska.php
1 <?php
2
3 /////////////////////////////////////////////////////////////////
4 /// getID3() by James Heinrich <info@getid3.org> //
5 // available at https://github.com/JamesHeinrich/getID3 //
6 // or https://www.getid3.org //
7 // or http://getid3.sourceforge.net //
8 // see readme.txt for more details //
9 /////////////////////////////////////////////////////////////////
10 // //
11 // module.audio-video.matriska.php //
12 // module for analyzing Matroska containers //
13 // dependencies: NONE //
14 // ///
15 /////////////////////////////////////////////////////////////////
16
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 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 /**
219 * If true, do not return information about CLUSTER chunks, since there's a lot of them
220 * and they're not usually useful [default: TRUE].
221 *
222 * @var bool
223 */
224 public static $hide_clusters = true;
225
226 /**
227 * True to parse the whole file, not only header [default: FALSE].
228 *
229 * @var bool
230 */
231 public static $parse_whole_file = false;
232
233 /*
234 * Private parser settings/placeholders.
235 */
236 private $EBMLbuffer = '';
237 private $EBMLbuffer_offset = 0;
238 private $EBMLbuffer_length = 0;
239 private $current_offset = 0;
240 private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID);
241
242 /**
243 * @return bool
244 */
245 public function Analyze()
246 {
247 $info = &$this->getid3->info;
248
249 // parse container
250 try {
251 $this->parseEBML($info);
252 } catch (Exception $e) {
253 $this->error('EBML parser: '.$e->getMessage());
254 }
255
256 // calculate playtime
257 if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
258 foreach ($info['matroska']['info'] as $key => $infoarray) {
259 if (isset($infoarray['Duration'])) {
260 // TimecodeScale is how many nanoseconds each Duration unit is
261 $info['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000);
262 break;
263 }
264 }
265 }
266
267 // extract tags
268 if (isset($info['matroska']['tags']) && is_array($info['matroska']['tags'])) {
269 foreach ($info['matroska']['tags'] as $key => $infoarray) {
270 $this->ExtractCommentsSimpleTag($infoarray);
271 }
272 }
273
274 // process tracks
275 if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
276 foreach ($info['matroska']['tracks']['tracks'] as $key => $trackarray) {
277
278 $track_info = array();
279 $track_info['dataformat'] = self::CodecIDtoCommonName($trackarray['CodecID']);
280 $track_info['default'] = (isset($trackarray['FlagDefault']) ? $trackarray['FlagDefault'] : true);
281 if (isset($trackarray['Name'])) { $track_info['name'] = $trackarray['Name']; }
282
283 switch ($trackarray['TrackType']) {
284
285 case 1: // Video
286 $track_info['resolution_x'] = $trackarray['PixelWidth'];
287 $track_info['resolution_y'] = $trackarray['PixelHeight'];
288 $track_info['display_unit'] = self::displayUnit(isset($trackarray['DisplayUnit']) ? $trackarray['DisplayUnit'] : 0);
289 $track_info['display_x'] = (isset($trackarray['DisplayWidth']) ? $trackarray['DisplayWidth'] : $trackarray['PixelWidth']);
290 $track_info['display_y'] = (isset($trackarray['DisplayHeight']) ? $trackarray['DisplayHeight'] : $trackarray['PixelHeight']);
291
292 if (isset($trackarray['PixelCropBottom'])) { $track_info['crop_bottom'] = $trackarray['PixelCropBottom']; }
293 if (isset($trackarray['PixelCropTop'])) { $track_info['crop_top'] = $trackarray['PixelCropTop']; }
294 if (isset($trackarray['PixelCropLeft'])) { $track_info['crop_left'] = $trackarray['PixelCropLeft']; }
295 if (isset($trackarray['PixelCropRight'])) { $track_info['crop_right'] = $trackarray['PixelCropRight']; }
296 if (isset($trackarray['DefaultDuration'])) { $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3); }
297 if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; }
298
299 switch ($trackarray['CodecID']) {
300 case 'V_MS/VFW/FOURCC':
301 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
302
303 $parsed = getid3_riff::ParseBITMAPINFOHEADER($trackarray['CodecPrivate']);
304 $track_info['codec'] = getid3_riff::fourccLookup($parsed['fourcc']);
305 $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
306 break;
307
308 /*case 'V_MPEG4/ISO/AVC':
309 $h264['profile'] = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 1, 1));
310 $h264['level'] = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 3, 1));
311 $rn = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 4, 1));
312 $h264['NALUlength'] = ($rn & 3) + 1;
313 $rn = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 5, 1));
314 $nsps = ($rn & 31);
315 $offset = 6;
316 for ($i = 0; $i < $nsps; $i ++) {
317 $length = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2));
318 $h264['SPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length);
319 $offset += 2 + $length;
320 }
321 $npps = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 1));
322 $offset += 1;
323 for ($i = 0; $i < $npps; $i ++) {
324 $length = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2));
325 $h264['PPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length);
326 $offset += 2 + $length;
327 }
328 $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $h264;
329 break;*/
330 }
331
332 $info['video']['streams'][] = $track_info;
333 break;
334
335 case 2: // Audio
336 $track_info['sample_rate'] = (isset($trackarray['SamplingFrequency']) ? $trackarray['SamplingFrequency'] : 8000.0);
337 $track_info['channels'] = (isset($trackarray['Channels']) ? $trackarray['Channels'] : 1);
338 $track_info['language'] = (isset($trackarray['Language']) ? $trackarray['Language'] : 'eng');
339 if (isset($trackarray['BitDepth'])) { $track_info['bits_per_sample'] = $trackarray['BitDepth']; }
340 if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; }
341
342 switch ($trackarray['CodecID']) {
343 case 'A_PCM/INT/LIT':
344 case 'A_PCM/INT/BIG':
345 $track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth'];
346 break;
347
348 case 'A_AC3':
349 case 'A_EAC3':
350 case 'A_DTS':
351 case 'A_MPEG/L3':
352 case 'A_MPEG/L2':
353 case 'A_FLAC':
354 $module_dataformat = ($track_info['dataformat'] == 'mp2' ? 'mp3' : ($track_info['dataformat'] == 'eac3' ? 'ac3' : $track_info['dataformat']));
355 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.$module_dataformat.'.php', __FILE__, true);
356
357 if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) {
358 $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set');
359 break;
360 }
361
362 // create temp instance
363 $getid3_temp = new getID3();
364 if ($track_info['dataformat'] != 'flac') {
365 $getid3_temp->openfile($this->getid3->filename);
366 }
367 $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
368 if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') {
369 $getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length'];
370 }
371
372 // analyze
373 $class = 'getid3_'.$module_dataformat;
374 $header_data_key = $track_info['dataformat'][0] == 'm' ? 'mpeg' : $track_info['dataformat'];
375 $getid3_audio = new $class($getid3_temp, __CLASS__);
376 if ($track_info['dataformat'] == 'flac') {
377 $getid3_audio->AnalyzeString($trackarray['CodecPrivate']);
378 }
379 else {
380 $getid3_audio->Analyze();
381 }
382 if (!empty($getid3_temp->info[$header_data_key])) {
383 $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key];
384 if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
385 foreach ($getid3_temp->info['audio'] as $sub_key => $value) {
386 $track_info[$sub_key] = $value;
387 }
388 }
389 }
390 else {
391 $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because '.$class.'::Analyze() failed at offset '.$getid3_temp->info['avdataoffset']);
392 }
393
394 // copy errors and warnings
395 if (!empty($getid3_temp->info['error'])) {
396 foreach ($getid3_temp->info['error'] as $newerror) {
397 $this->warning($class.'() says: ['.$newerror.']');
398 }
399 }
400 if (!empty($getid3_temp->info['warning'])) {
401 foreach ($getid3_temp->info['warning'] as $newerror) {
402 $this->warning($class.'() says: ['.$newerror.']');
403 }
404 }
405 unset($getid3_temp, $getid3_audio);
406 break;
407
408 case 'A_AAC':
409 case 'A_AAC/MPEG2/LC':
410 case 'A_AAC/MPEG2/LC/SBR':
411 case 'A_AAC/MPEG4/LC':
412 case 'A_AAC/MPEG4/LC/SBR':
413 $this->warning($trackarray['CodecID'].' audio data contains no header, audio/video bitrates can\'t be calculated');
414 break;
415
416 case 'A_VORBIS':
417 if (!isset($trackarray['CodecPrivate'])) {
418 $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data not set');
419 break;
420 }
421 $vorbis_offset = strpos($trackarray['CodecPrivate'], 'vorbis', 1);
422 if ($vorbis_offset === false) {
423 $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data does not contain "vorbis" keyword');
424 break;
425 }
426 $vorbis_offset -= 1;
427
428 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
429
430 // create temp instance
431 $getid3_temp = new getID3();
432
433 // analyze
434 $getid3_ogg = new getid3_ogg($getid3_temp);
435 $oggpageinfo['page_seqno'] = 0;
436 $getid3_ogg->ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $oggpageinfo);
437 if (!empty($getid3_temp->info['ogg'])) {
438 $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg'];
439 if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
440 foreach ($getid3_temp->info['audio'] as $sub_key => $value) {
441 $track_info[$sub_key] = $value;
442 }
443 }
444 }
445
446 // copy errors and warnings
447 if (!empty($getid3_temp->info['error'])) {
448 foreach ($getid3_temp->info['error'] as $newerror) {
449 $this->warning('getid3_ogg() says: ['.$newerror.']');
450 }
451 }
452 if (!empty($getid3_temp->info['warning'])) {
453 foreach ($getid3_temp->info['warning'] as $newerror) {
454 $this->warning('getid3_ogg() says: ['.$newerror.']');
455 }
456 }
457
458 if (!empty($getid3_temp->info['ogg']['bitrate_nominal'])) {
459 $track_info['bitrate'] = $getid3_temp->info['ogg']['bitrate_nominal'];
460 }
461 unset($getid3_temp, $getid3_ogg, $oggpageinfo, $vorbis_offset);
462 break;
463
464 case 'A_MS/ACM':
465 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
466
467 $parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']);
468 foreach ($parsed as $sub_key => $value) {
469 if ($sub_key != 'raw') {
470 $track_info[$sub_key] = $value;
471 }
472 }
473 $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
474 break;
475
476 default:
477 $this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"');
478 break;
479 }
480
481 $info['audio']['streams'][] = $track_info;
482 break;
483 }
484 }
485
486 if (!empty($info['video']['streams'])) {
487 $info['video'] = self::getDefaultStreamInfo($info['video']['streams']);
488 }
489 if (!empty($info['audio']['streams'])) {
490 $info['audio'] = self::getDefaultStreamInfo($info['audio']['streams']);
491 }
492 }
493
494 // process attachments
495 if (isset($info['matroska']['attachments']) && $this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE) {
496 foreach ($info['matroska']['attachments'] as $i => $entry) {
497 if (strpos($entry['FileMimeType'], 'image/') === 0 && !empty($entry['FileData'])) {
498 $info['matroska']['comments']['picture'][] = array('data' => $entry['FileData'], 'image_mime' => $entry['FileMimeType'], 'filename' => $entry['FileName']);
499 }
500 }
501 }
502
503 // determine mime type
504 if (!empty($info['video']['streams'])) {
505 $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'video/webm' : 'video/x-matroska');
506 } elseif (!empty($info['audio']['streams'])) {
507 $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'audio/webm' : 'audio/x-matroska');
508 } elseif (isset($info['mime_type'])) {
509 unset($info['mime_type']);
510 }
511
512 return true;
513 }
514
515 /**
516 * @param array $info
517 */
518 private function parseEBML(&$info) {
519 // http://www.matroska.org/technical/specs/index.html#EBMLBasics
520 $this->current_offset = $info['avdataoffset'];
521
522 while ($this->getEBMLelement($top_element, $info['avdataend'])) {
523 switch ($top_element['id']) {
524
525 case EBML_ID_EBML:
526 $info['matroska']['header']['offset'] = $top_element['offset'];
527 $info['matroska']['header']['length'] = $top_element['length'];
528
529 while ($this->getEBMLelement($element_data, $top_element['end'], true)) {
530 switch ($element_data['id']) {
531
532 case EBML_ID_EBMLVERSION:
533 case EBML_ID_EBMLREADVERSION:
534 case EBML_ID_EBMLMAXIDLENGTH:
535 case EBML_ID_EBMLMAXSIZELENGTH:
536 case EBML_ID_DOCTYPEVERSION:
537 case EBML_ID_DOCTYPEREADVERSION:
538 $element_data['data'] = getid3_lib::BigEndian2Int($element_data['data']);
539 break;
540
541 case EBML_ID_DOCTYPE:
542 $element_data['data'] = getid3_lib::trimNullByte($element_data['data']);
543 $info['matroska']['doctype'] = $element_data['data'];
544 $info['fileformat'] = $element_data['data'];
545 break;
546
547 default:
548 $this->unhandledElement('header', __LINE__, $element_data);
549 break;
550 }
551
552 unset($element_data['offset'], $element_data['end']);
553 $info['matroska']['header']['elements'][] = $element_data;
554 }
555 break;
556
557 case EBML_ID_SEGMENT:
558 $info['matroska']['segment'][0]['offset'] = $top_element['offset'];
559 $info['matroska']['segment'][0]['length'] = $top_element['length'];
560
561 while ($this->getEBMLelement($element_data, $top_element['end'])) {
562 if ($element_data['id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required
563 $info['matroska']['segments'][] = $element_data;
564 }
565 switch ($element_data['id']) {
566
567 case EBML_ID_SEEKHEAD: // Contains the position of other level 1 elements.
568
569 while ($this->getEBMLelement($seek_entry, $element_data['end'])) {
570 switch ($seek_entry['id']) {
571
572 case EBML_ID_SEEK: // Contains a single seek entry to an EBML element
573 while ($this->getEBMLelement($sub_seek_entry, $seek_entry['end'], true)) {
574
575 switch ($sub_seek_entry['id']) {
576
577 case EBML_ID_SEEKID:
578 $seek_entry['target_id'] = self::EBML2Int($sub_seek_entry['data']);
579 $seek_entry['target_name'] = self::EBMLidName($seek_entry['target_id']);
580 break;
581
582 case EBML_ID_SEEKPOSITION:
583 $seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($sub_seek_entry['data']);
584 break;
585
586 default:
587 $this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry); }
588 break;
589 }
590 if (!isset($seek_entry['target_id'])) {
591 $this->warning('seek_entry[target_id] unexpectedly not set at '.$seek_entry['offset']);
592 break;
593 }
594 if (($seek_entry['target_id'] != EBML_ID_CLUSTER) || !self::$hide_clusters) { // collect clusters only if required
595 $info['matroska']['seek'][] = $seek_entry;
596 }
597 break;
598
599 default:
600 $this->unhandledElement('seekhead', __LINE__, $seek_entry);
601 break;
602 }
603 }
604 break;
605
606 case EBML_ID_TRACKS: // A top-level block of information with many tracks described.
607 $info['matroska']['tracks'] = $element_data;
608
609 while ($this->getEBMLelement($track_entry, $element_data['end'])) {
610 switch ($track_entry['id']) {
611
612 case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements.
613
614 while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) {
615 switch ($subelement['id']) {
616
617 case EBML_ID_TRACKNUMBER:
618 case EBML_ID_TRACKUID:
619 case EBML_ID_TRACKTYPE:
620 case EBML_ID_MINCACHE:
621 case EBML_ID_MAXCACHE:
622 case EBML_ID_MAXBLOCKADDITIONID:
623 case EBML_ID_DEFAULTDURATION: // nanoseconds per frame
624 $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
625 break;
626
627 case EBML_ID_TRACKTIMECODESCALE:
628 $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']);
629 break;
630
631 case EBML_ID_CODECID:
632 case EBML_ID_LANGUAGE:
633 case EBML_ID_NAME:
634 case EBML_ID_CODECNAME:
635 $track_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
636 break;
637
638 case EBML_ID_CODECPRIVATE:
639 $track_entry[$subelement['id_name']] = $this->readEBMLelementData($subelement['length'], true);
640 break;
641
642 case EBML_ID_FLAGENABLED:
643 case EBML_ID_FLAGDEFAULT:
644 case EBML_ID_FLAGFORCED:
645 case EBML_ID_FLAGLACING:
646 case EBML_ID_CODECDECODEALL:
647 $track_entry[$subelement['id_name']] = (bool) getid3_lib::BigEndian2Int($subelement['data']);
648 break;
649
650 case EBML_ID_VIDEO:
651
652 while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
653 switch ($sub_subelement['id']) {
654
655 case EBML_ID_PIXELWIDTH:
656 case EBML_ID_PIXELHEIGHT:
657 case EBML_ID_PIXELCROPBOTTOM:
658 case EBML_ID_PIXELCROPTOP:
659 case EBML_ID_PIXELCROPLEFT:
660 case EBML_ID_PIXELCROPRIGHT:
661 case EBML_ID_DISPLAYWIDTH:
662 case EBML_ID_DISPLAYHEIGHT:
663 case EBML_ID_DISPLAYUNIT:
664 case EBML_ID_ASPECTRATIOTYPE:
665 case EBML_ID_STEREOMODE:
666 case EBML_ID_OLDSTEREOMODE:
667 $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
668 break;
669
670 case EBML_ID_FLAGINTERLACED:
671 $track_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']);
672 break;
673
674 case EBML_ID_GAMMAVALUE:
675 $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']);
676 break;
677
678 case EBML_ID_COLOURSPACE:
679 $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
680 break;
681
682 default:
683 $this->unhandledElement('track.video', __LINE__, $sub_subelement);
684 break;
685 }
686 }
687 break;
688
689 case EBML_ID_AUDIO:
690
691 while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
692 switch ($sub_subelement['id']) {
693
694 case EBML_ID_CHANNELS:
695 case EBML_ID_BITDEPTH:
696 $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
697 break;
698
699 case EBML_ID_SAMPLINGFREQUENCY:
700 case EBML_ID_OUTPUTSAMPLINGFREQUENCY:
701 $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']);
702 break;
703
704 case EBML_ID_CHANNELPOSITIONS:
705 $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
706 break;
707
708 default:
709 $this->unhandledElement('track.audio', __LINE__, $sub_subelement);
710 break;
711 }
712 }
713 break;
714
715 case EBML_ID_CONTENTENCODINGS:
716
717 while ($this->getEBMLelement($sub_subelement, $subelement['end'])) {
718 switch ($sub_subelement['id']) {
719
720 case EBML_ID_CONTENTENCODING:
721
722 while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CONTENTCOMPRESSION, EBML_ID_CONTENTENCRYPTION))) {
723 switch ($sub_sub_subelement['id']) {
724
725 case EBML_ID_CONTENTENCODINGORDER:
726 case EBML_ID_CONTENTENCODINGSCOPE:
727 case EBML_ID_CONTENTENCODINGTYPE:
728 $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
729 break;
730
731 case EBML_ID_CONTENTCOMPRESSION:
732
733 while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
734 switch ($sub_sub_sub_subelement['id']) {
735
736 case EBML_ID_CONTENTCOMPALGO:
737 $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']);
738 break;
739
740 case EBML_ID_CONTENTCOMPSETTINGS:
741 $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
742 break;
743
744 default:
745 $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
746 break;
747 }
748 }
749 break;
750
751 case EBML_ID_CONTENTENCRYPTION:
752
753 while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
754 switch ($sub_sub_sub_subelement['id']) {
755
756 case EBML_ID_CONTENTENCALGO:
757 case EBML_ID_CONTENTSIGALGO:
758 case EBML_ID_CONTENTSIGHASHALGO:
759 $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']);
760 break;
761
762 case EBML_ID_CONTENTENCKEYID:
763 case EBML_ID_CONTENTSIGNATURE:
764 case EBML_ID_CONTENTSIGKEYID:
765 $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
766 break;
767
768 default:
769 $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
770 break;
771 }
772 }
773 break;
774
775 default:
776 $this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement);
777 break;
778 }
779 }
780 break;
781
782 default:
783 $this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement);
784 break;
785 }
786 }
787 break;
788
789 default:
790 $this->unhandledElement('track', __LINE__, $subelement);
791 break;
792 }
793 }
794
795 $info['matroska']['tracks']['tracks'][] = $track_entry;
796 break;
797
798 default:
799 $this->unhandledElement('tracks', __LINE__, $track_entry);
800 break;
801 }
802 }
803 break;
804
805 case EBML_ID_INFO: // Contains miscellaneous general information and statistics on the file.
806 $info_entry = array();
807
808 while ($this->getEBMLelement($subelement, $element_data['end'], true)) {
809 switch ($subelement['id']) {
810
811 case EBML_ID_TIMECODESCALE:
812 $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
813 break;
814
815 case EBML_ID_DURATION:
816 $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']);
817 break;
818
819 case EBML_ID_DATEUTC:
820 $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
821 $info_entry[$subelement['id_name'].'_unix'] = self::EBMLdate2unix($info_entry[$subelement['id_name']]);
822 break;
823
824 case EBML_ID_SEGMENTUID:
825 case EBML_ID_PREVUID:
826 case EBML_ID_NEXTUID:
827 $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
828 break;
829
830 case EBML_ID_SEGMENTFAMILY:
831 $info_entry[$subelement['id_name']][] = getid3_lib::trimNullByte($subelement['data']);
832 break;
833
834 case EBML_ID_SEGMENTFILENAME:
835 case EBML_ID_PREVFILENAME:
836 case EBML_ID_NEXTFILENAME:
837 case EBML_ID_TITLE:
838 case EBML_ID_MUXINGAPP:
839 case EBML_ID_WRITINGAPP:
840 $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
841 $info['matroska']['comments'][strtolower($subelement['id_name'])][] = $info_entry[$subelement['id_name']];
842 break;
843
844 case EBML_ID_CHAPTERTRANSLATE:
845 $chaptertranslate_entry = array();
846
847 while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
848 switch ($sub_subelement['id']) {
849
850 case EBML_ID_CHAPTERTRANSLATEEDITIONUID:
851 $chaptertranslate_entry[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data']);
852 break;
853
854 case EBML_ID_CHAPTERTRANSLATECODEC:
855 $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
856 break;
857
858 case EBML_ID_CHAPTERTRANSLATEID:
859 $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
860 break;
861
862 default:
863 $this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement);
864 break;
865 }
866 }
867 $info_entry[$subelement['id_name']] = $chaptertranslate_entry;
868 break;
869
870 default:
871 $this->unhandledElement('info', __LINE__, $subelement);
872 break;
873 }
874 }
875 $info['matroska']['info'][] = $info_entry;
876 break;
877
878 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.
879 if (self::$hide_clusters) { // do not parse cues if hide clusters is "ON" till they point to clusters anyway
880 $this->current_offset = $element_data['end'];
881 break;
882 }
883 $cues_entry = array();
884
885 while ($this->getEBMLelement($subelement, $element_data['end'])) {
886 switch ($subelement['id']) {
887
888 case EBML_ID_CUEPOINT:
889 $cuepoint_entry = array();
890
891 while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CUETRACKPOSITIONS))) {
892 switch ($sub_subelement['id']) {
893
894 case EBML_ID_CUETRACKPOSITIONS:
895 $cuetrackpositions_entry = array();
896
897 while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
898 switch ($sub_sub_subelement['id']) {
899
900 case EBML_ID_CUETRACK:
901 case EBML_ID_CUECLUSTERPOSITION:
902 case EBML_ID_CUEBLOCKNUMBER:
903 case EBML_ID_CUECODECSTATE:
904 $cuetrackpositions_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
905 break;
906
907 default:
908 $this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement);
909 break;
910 }
911 }
912 $cuepoint_entry[$sub_subelement['id_name']][] = $cuetrackpositions_entry;
913 break;
914
915 case EBML_ID_CUETIME:
916 $cuepoint_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
917 break;
918
919 default:
920 $this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement);
921 break;
922 }
923 }
924 $cues_entry[] = $cuepoint_entry;
925 break;
926
927 default:
928 $this->unhandledElement('cues', __LINE__, $subelement);
929 break;
930 }
931 }
932 $info['matroska']['cues'] = $cues_entry;
933 break;
934
935 case EBML_ID_TAGS: // Element containing elements specific to Tracks/Chapters.
936 $tags_entry = array();
937
938 while ($this->getEBMLelement($subelement, $element_data['end'], false)) {
939 switch ($subelement['id']) {
940
941 case EBML_ID_TAG:
942 $tag_entry = array();
943
944 while ($this->getEBMLelement($sub_subelement, $subelement['end'], false)) {
945 switch ($sub_subelement['id']) {
946
947 case EBML_ID_TARGETS:
948 $targets_entry = array();
949
950 while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
951 switch ($sub_sub_subelement['id']) {
952
953 case EBML_ID_TARGETTYPEVALUE:
954 $targets_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
955 $targets_entry[strtolower($sub_sub_subelement['id_name']).'_long'] = self::TargetTypeValue($targets_entry[$sub_sub_subelement['id_name']]);
956 break;
957
958 case EBML_ID_TARGETTYPE:
959 $targets_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
960 break;
961
962 case EBML_ID_TAGTRACKUID:
963 case EBML_ID_TAGEDITIONUID:
964 case EBML_ID_TAGCHAPTERUID:
965 case EBML_ID_TAGATTACHMENTUID:
966 $targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
967 break;
968
969 default:
970 $this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement);
971 break;
972 }
973 }
974 $tag_entry[$sub_subelement['id_name']] = $targets_entry;
975 break;
976
977 case EBML_ID_SIMPLETAG:
978 $tag_entry[$sub_subelement['id_name']][] = $this->HandleEMBLSimpleTag($sub_subelement['end']);
979 break;
980
981 default:
982 $this->unhandledElement('tags.tag', __LINE__, $sub_subelement);
983 break;
984 }
985 }
986 $tags_entry[] = $tag_entry;
987 break;
988
989 default:
990 $this->unhandledElement('tags', __LINE__, $subelement);
991 break;
992 }
993 }
994 $info['matroska']['tags'] = $tags_entry;
995 break;
996
997 case EBML_ID_ATTACHMENTS: // Contain attached files.
998
999 while ($this->getEBMLelement($subelement, $element_data['end'])) {
1000 switch ($subelement['id']) {
1001
1002 case EBML_ID_ATTACHEDFILE:
1003 $attachedfile_entry = array();
1004
1005 while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_FILEDATA))) {
1006 switch ($sub_subelement['id']) {
1007
1008 case EBML_ID_FILEDESCRIPTION:
1009 case EBML_ID_FILENAME:
1010 case EBML_ID_FILEMIMETYPE:
1011 $attachedfile_entry[$sub_subelement['id_name']] = $sub_subelement['data'];
1012 break;
1013
1014 case EBML_ID_FILEDATA:
1015 $attachedfile_entry['data_offset'] = $this->current_offset;
1016 $attachedfile_entry['data_length'] = $sub_subelement['length'];
1017
1018 $attachedfile_entry[$sub_subelement['id_name']] = $this->saveAttachment(
1019 $attachedfile_entry['FileName'],
1020 $attachedfile_entry['data_offset'],
1021 $attachedfile_entry['data_length']);
1022
1023 $this->current_offset = $sub_subelement['end'];
1024 break;
1025
1026 case EBML_ID_FILEUID:
1027 $attachedfile_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1028 break;
1029
1030 default:
1031 $this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement);
1032 break;
1033 }
1034 }
1035 $info['matroska']['attachments'][] = $attachedfile_entry;
1036 break;
1037
1038 default:
1039 $this->unhandledElement('attachments', __LINE__, $subelement);
1040 break;
1041 }
1042 }
1043 break;
1044
1045 case EBML_ID_CHAPTERS:
1046
1047 while ($this->getEBMLelement($subelement, $element_data['end'])) {
1048 switch ($subelement['id']) {
1049
1050 case EBML_ID_EDITIONENTRY:
1051 $editionentry_entry = array();
1052
1053 while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CHAPTERATOM))) {
1054 switch ($sub_subelement['id']) {
1055
1056 case EBML_ID_EDITIONUID:
1057 $editionentry_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1058 break;
1059
1060 case EBML_ID_EDITIONFLAGHIDDEN:
1061 case EBML_ID_EDITIONFLAGDEFAULT:
1062 case EBML_ID_EDITIONFLAGORDERED:
1063 $editionentry_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']);
1064 break;
1065
1066 case EBML_ID_CHAPTERATOM:
1067 $chapteratom_entry = array();
1068
1069 while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CHAPTERTRACK, EBML_ID_CHAPTERDISPLAY))) {
1070 switch ($sub_sub_subelement['id']) {
1071
1072 case EBML_ID_CHAPTERSEGMENTUID:
1073 case EBML_ID_CHAPTERSEGMENTEDITIONUID:
1074 $chapteratom_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
1075 break;
1076
1077 case EBML_ID_CHAPTERFLAGENABLED:
1078 case EBML_ID_CHAPTERFLAGHIDDEN:
1079 $chapteratom_entry[$sub_sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
1080 break;
1081
1082 case EBML_ID_CHAPTERUID:
1083 case EBML_ID_CHAPTERTIMESTART:
1084 case EBML_ID_CHAPTERTIMEEND:
1085 $chapteratom_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
1086 break;
1087
1088 case EBML_ID_CHAPTERTRACK:
1089 $chaptertrack_entry = array();
1090
1091 while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
1092 switch ($sub_sub_sub_subelement['id']) {
1093
1094 case EBML_ID_CHAPTERTRACKNUMBER:
1095 $chaptertrack_entry[$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
1096 break;
1097
1098 default:
1099 $this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement);
1100 break;
1101 }
1102 }
1103 $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chaptertrack_entry;
1104 break;
1105
1106 case EBML_ID_CHAPTERDISPLAY:
1107 $chapterdisplay_entry = array();
1108
1109 while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
1110 switch ($sub_sub_sub_subelement['id']) {
1111
1112 case EBML_ID_CHAPSTRING:
1113 case EBML_ID_CHAPLANGUAGE:
1114 case EBML_ID_CHAPCOUNTRY:
1115 $chapterdisplay_entry[$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
1116 break;
1117
1118 default:
1119 $this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement);
1120 break;
1121 }
1122 }
1123 $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chapterdisplay_entry;
1124 break;
1125
1126 default:
1127 $this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement);
1128 break;
1129 }
1130 }
1131 $editionentry_entry[$sub_subelement['id_name']][] = $chapteratom_entry;
1132 break;
1133
1134 default:
1135 $this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement);
1136 break;
1137 }
1138 }
1139 $info['matroska']['chapters'][] = $editionentry_entry;
1140 break;
1141
1142 default:
1143 $this->unhandledElement('chapters', __LINE__, $subelement);
1144 break;
1145 }
1146 }
1147 break;
1148
1149 case EBML_ID_CLUSTER: // The lower level element containing the (monolithic) Block structure.
1150 $cluster_entry = array();
1151
1152 while ($this->getEBMLelement($subelement, $element_data['end'], array(EBML_ID_CLUSTERSILENTTRACKS, EBML_ID_CLUSTERBLOCKGROUP, EBML_ID_CLUSTERSIMPLEBLOCK))) {
1153 switch ($subelement['id']) {
1154
1155 case EBML_ID_CLUSTERTIMECODE:
1156 case EBML_ID_CLUSTERPOSITION:
1157 case EBML_ID_CLUSTERPREVSIZE:
1158 $cluster_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
1159 break;
1160
1161 case EBML_ID_CLUSTERSILENTTRACKS:
1162 $cluster_silent_tracks = array();
1163
1164 while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
1165 switch ($sub_subelement['id']) {
1166
1167 case EBML_ID_CLUSTERSILENTTRACKNUMBER:
1168 $cluster_silent_tracks[] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1169 break;
1170
1171 default:
1172 $this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement);
1173 break;
1174 }
1175 }
1176 $cluster_entry[$subelement['id_name']][] = $cluster_silent_tracks;
1177 break;
1178
1179 case EBML_ID_CLUSTERBLOCKGROUP:
1180 $cluster_block_group = array('offset' => $this->current_offset);
1181
1182 while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CLUSTERBLOCK))) {
1183 switch ($sub_subelement['id']) {
1184
1185 case EBML_ID_CLUSTERBLOCK:
1186 $cluster_block_group[$sub_subelement['id_name']] = $this->HandleEMBLClusterBlock($sub_subelement, EBML_ID_CLUSTERBLOCK, $info);
1187 break;
1188
1189 case EBML_ID_CLUSTERREFERENCEPRIORITY: // unsigned-int
1190 case EBML_ID_CLUSTERBLOCKDURATION: // unsigned-int
1191 $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1192 break;
1193
1194 case EBML_ID_CLUSTERREFERENCEBLOCK: // signed-int
1195 $cluster_block_group[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data'], false, true);
1196 break;
1197
1198 case EBML_ID_CLUSTERCODECSTATE:
1199 $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
1200 break;
1201
1202 default:
1203 $this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement);
1204 break;
1205 }
1206 }
1207 $cluster_entry[$subelement['id_name']][] = $cluster_block_group;
1208 break;
1209
1210 case EBML_ID_CLUSTERSIMPLEBLOCK:
1211 $cluster_entry[$subelement['id_name']][] = $this->HandleEMBLClusterBlock($subelement, EBML_ID_CLUSTERSIMPLEBLOCK, $info);
1212 break;
1213
1214 default:
1215 $this->unhandledElement('cluster', __LINE__, $subelement);
1216 break;
1217 }
1218 $this->current_offset = $subelement['end'];
1219 }
1220 if (!self::$hide_clusters) {
1221 $info['matroska']['cluster'][] = $cluster_entry;
1222 }
1223
1224 // check to see if all the data we need exists already, if so, break out of the loop
1225 if (!self::$parse_whole_file) {
1226 if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
1227 if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
1228 if (count($info['matroska']['track_data_offsets']) == count($info['matroska']['tracks']['tracks'])) {
1229 return;
1230 }
1231 }
1232 }
1233 }
1234 break;
1235
1236 default:
1237 $this->unhandledElement('segment', __LINE__, $element_data);
1238 break;
1239 }
1240 }
1241 break;
1242
1243 default:
1244 $this->unhandledElement('root', __LINE__, $top_element);
1245 break;
1246 }
1247 }
1248 }
1249
1250 /**
1251 * @param int $min_data
1252 *
1253 * @return bool
1254 */
1255 private function EnsureBufferHasEnoughData($min_data=1024) {
1256 if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) {
1257 $read_bytes = max($min_data, $this->getid3->fread_buffer_size());
1258
1259 try {
1260 $this->fseek($this->current_offset);
1261 $this->EBMLbuffer_offset = $this->current_offset;
1262 $this->EBMLbuffer = $this->fread($read_bytes);
1263 $this->EBMLbuffer_length = strlen($this->EBMLbuffer);
1264 } catch (getid3_exception $e) {
1265 $this->warning('EBML parser: '.$e->getMessage());
1266 return false;
1267 }
1268
1269 if ($this->EBMLbuffer_length == 0 && $this->feof()) {
1270 return $this->error('EBML parser: ran out of file at offset '.$this->current_offset);
1271 }
1272 }
1273 return true;
1274 }
1275
1276 /**
1277 * @return int|float|false
1278 */
1279 private function readEBMLint() {
1280 $actual_offset = $this->current_offset - $this->EBMLbuffer_offset;
1281
1282 // get length of integer
1283 $first_byte_int = ord($this->EBMLbuffer[$actual_offset]);
1284 if (0x80 & $first_byte_int) {
1285 $length = 1;
1286 } elseif (0x40 & $first_byte_int) {
1287 $length = 2;
1288 } elseif (0x20 & $first_byte_int) {
1289 $length = 3;
1290 } elseif (0x10 & $first_byte_int) {
1291 $length = 4;
1292 } elseif (0x08 & $first_byte_int) {
1293 $length = 5;
1294 } elseif (0x04 & $first_byte_int) {
1295 $length = 6;
1296 } elseif (0x02 & $first_byte_int) {
1297 $length = 7;
1298 } elseif (0x01 & $first_byte_int) {
1299 $length = 8;
1300 } else {
1301 throw new Exception('invalid EBML integer (leading 0x00) at '.$this->current_offset);
1302 }
1303
1304 // read
1305 $int_value = self::EBML2Int(substr($this->EBMLbuffer, $actual_offset, $length));
1306 $this->current_offset += $length;
1307
1308 return $int_value;
1309 }
1310
1311 /**
1312 * @param int $length
1313 * @param bool $check_buffer
1314 *
1315 * @return string|false
1316 */
1317 private function readEBMLelementData($length, $check_buffer=false) {
1318 if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) {
1319 return false;
1320 }
1321 $data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length);
1322 $this->current_offset += $length;
1323 return $data;
1324 }
1325
1326 /**
1327 * @param array $element
1328 * @param int $parent_end
1329 * @param array|bool $get_data
1330 *
1331 * @return bool
1332 */
1333 private function getEBMLelement(&$element, $parent_end, $get_data=false) {
1334 if ($this->current_offset >= $parent_end) {
1335 return false;
1336 }
1337
1338 if (!$this->EnsureBufferHasEnoughData()) {
1339 $this->current_offset = PHP_INT_MAX; // do not exit parser right now, allow to finish current loop to gather maximum information
1340 return false;
1341 }
1342
1343 $element = array();
1344
1345 // set offset
1346 $element['offset'] = $this->current_offset;
1347
1348 // get ID
1349 $element['id'] = $this->readEBMLint();
1350
1351 // get name
1352 $element['id_name'] = self::EBMLidName($element['id']);
1353
1354 // get length
1355 $element['length'] = $this->readEBMLint();
1356
1357 // get end offset
1358 $element['end'] = $this->current_offset + $element['length'];
1359
1360 // get raw data
1361 $dont_parse = (in_array($element['id'], $this->unuseful_elements) || $element['id_name'] == dechex($element['id']));
1362 if (($get_data === true || (is_array($get_data) && !in_array($element['id'], $get_data))) && !$dont_parse) {
1363 $element['data'] = $this->readEBMLelementData($element['length'], $element);
1364 }
1365
1366 return true;
1367 }
1368
1369 /**
1370 * @param string $type
1371 * @param int $line
1372 * @param array $element
1373 */
1374 private function unhandledElement($type, $line, $element) {
1375 // warn only about unknown and missed elements, not about unuseful
1376 if (!in_array($element['id'], $this->unuseful_elements)) {
1377 $this->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']);
1378 }
1379
1380 // increase offset for unparsed elements
1381 if (!isset($element['data'])) {
1382 $this->current_offset = $element['end'];
1383 }
1384 }
1385
1386 /**
1387 * @param array $SimpleTagArray
1388 *
1389 * @return bool
1390 */
1391 private function ExtractCommentsSimpleTag($SimpleTagArray) {
1392 if (!empty($SimpleTagArray['SimpleTag'])) {
1393 foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) {
1394 if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) {
1395 $this->getid3->info['matroska']['comments'][strtolower($SimpleTagData['TagName'])][] = $SimpleTagData['TagString'];
1396 }
1397 if (!empty($SimpleTagData['SimpleTag'])) {
1398 $this->ExtractCommentsSimpleTag($SimpleTagData);
1399 }
1400 }
1401 }
1402
1403 return true;
1404 }
1405
1406 /**
1407 * @param int $parent_end
1408 *
1409 * @return array
1410 */
1411 private function HandleEMBLSimpleTag($parent_end) {
1412 $simpletag_entry = array();
1413
1414 while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) {
1415 switch ($element['id']) {
1416
1417 case EBML_ID_TAGNAME:
1418 case EBML_ID_TAGLANGUAGE:
1419 case EBML_ID_TAGSTRING:
1420 case EBML_ID_TAGBINARY:
1421 $simpletag_entry[$element['id_name']] = $element['data'];
1422 break;
1423
1424 case EBML_ID_SIMPLETAG:
1425 $simpletag_entry[$element['id_name']][] = $this->HandleEMBLSimpleTag($element['end']);
1426 break;
1427
1428 case EBML_ID_TAGDEFAULT:
1429 $simpletag_entry[$element['id_name']] = (bool)getid3_lib::BigEndian2Int($element['data']);
1430 break;
1431
1432 default:
1433 $this->unhandledElement('tag.simpletag', __LINE__, $element);
1434 break;
1435 }
1436 }
1437
1438 return $simpletag_entry;
1439 }
1440
1441 /**
1442 * @param array $element
1443 * @param int $block_type
1444 * @param array $info
1445 *
1446 * @return array
1447 */
1448 private function HandleEMBLClusterBlock($element, $block_type, &$info) {
1449 // http://www.matroska.org/technical/specs/index.html#block_structure
1450 // http://www.matroska.org/technical/specs/index.html#simpleblock_structure
1451
1452 $block_data = array();
1453 $block_data['tracknumber'] = $this->readEBMLint();
1454 $block_data['timecode'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(2), false, true);
1455 $block_data['flags_raw'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
1456
1457 if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
1458 $block_data['flags']['keyframe'] = (($block_data['flags_raw'] & 0x80) >> 7);
1459 //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0x70) >> 4);
1460 }
1461 else {
1462 //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0xF0) >> 4);
1463 }
1464 $block_data['flags']['invisible'] = (bool)(($block_data['flags_raw'] & 0x08) >> 3);
1465 $block_data['flags']['lacing'] = (($block_data['flags_raw'] & 0x06) >> 1); // 00=no lacing; 01=Xiph lacing; 11=EBML lacing; 10=fixed-size lacing
1466 if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
1467 $block_data['flags']['discardable'] = (($block_data['flags_raw'] & 0x01));
1468 }
1469 else {
1470 //$block_data['flags']['reserved2'] = (($block_data['flags_raw'] & 0x01) >> 0);
1471 }
1472 $block_data['flags']['lacing_type'] = self::BlockLacingType($block_data['flags']['lacing']);
1473
1474 // Lace (when lacing bit is set)
1475 if ($block_data['flags']['lacing'] > 0) {
1476 $block_data['lace_frames'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)) + 1; // Number of frames in the lace-1 (uint8)
1477 if ($block_data['flags']['lacing'] != 0x02) {
1478 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).
1479 if ($block_data['flags']['lacing'] == 0x03) { // EBML lacing
1480 $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.
1481 }
1482 else { // Xiph lacing
1483 $block_data['lace_frames_size'][$i] = 0;
1484 do {
1485 $size = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
1486 $block_data['lace_frames_size'][$i] += $size;
1487 }
1488 while ($size == 255);
1489 }
1490 }
1491 if ($block_data['flags']['lacing'] == 0x01) { // calc size of the last frame only for Xiph lacing, till EBML sizes are now anyway determined incorrectly
1492 $block_data['lace_frames_size'][] = $element['end'] - $this->current_offset - array_sum($block_data['lace_frames_size']);
1493 }
1494 }
1495 }
1496
1497 if (!isset($info['matroska']['track_data_offsets'][$block_data['tracknumber']])) {
1498 $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['offset'] = $this->current_offset;
1499 $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'] = $element['end'] - $this->current_offset;
1500 //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] = 0;
1501 }
1502 //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] += $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'];
1503 //$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);
1504
1505 // set offset manually
1506 $this->current_offset = $element['end'];
1507
1508 return $block_data;
1509 }
1510
1511 /**
1512 * @param string $EBMLstring
1513 *
1514 * @return int|float|false
1515 */
1516 private static function EBML2Int($EBMLstring) {
1517 // http://matroska.org/specs/
1518
1519 // Element ID coded with an UTF-8 like system:
1520 // 1xxx xxxx - Class A IDs (2^7 -2 possible values) (base 0x8X)
1521 // 01xx xxxx xxxx xxxx - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX)
1522 // 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX)
1523 // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX)
1524 // Values with all x at 0 and 1 are reserved (hence the -2).
1525
1526 // Data size, in octets, is also coded with an UTF-8 like system :
1527 // 1xxx xxxx - value 0 to 2^7-2
1528 // 01xx xxxx xxxx xxxx - value 0 to 2^14-2
1529 // 001x xxxx xxxx xxxx xxxx xxxx - value 0 to 2^21-2
1530 // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^28-2
1531 // 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^35-2
1532 // 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^42-2
1533 // 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^49-2
1534 // 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^56-2
1535
1536 $first_byte_int = ord($EBMLstring[0]);
1537 if (0x80 & $first_byte_int) {
1538 $EBMLstring[0] = chr($first_byte_int & 0x7F);
1539 } elseif (0x40 & $first_byte_int) {
1540 $EBMLstring[0] = chr($first_byte_int & 0x3F);
1541 } elseif (0x20 & $first_byte_int) {
1542 $EBMLstring[0] = chr($first_byte_int & 0x1F);
1543 } elseif (0x10 & $first_byte_int) {
1544 $EBMLstring[0] = chr($first_byte_int & 0x0F);
1545 } elseif (0x08 & $first_byte_int) {
1546 $EBMLstring[0] = chr($first_byte_int & 0x07);
1547 } elseif (0x04 & $first_byte_int) {
1548 $EBMLstring[0] = chr($first_byte_int & 0x03);
1549 } elseif (0x02 & $first_byte_int) {
1550 $EBMLstring[0] = chr($first_byte_int & 0x01);
1551 } elseif (0x01 & $first_byte_int) {
1552 $EBMLstring[0] = chr($first_byte_int & 0x00);
1553 }
1554
1555 return getid3_lib::BigEndian2Int($EBMLstring);
1556 }
1557
1558 /**
1559 * @param int $EBMLdatestamp
1560 *
1561 * @return float
1562 */
1563 private static function EBMLdate2unix($EBMLdatestamp) {
1564 // Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC)
1565 // 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC
1566 return round(($EBMLdatestamp / 1000000000) + 978307200);
1567 }
1568
1569 /**
1570 * @param int $target_type
1571 *
1572 * @return string|int
1573 */
1574 public static function TargetTypeValue($target_type) {
1575 // http://www.matroska.org/technical/specs/tagging/index.html
1576 static $TargetTypeValue = array();
1577 if (empty($TargetTypeValue)) {
1578 $TargetTypeValue[10] = 'A: ~ V:shot'; // the lowest hierarchy found in music or movies
1579 $TargetTypeValue[20] = 'A:subtrack/part/movement ~ V:scene'; // corresponds to parts of a track for audio (like a movement)
1580 $TargetTypeValue[30] = 'A:track/song ~ V:chapter'; // the common parts of an album or a movie
1581 $TargetTypeValue[40] = 'A:part/session ~ V:part/session'; // when an album or episode has different logical parts
1582 $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)
1583 $TargetTypeValue[60] = 'A:edition/issue/volume/opus ~ V:season/sequel/volume'; // a list of lower levels grouped together
1584 $TargetTypeValue[70] = 'A:collection ~ V:collection'; // the high hierarchy consisting of many different lower items
1585 }
1586 return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type);
1587 }
1588
1589 /**
1590 * @param int $lacingtype
1591 *
1592 * @return string|int
1593 */
1594 public static function BlockLacingType($lacingtype) {
1595 // http://matroska.org/technical/specs/index.html#block_structure
1596 static $BlockLacingType = array();
1597 if (empty($BlockLacingType)) {
1598 $BlockLacingType[0x00] = 'no lacing';
1599 $BlockLacingType[0x01] = 'Xiph lacing';
1600 $BlockLacingType[0x02] = 'fixed-size lacing';
1601 $BlockLacingType[0x03] = 'EBML lacing';
1602 }
1603 return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype);
1604 }
1605
1606 /**
1607 * @param string $codecid
1608 *
1609 * @return string
1610 */
1611 public static function CodecIDtoCommonName($codecid) {
1612 // http://www.matroska.org/technical/specs/codecid/index.html
1613 static $CodecIDlist = array();
1614 if (empty($CodecIDlist)) {
1615 $CodecIDlist['A_AAC'] = 'aac';
1616 $CodecIDlist['A_AAC/MPEG2/LC'] = 'aac';
1617 $CodecIDlist['A_AC3'] = 'ac3';
1618 $CodecIDlist['A_EAC3'] = 'eac3';
1619 $CodecIDlist['A_DTS'] = 'dts';
1620 $CodecIDlist['A_FLAC'] = 'flac';
1621 $CodecIDlist['A_MPEG/L1'] = 'mp1';
1622 $CodecIDlist['A_MPEG/L2'] = 'mp2';
1623 $CodecIDlist['A_MPEG/L3'] = 'mp3';
1624 $CodecIDlist['A_PCM/INT/LIT'] = 'pcm'; // PCM Integer Little Endian
1625 $CodecIDlist['A_PCM/INT/BIG'] = 'pcm'; // PCM Integer Big Endian
1626 $CodecIDlist['A_QUICKTIME/QDMC'] = 'quicktime'; // Quicktime: QDesign Music
1627 $CodecIDlist['A_QUICKTIME/QDM2'] = 'quicktime'; // Quicktime: QDesign Music v2
1628 $CodecIDlist['A_VORBIS'] = 'vorbis';
1629 $CodecIDlist['V_MPEG1'] = 'mpeg';
1630 $CodecIDlist['V_THEORA'] = 'theora';
1631 $CodecIDlist['V_REAL/RV40'] = 'real';
1632 $CodecIDlist['V_REAL/RV10'] = 'real';
1633 $CodecIDlist['V_REAL/RV20'] = 'real';
1634 $CodecIDlist['V_REAL/RV30'] = 'real';
1635 $CodecIDlist['V_QUICKTIME'] = 'quicktime'; // Quicktime
1636 $CodecIDlist['V_MPEG4/ISO/AP'] = 'mpeg4';
1637 $CodecIDlist['V_MPEG4/ISO/ASP'] = 'mpeg4';
1638 $CodecIDlist['V_MPEG4/ISO/AVC'] = 'h264';
1639 $CodecIDlist['V_MPEG4/ISO/SP'] = 'mpeg4';
1640 $CodecIDlist['V_VP8'] = 'vp8';
1641 $CodecIDlist['V_MS/VFW/FOURCC'] = 'vcm'; // Microsoft (TM) Video Codec Manager (VCM)
1642 $CodecIDlist['A_MS/ACM'] = 'acm'; // Microsoft (TM) Audio Codec Manager (ACM)
1643 }
1644 return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid);
1645 }
1646
1647 /**
1648 * @param int $value
1649 *
1650 * @return string
1651 */
1652 private static function EBMLidName($value) {
1653 static $EBMLidList = array();
1654 if (empty($EBMLidList)) {
1655 $EBMLidList[EBML_ID_ASPECTRATIOTYPE] = 'AspectRatioType';
1656 $EBMLidList[EBML_ID_ATTACHEDFILE] = 'AttachedFile';
1657 $EBMLidList[EBML_ID_ATTACHMENTLINK] = 'AttachmentLink';
1658 $EBMLidList[EBML_ID_ATTACHMENTS] = 'Attachments';
1659 $EBMLidList[EBML_ID_AUDIO] = 'Audio';
1660 $EBMLidList[EBML_ID_BITDEPTH] = 'BitDepth';
1661 $EBMLidList[EBML_ID_CHANNELPOSITIONS] = 'ChannelPositions';
1662 $EBMLidList[EBML_ID_CHANNELS] = 'Channels';
1663 $EBMLidList[EBML_ID_CHAPCOUNTRY] = 'ChapCountry';
1664 $EBMLidList[EBML_ID_CHAPLANGUAGE] = 'ChapLanguage';
1665 $EBMLidList[EBML_ID_CHAPPROCESS] = 'ChapProcess';
1666 $EBMLidList[EBML_ID_CHAPPROCESSCODECID] = 'ChapProcessCodecID';
1667 $EBMLidList[EBML_ID_CHAPPROCESSCOMMAND] = 'ChapProcessCommand';
1668 $EBMLidList[EBML_ID_CHAPPROCESSDATA] = 'ChapProcessData';
1669 $EBMLidList[EBML_ID_CHAPPROCESSPRIVATE] = 'ChapProcessPrivate';
1670 $EBMLidList[EBML_ID_CHAPPROCESSTIME] = 'ChapProcessTime';
1671 $EBMLidList[EBML_ID_CHAPSTRING] = 'ChapString';
1672 $EBMLidList[EBML_ID_CHAPTERATOM] = 'ChapterAtom';
1673 $EBMLidList[EBML_ID_CHAPTERDISPLAY] = 'ChapterDisplay';
1674 $EBMLidList[EBML_ID_CHAPTERFLAGENABLED] = 'ChapterFlagEnabled';
1675 $EBMLidList[EBML_ID_CHAPTERFLAGHIDDEN] = 'ChapterFlagHidden';
1676 $EBMLidList[EBML_ID_CHAPTERPHYSICALEQUIV] = 'ChapterPhysicalEquiv';
1677 $EBMLidList[EBML_ID_CHAPTERS] = 'Chapters';
1678 $EBMLidList[EBML_ID_CHAPTERSEGMENTEDITIONUID] = 'ChapterSegmentEditionUID';
1679 $EBMLidList[EBML_ID_CHAPTERSEGMENTUID] = 'ChapterSegmentUID';
1680 $EBMLidList[EBML_ID_CHAPTERTIMEEND] = 'ChapterTimeEnd';
1681 $EBMLidList[EBML_ID_CHAPTERTIMESTART] = 'ChapterTimeStart';
1682 $EBMLidList[EBML_ID_CHAPTERTRACK] = 'ChapterTrack';
1683 $EBMLidList[EBML_ID_CHAPTERTRACKNUMBER] = 'ChapterTrackNumber';
1684 $EBMLidList[EBML_ID_CHAPTERTRANSLATE] = 'ChapterTranslate';
1685 $EBMLidList[EBML_ID_CHAPTERTRANSLATECODEC] = 'ChapterTranslateCodec';
1686 $EBMLidList[EBML_ID_CHAPTERTRANSLATEEDITIONUID] = 'ChapterTranslateEditionUID';
1687 $EBMLidList[EBML_ID_CHAPTERTRANSLATEID] = 'ChapterTranslateID';
1688 $EBMLidList[EBML_ID_CHAPTERUID] = 'ChapterUID';
1689 $EBMLidList[EBML_ID_CLUSTER] = 'Cluster';
1690 $EBMLidList[EBML_ID_CLUSTERBLOCK] = 'ClusterBlock';
1691 $EBMLidList[EBML_ID_CLUSTERBLOCKADDID] = 'ClusterBlockAddID';
1692 $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONAL] = 'ClusterBlockAdditional';
1693 $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONID] = 'ClusterBlockAdditionID';
1694 $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONS] = 'ClusterBlockAdditions';
1695 $EBMLidList[EBML_ID_CLUSTERBLOCKDURATION] = 'ClusterBlockDuration';
1696 $EBMLidList[EBML_ID_CLUSTERBLOCKGROUP] = 'ClusterBlockGroup';
1697 $EBMLidList[EBML_ID_CLUSTERBLOCKMORE] = 'ClusterBlockMore';
1698 $EBMLidList[EBML_ID_CLUSTERBLOCKVIRTUAL] = 'ClusterBlockVirtual';
1699 $EBMLidList[EBML_ID_CLUSTERCODECSTATE] = 'ClusterCodecState';
1700 $EBMLidList[EBML_ID_CLUSTERDELAY] = 'ClusterDelay';
1701 $EBMLidList[EBML_ID_CLUSTERDURATION] = 'ClusterDuration';
1702 $EBMLidList[EBML_ID_CLUSTERENCRYPTEDBLOCK] = 'ClusterEncryptedBlock';
1703 $EBMLidList[EBML_ID_CLUSTERFRAMENUMBER] = 'ClusterFrameNumber';
1704 $EBMLidList[EBML_ID_CLUSTERLACENUMBER] = 'ClusterLaceNumber';
1705 $EBMLidList[EBML_ID_CLUSTERPOSITION] = 'ClusterPosition';
1706 $EBMLidList[EBML_ID_CLUSTERPREVSIZE] = 'ClusterPrevSize';
1707 $EBMLidList[EBML_ID_CLUSTERREFERENCEBLOCK] = 'ClusterReferenceBlock';
1708 $EBMLidList[EBML_ID_CLUSTERREFERENCEPRIORITY] = 'ClusterReferencePriority';
1709 $EBMLidList[EBML_ID_CLUSTERREFERENCEVIRTUAL] = 'ClusterReferenceVirtual';
1710 $EBMLidList[EBML_ID_CLUSTERSILENTTRACKNUMBER] = 'ClusterSilentTrackNumber';
1711 $EBMLidList[EBML_ID_CLUSTERSILENTTRACKS] = 'ClusterSilentTracks';
1712 $EBMLidList[EBML_ID_CLUSTERSIMPLEBLOCK] = 'ClusterSimpleBlock';
1713 $EBMLidList[EBML_ID_CLUSTERTIMECODE] = 'ClusterTimecode';
1714 $EBMLidList[EBML_ID_CLUSTERTIMESLICE] = 'ClusterTimeSlice';
1715 $EBMLidList[EBML_ID_CODECDECODEALL] = 'CodecDecodeAll';
1716 $EBMLidList[EBML_ID_CODECDOWNLOADURL] = 'CodecDownloadURL';
1717 $EBMLidList[EBML_ID_CODECID] = 'CodecID';
1718 $EBMLidList[EBML_ID_CODECINFOURL] = 'CodecInfoURL';
1719 $EBMLidList[EBML_ID_CODECNAME] = 'CodecName';
1720 $EBMLidList[EBML_ID_CODECPRIVATE] = 'CodecPrivate';
1721 $EBMLidList[EBML_ID_CODECSETTINGS] = 'CodecSettings';
1722 $EBMLidList[EBML_ID_COLOURSPACE] = 'ColourSpace';
1723 $EBMLidList[EBML_ID_CONTENTCOMPALGO] = 'ContentCompAlgo';
1724 $EBMLidList[EBML_ID_CONTENTCOMPRESSION] = 'ContentCompression';
1725 $EBMLidList[EBML_ID_CONTENTCOMPSETTINGS] = 'ContentCompSettings';
1726 $EBMLidList[EBML_ID_CONTENTENCALGO] = 'ContentEncAlgo';
1727 $EBMLidList[EBML_ID_CONTENTENCKEYID] = 'ContentEncKeyID';
1728 $EBMLidList[EBML_ID_CONTENTENCODING] = 'ContentEncoding';
1729 $EBMLidList[EBML_ID_CONTENTENCODINGORDER] = 'ContentEncodingOrder';
1730 $EBMLidList[EBML_ID_CONTENTENCODINGS] = 'ContentEncodings';
1731 $EBMLidList[EBML_ID_CONTENTENCODINGSCOPE] = 'ContentEncodingScope';
1732 $EBMLidList[EBML_ID_CONTENTENCODINGTYPE] = 'ContentEncodingType';
1733 $EBMLidList[EBML_ID_CONTENTENCRYPTION] = 'ContentEncryption';
1734 $EBMLidList[EBML_ID_CONTENTSIGALGO] = 'ContentSigAlgo';
1735 $EBMLidList[EBML_ID_CONTENTSIGHASHALGO] = 'ContentSigHashAlgo';
1736 $EBMLidList[EBML_ID_CONTENTSIGKEYID] = 'ContentSigKeyID';
1737 $EBMLidList[EBML_ID_CONTENTSIGNATURE] = 'ContentSignature';
1738 $EBMLidList[EBML_ID_CRC32] = 'CRC32';
1739 $EBMLidList[EBML_ID_CUEBLOCKNUMBER] = 'CueBlockNumber';
1740 $EBMLidList[EBML_ID_CUECLUSTERPOSITION] = 'CueClusterPosition';
1741 $EBMLidList[EBML_ID_CUECODECSTATE] = 'CueCodecState';
1742 $EBMLidList[EBML_ID_CUEPOINT] = 'CuePoint';
1743 $EBMLidList[EBML_ID_CUEREFCLUSTER] = 'CueRefCluster';
1744 $EBMLidList[EBML_ID_CUEREFCODECSTATE] = 'CueRefCodecState';
1745 $EBMLidList[EBML_ID_CUEREFERENCE] = 'CueReference';
1746 $EBMLidList[EBML_ID_CUEREFNUMBER] = 'CueRefNumber';
1747 $EBMLidList[EBML_ID_CUEREFTIME] = 'CueRefTime';
1748 $EBMLidList[EBML_ID_CUES] = 'Cues';
1749 $EBMLidList[EBML_ID_CUETIME] = 'CueTime';
1750 $EBMLidList[EBML_ID_CUETRACK] = 'CueTrack';
1751 $EBMLidList[EBML_ID_CUETRACKPOSITIONS] = 'CueTrackPositions';
1752 $EBMLidList[EBML_ID_DATEUTC] = 'DateUTC';
1753 $EBMLidList[EBML_ID_DEFAULTDURATION] = 'DefaultDuration';
1754 $EBMLidList[EBML_ID_DISPLAYHEIGHT] = 'DisplayHeight';
1755 $EBMLidList[EBML_ID_DISPLAYUNIT] = 'DisplayUnit';
1756 $EBMLidList[EBML_ID_DISPLAYWIDTH] = 'DisplayWidth';
1757 $EBMLidList[EBML_ID_DOCTYPE] = 'DocType';
1758 $EBMLidList[EBML_ID_DOCTYPEREADVERSION] = 'DocTypeReadVersion';
1759 $EBMLidList[EBML_ID_DOCTYPEVERSION] = 'DocTypeVersion';
1760 $EBMLidList[EBML_ID_DURATION] = 'Duration';
1761 $EBMLidList[EBML_ID_EBML] = 'EBML';
1762 $EBMLidList[EBML_ID_EBMLMAXIDLENGTH] = 'EBMLMaxIDLength';
1763 $EBMLidList[EBML_ID_EBMLMAXSIZELENGTH] = 'EBMLMaxSizeLength';
1764 $EBMLidList[EBML_ID_EBMLREADVERSION] = 'EBMLReadVersion';
1765 $EBMLidList[EBML_ID_EBMLVERSION] = 'EBMLVersion';
1766 $EBMLidList[EBML_ID_EDITIONENTRY] = 'EditionEntry';
1767 $EBMLidList[EBML_ID_EDITIONFLAGDEFAULT] = 'EditionFlagDefault';
1768 $EBMLidList[EBML_ID_EDITIONFLAGHIDDEN] = 'EditionFlagHidden';
1769 $EBMLidList[EBML_ID_EDITIONFLAGORDERED] = 'EditionFlagOrdered';
1770 $EBMLidList[EBML_ID_EDITIONUID] = 'EditionUID';
1771 $EBMLidList[EBML_ID_FILEDATA] = 'FileData';
1772 $EBMLidList[EBML_ID_FILEDESCRIPTION] = 'FileDescription';
1773 $EBMLidList[EBML_ID_FILEMIMETYPE] = 'FileMimeType';
1774 $EBMLidList[EBML_ID_FILENAME] = 'FileName';
1775 $EBMLidList[EBML_ID_FILEREFERRAL] = 'FileReferral';
1776 $EBMLidList[EBML_ID_FILEUID] = 'FileUID';
1777 $EBMLidList[EBML_ID_FLAGDEFAULT] = 'FlagDefault';
1778 $EBMLidList[EBML_ID_FLAGENABLED] = 'FlagEnabled';
1779 $EBMLidList[EBML_ID_FLAGFORCED] = 'FlagForced';
1780 $EBMLidList[EBML_ID_FLAGINTERLACED] = 'FlagInterlaced';
1781 $EBMLidList[EBML_ID_FLAGLACING] = 'FlagLacing';
1782 $EBMLidList[EBML_ID_GAMMAVALUE] = 'GammaValue';
1783 $EBMLidList[EBML_ID_INFO] = 'Info';
1784 $EBMLidList[EBML_ID_LANGUAGE] = 'Language';
1785 $EBMLidList[EBML_ID_MAXBLOCKADDITIONID] = 'MaxBlockAdditionID';
1786 $EBMLidList[EBML_ID_MAXCACHE] = 'MaxCache';
1787 $EBMLidList[EBML_ID_MINCACHE] = 'MinCache';
1788 $EBMLidList[EBML_ID_MUXINGAPP] = 'MuxingApp';
1789 $EBMLidList[EBML_ID_NAME] = 'Name';
1790 $EBMLidList[EBML_ID_NEXTFILENAME] = 'NextFilename';
1791 $EBMLidList[EBML_ID_NEXTUID] = 'NextUID';
1792 $EBMLidList[EBML_ID_OUTPUTSAMPLINGFREQUENCY] = 'OutputSamplingFrequency';
1793 $EBMLidList[EBML_ID_PIXELCROPBOTTOM] = 'PixelCropBottom';
1794 $EBMLidList[EBML_ID_PIXELCROPLEFT] = 'PixelCropLeft';
1795 $EBMLidList[EBML_ID_PIXELCROPRIGHT] = 'PixelCropRight';
1796 $EBMLidList[EBML_ID_PIXELCROPTOP] = 'PixelCropTop';
1797 $EBMLidList[EBML_ID_PIXELHEIGHT] = 'PixelHeight';
1798 $EBMLidList[EBML_ID_PIXELWIDTH] = 'PixelWidth';
1799 $EBMLidList[EBML_ID_PREVFILENAME] = 'PrevFilename';
1800 $EBMLidList[EBML_ID_PREVUID] = 'PrevUID';
1801 $EBMLidList[EBML_ID_SAMPLINGFREQUENCY] = 'SamplingFrequency';
1802 $EBMLidList[EBML_ID_SEEK] = 'Seek';
1803 $EBMLidList[EBML_ID_SEEKHEAD] = 'SeekHead';
1804 $EBMLidList[EBML_ID_SEEKID] = 'SeekID';
1805 $EBMLidList[EBML_ID_SEEKPOSITION] = 'SeekPosition';
1806 $EBMLidList[EBML_ID_SEGMENT] = 'Segment';
1807 $EBMLidList[EBML_ID_SEGMENTFAMILY] = 'SegmentFamily';
1808 $EBMLidList[EBML_ID_SEGMENTFILENAME] = 'SegmentFilename';
1809 $EBMLidList[EBML_ID_SEGMENTUID] = 'SegmentUID';
1810 $EBMLidList[EBML_ID_SIMPLETAG] = 'SimpleTag';
1811 $EBMLidList[EBML_ID_CLUSTERSLICES] = 'ClusterSlices';
1812 $EBMLidList[EBML_ID_STEREOMODE] = 'StereoMode';
1813 $EBMLidList[EBML_ID_OLDSTEREOMODE] = 'OldStereoMode';
1814 $EBMLidList[EBML_ID_TAG] = 'Tag';
1815 $EBMLidList[EBML_ID_TAGATTACHMENTUID] = 'TagAttachmentUID';
1816 $EBMLidList[EBML_ID_TAGBINARY] = 'TagBinary';
1817 $EBMLidList[EBML_ID_TAGCHAPTERUID] = 'TagChapterUID';
1818 $EBMLidList[EBML_ID_TAGDEFAULT] = 'TagDefault';
1819 $EBMLidList[EBML_ID_TAGEDITIONUID] = 'TagEditionUID';
1820 $EBMLidList[EBML_ID_TAGLANGUAGE] = 'TagLanguage';
1821 $EBMLidList[EBML_ID_TAGNAME] = 'TagName';
1822 $EBMLidList[EBML_ID_TAGTRACKUID] = 'TagTrackUID';
1823 $EBMLidList[EBML_ID_TAGS] = 'Tags';
1824 $EBMLidList[EBML_ID_TAGSTRING] = 'TagString';
1825 $EBMLidList[EBML_ID_TARGETS] = 'Targets';
1826 $EBMLidList[EBML_ID_TARGETTYPE] = 'TargetType';
1827 $EBMLidList[EBML_ID_TARGETTYPEVALUE] = 'TargetTypeValue';
1828 $EBMLidList[EBML_ID_TIMECODESCALE] = 'TimecodeScale';
1829 $EBMLidList[EBML_ID_TITLE] = 'Title';
1830 $EBMLidList[EBML_ID_TRACKENTRY] = 'TrackEntry';
1831 $EBMLidList[EBML_ID_TRACKNUMBER] = 'TrackNumber';
1832 $EBMLidList[EBML_ID_TRACKOFFSET] = 'TrackOffset';
1833 $EBMLidList[EBML_ID_TRACKOVERLAY] = 'TrackOverlay';
1834 $EBMLidList[EBML_ID_TRACKS] = 'Tracks';
1835 $EBMLidList[EBML_ID_TRACKTIMECODESCALE] = 'TrackTimecodeScale';
1836 $EBMLidList[EBML_ID_TRACKTRANSLATE] = 'TrackTranslate';
1837 $EBMLidList[EBML_ID_TRACKTRANSLATECODEC] = 'TrackTranslateCodec';
1838 $EBMLidList[EBML_ID_TRACKTRANSLATEEDITIONUID] = 'TrackTranslateEditionUID';
1839 $EBMLidList[EBML_ID_TRACKTRANSLATETRACKID] = 'TrackTranslateTrackID';
1840 $EBMLidList[EBML_ID_TRACKTYPE] = 'TrackType';
1841 $EBMLidList[EBML_ID_TRACKUID] = 'TrackUID';
1842 $EBMLidList[EBML_ID_VIDEO] = 'Video';
1843 $EBMLidList[EBML_ID_VOID] = 'Void';
1844 $EBMLidList[EBML_ID_WRITINGAPP] = 'WritingApp';
1845 }
1846
1847 return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
1848 }
1849
1850 /**
1851 * @param int $value
1852 *
1853 * @return string
1854 */
1855 public static function displayUnit($value) {
1856 // http://www.matroska.org/technical/specs/index.html#DisplayUnit
1857 static $units = array(
1858 0 => 'pixels',
1859 1 => 'centimeters',
1860 2 => 'inches',
1861 3 => 'Display Aspect Ratio');
1862
1863 return (isset($units[$value]) ? $units[$value] : 'unknown');
1864 }
1865
1866 /**
1867 * @param array $streams
1868 *
1869 * @return array
1870 */
1871 private static function getDefaultStreamInfo($streams)
1872 {
1873 $stream = array();
1874 foreach (array_reverse($streams) as $stream) {
1875 if ($stream['default']) {
1876 break;
1877 }
1878 }
1879
1880 $unset = array('default', 'name');
1881 foreach ($unset as $u) {
1882 if (isset($stream[$u])) {
1883 unset($stream[$u]);
1884 }
1885 }
1886
1887 $info = $stream;
1888 $info['streams'] = $streams;
1889
1890 return $info;
1891 }
1892
1893 }