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