File indexing completed on 2024-12-22 05:33:11

0001 <?php
0002 /////////////////////////////////////////////////////////////////
0003 /// getID3() by James Heinrich <info@getid3.org>               //
0004 //  available at http://getid3.sourceforge.net                 //
0005 //            or http://www.getid3.org                         //
0006 //          also https://github.com/JamesHeinrich/getID3       //
0007 /////////////////////////////////////////////////////////////////
0008 // See readme.txt for more details                             //
0009 /////////////////////////////////////////////////////////////////
0010 //                                                             //
0011 // module.audio-video.matriska.php                             //
0012 // module for analyzing Matroska containers                    //
0013 // dependencies: NONE                                          //
0014 //                                                            ///
0015 /////////////////////////////////////////////////////////////////
0016 
0017 
0018 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.
0019 define('EBML_ID_SEEKHEAD',                  0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
0020 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>.
0021 define('EBML_ID_INFO',                      0x0549A966); // [15][49][A9][66] -- Contains miscellaneous general information and statistics on the file.
0022 define('EBML_ID_TRACKS',                    0x0654AE6B); // [16][54][AE][6B] -- A top-level block of information with many tracks described.
0023 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.
0024 define('EBML_ID_ATTACHMENTS',               0x0941A469); // [19][41][A4][69] -- Contain attached files.
0025 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.
0026 define('EBML_ID_CUES',                      0x0C53BB6B); // [1C][53][BB][6B] -- A top-level element to speed seeking access. All entries are local to the segment.
0027 define('EBML_ID_CLUSTER',                   0x0F43B675); // [1F][43][B6][75] -- The lower level element containing the (monolithic) Block structure.
0028 define('EBML_ID_LANGUAGE',                    0x02B59C); //     [22][B5][9C] -- Specifies the language of the track in the Matroska languages form.
0029 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).
0030 define('EBML_ID_DEFAULTDURATION',             0x03E383); //     [23][E3][83] -- Number of nanoseconds (i.e. not scaled) per frame.
0031 define('EBML_ID_CODECNAME',                   0x058688); //     [25][86][88] -- A human-readable string specifying the codec.
0032 define('EBML_ID_CODECDOWNLOADURL',            0x06B240); //     [26][B2][40] -- A URL to download about the codec used.
0033 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).
0034 define('EBML_ID_COLOURSPACE',                 0x0EB524); //     [2E][B5][24] -- Same value as in AVI (32 bits).
0035 define('EBML_ID_GAMMAVALUE',                  0x0FB523); //     [2F][B5][23] -- Gamma Value.
0036 define('EBML_ID_CODECSETTINGS',               0x1A9697); //     [3A][96][97] -- A string describing the encoding setting used.
0037 define('EBML_ID_CODECINFOURL',                0x1B4040); //     [3B][40][40] -- A URL to find information about the codec used.
0038 define('EBML_ID_PREVFILENAME',                0x1C83AB); //     [3C][83][AB] -- An escaped filename corresponding to the previous segment.
0039 define('EBML_ID_PREVUID',                     0x1CB923); //     [3C][B9][23] -- A unique ID to identify the previous chained segment (128 bits).
0040 define('EBML_ID_NEXTFILENAME',                0x1E83BB); //     [3E][83][BB] -- An escaped filename corresponding to the next segment.
0041 define('EBML_ID_NEXTUID',                     0x1EB923); //     [3E][B9][23] -- A unique ID to identify the next chained segment (128 bits).
0042 define('EBML_ID_CONTENTCOMPALGO',               0x0254); //         [42][54] -- The compression algorithm used. Algorithms that have been specified so far are:
0043 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.
0044 define('EBML_ID_DOCTYPE',                       0x0282); //         [42][82] -- A string that describes the type of document that follows this EBML header ('matroska' in our case).
0045 define('EBML_ID_DOCTYPEREADVERSION',            0x0285); //         [42][85] -- The minimum DocType version an interpreter has to support to read this file.
0046 define('EBML_ID_EBMLVERSION',                   0x0286); //         [42][86] -- The version of EBML parser used to create the file.
0047 define('EBML_ID_DOCTYPEVERSION',                0x0287); //         [42][87] -- The version of DocType interpreter used to create the file.
0048 define('EBML_ID_EBMLMAXIDLENGTH',               0x02F2); //         [42][F2] -- The maximum length of the IDs you'll find in this file (4 or less in Matroska).
0049 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.
0050 define('EBML_ID_EBMLREADVERSION',               0x02F7); //         [42][F7] -- The minimum EBML version a parser has to support to read this file.
0051 define('EBML_ID_CHAPLANGUAGE',                  0x037C); //         [43][7C] -- The languages corresponding to the string, in the bibliographic ISO-639-2 form.
0052 define('EBML_ID_CHAPCOUNTRY',                   0x037E); //         [43][7E] -- The countries corresponding to the string, same 2 octets as in Internet domains.
0053 define('EBML_ID_SEGMENTFAMILY',                 0x0444); //         [44][44] -- A randomly generated unique ID that all segments related to each other must use (128 bits).
0054 define('EBML_ID_DATEUTC',                       0x0461); //         [44][61] -- Date of the origin of timecode (value 0), i.e. production date.
0055 define('EBML_ID_TAGLANGUAGE',                   0x047A); //         [44][7A] -- Specifies the language of the tag specified, in the Matroska languages form.
0056 define('EBML_ID_TAGDEFAULT',                    0x0484); //         [44][84] -- Indication to know if this is the default/original language to use for the given tag.
0057 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.
0058 define('EBML_ID_TAGSTRING',                     0x0487); //         [44][87] -- The value of the Tag.
0059 define('EBML_ID_DURATION',                      0x0489); //         [44][89] -- Duration of the segment (based on TimecodeScale).
0060 define('EBML_ID_CHAPPROCESSPRIVATE',            0x050D); //         [45][0D] -- Some optional data attached to the ChapProcessCodecID information. For ChapProcessCodecID = 1, it is the "DVD level" equivalent.
0061 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.
0062 define('EBML_ID_TAGNAME',                       0x05A3); //         [45][A3] -- The name of the Tag that is going to be stored.
0063 define('EBML_ID_EDITIONENTRY',                  0x05B9); //         [45][B9] -- Contains all information about a segment edition.
0064 define('EBML_ID_EDITIONUID',                    0x05BC); //         [45][BC] -- A unique ID to identify the edition. It's useful for tagging an edition.
0065 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).
0066 define('EBML_ID_EDITIONFLAGDEFAULT',            0x05DB); //         [45][DB] -- If a flag is set (1) the edition should be used as the default one.
0067 define('EBML_ID_EDITIONFLAGORDERED',            0x05DD); //         [45][DD] -- Specify if the chapters can be defined multiple times and the order to play them is enforced.
0068 define('EBML_ID_FILEDATA',                      0x065C); //         [46][5C] -- The data of the file.
0069 define('EBML_ID_FILEMIMETYPE',                  0x0660); //         [46][60] -- MIME type of the file.
0070 define('EBML_ID_FILENAME',                      0x066E); //         [46][6E] -- Filename of the attached file.
0071 define('EBML_ID_FILEREFERRAL',                  0x0675); //         [46][75] -- A binary value that a track/codec can refer to when the attachment is needed.
0072 define('EBML_ID_FILEDESCRIPTION',               0x067E); //         [46][7E] -- A human-friendly name for the attached file.
0073 define('EBML_ID_FILEUID',                       0x06AE); //         [46][AE] -- Unique ID representing the file, as random as possible.
0074 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:
0075 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.
0076 define('EBML_ID_CONTENTSIGNATURE',              0x07E3); //         [47][E3] -- A cryptographic signature of the contents.
0077 define('EBML_ID_CONTENTSIGKEYID',               0x07E4); //         [47][E4] -- This is the ID of the private key the data was signed with.
0078 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:
0079 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:
0080 define('EBML_ID_MUXINGAPP',                     0x0D80); //         [4D][80] -- Muxing application or library ("libmatroska-0.4.3").
0081 define('EBML_ID_SEEK',                          0x0DBB); //         [4D][BB] -- Contains a single seek entry to an EBML element.
0082 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.
0083 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:
0084 define('EBML_ID_CONTENTENCODINGTYPE',           0x1033); //         [50][33] -- A value describing what kind of transformation has been done. Possible values:
0085 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.
0086 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.
0087 define('EBML_ID_CUEREFNUMBER',                  0x135F); //         [53][5F] -- Number of the referenced Block of Track X in the specified Cluster.
0088 define('EBML_ID_NAME',                          0x136E); //         [53][6E] -- A human-readable track name.
0089 define('EBML_ID_CUEBLOCKNUMBER',                0x1378); //         [53][78] -- Number of the Block in the specified Cluster.
0090 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.
0091 define('EBML_ID_SEEKID',                        0x13AB); //         [53][AB] -- The binary ID corresponding to the element name.
0092 define('EBML_ID_SEEKPOSITION',                  0x13AC); //         [53][AC] -- The position of the element in the segment in octets (0 = first level 1 element).
0093 define('EBML_ID_STEREOMODE',                    0x13B8); //         [53][B8] -- Stereo-3D video mode.
0094 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).
0095 define('EBML_ID_PIXELCROPBOTTOM',               0x14AA); //         [54][AA] -- The number of video pixels to remove at the bottom of the image (for HDTV content).
0096 define('EBML_ID_DISPLAYWIDTH',                  0x14B0); //         [54][B0] -- Width of the video frames to display.
0097 define('EBML_ID_DISPLAYUNIT',                   0x14B2); //         [54][B2] -- Type of the unit for DisplayWidth/Height (0: pixels, 1: centimeters, 2: inches).
0098 define('EBML_ID_ASPECTRATIOTYPE',               0x14B3); //         [54][B3] -- Specify the possible modifications to the aspect ratio (0: free resizing, 1: keep aspect ratio, 2: fixed).
0099 define('EBML_ID_DISPLAYHEIGHT',                 0x14BA); //         [54][BA] -- Height of the video frames to display.
0100 define('EBML_ID_PIXELCROPTOP',                  0x14BB); //         [54][BB] -- The number of video pixels to remove at the top of the image.
0101 define('EBML_ID_PIXELCROPLEFT',                 0x14CC); //         [54][CC] -- The number of video pixels to remove on the left of the image.
0102 define('EBML_ID_PIXELCROPRIGHT',                0x14DD); //         [54][DD] -- The number of video pixels to remove on the right of the image.
0103 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.
0104 define('EBML_ID_MAXBLOCKADDITIONID',            0x15EE); //         [55][EE] -- The maximum value of BlockAddID. A value 0 means there is no BlockAdditions for this track.
0105 define('EBML_ID_WRITINGAPP',                    0x1741); //         [57][41] -- Writing application ("mkvmerge-0.3.3").
0106 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.
0107 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.
0108 define('EBML_ID_ATTACHEDFILE',                  0x21A7); //         [61][A7] -- An attached file.
0109 define('EBML_ID_CONTENTENCODING',               0x2240); //         [62][40] -- Settings for one content encoding like compression or encryption.
0110 define('EBML_ID_BITDEPTH',                      0x2264); //         [62][64] -- Bits per sample, mostly used for PCM.
0111 define('EBML_ID_CODECPRIVATE',                  0x23A2); //         [63][A2] -- Private data only known to the codec.
0112 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.
0113 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.
0114 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.
0115 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.
0116 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.
0117 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.
0118 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).
0119 define('EBML_ID_TRACKTRANSLATE',                0x2624); //         [66][24] -- The track identification for the given Chapter Codec.
0120 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.
0121 define('EBML_ID_TRACKTRANSLATECODEC',           0x26BF); //         [66][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
0122 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.
0123 define('EBML_ID_SIMPLETAG',                     0x27C8); //         [67][C8] -- Contains general information about the target.
0124 define('EBML_ID_TARGETTYPEVALUE',               0x28CA); //         [68][CA] -- A number to indicate the logical level of the target (see TargetType).
0125 define('EBML_ID_CHAPPROCESSCOMMAND',            0x2911); //         [69][11] -- Contains all the commands associated to the Atom.
0126 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).
0127 define('EBML_ID_CHAPTERTRANSLATE',              0x2924); //         [69][24] -- A tuple of corresponding ID used by chapter codecs to represent this segment.
0128 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.
0129 define('EBML_ID_CHAPPROCESS',                   0x2944); //         [69][44] -- Contains all the commands associated to the Atom.
0130 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.
0131 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.
0132 define('EBML_ID_CHAPTERTRANSLATECODEC',         0x29BF); //         [69][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
0133 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.
0134 define('EBML_ID_CONTENTENCODINGS',              0x2D80); //         [6D][80] -- Settings for several content encoding mechanisms like compression or encryption.
0135 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.
0136 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.
0137 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.
0138 define('EBML_ID_CHAPTERSEGMENTEDITIONUID',      0x2EBC); //         [6E][BC] -- The edition to play from the segment linked in ChapterSegmentUID.
0139 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.
0140 define('EBML_ID_TAG',                           0x3373); //         [73][73] -- Element containing elements specific to Tracks/Chapters.
0141 define('EBML_ID_SEGMENTFILENAME',               0x3384); //         [73][84] -- A filename corresponding to this segment.
0142 define('EBML_ID_SEGMENTUID',                    0x33A4); //         [73][A4] -- A randomly generated unique ID to identify the current segment between many others (128 bits).
0143 define('EBML_ID_CHAPTERUID',                    0x33C4); //         [73][C4] -- A unique ID to identify the Chapter.
0144 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.
0145 define('EBML_ID_ATTACHMENTLINK',                0x3446); //         [74][46] -- The UID of an attachment that is used by this codec.
0146 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.
0147 define('EBML_ID_CHANNELPOSITIONS',              0x347B); //         [7D][7B] -- Table of horizontal angles for each successive channel, see appendix.
0148 define('EBML_ID_OUTPUTSAMPLINGFREQUENCY',       0x38B5); //         [78][B5] -- Real output sampling frequency in Hz (used for SBR techniques).
0149 define('EBML_ID_TITLE',                         0x3BA9); //         [7B][A9] -- General name of the segment.
0150 define('EBML_ID_CHAPTERDISPLAY',                  0x00); //             [80] -- Contains all possible strings to use for the chapter display.
0151 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).
0152 define('EBML_ID_CHAPSTRING',                      0x05); //             [85] -- Contains the string to use as the chapter atom.
0153 define('EBML_ID_CODECID',                         0x06); //             [86] -- An ID corresponding to the codec, see the codec page for more info.
0154 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.
0155 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.
0156 define('EBML_ID_CLUSTERSLICES',                   0x0E); //             [8E] -- Contains slices description.
0157 define('EBML_ID_CHAPTERTRACK',                    0x0F); //             [8F] -- List of tracks on which the chapter applies. If this element is not present, all tracks apply
0158 define('EBML_ID_CHAPTERTIMESTART',                0x11); //             [91] -- Timecode of the start of Chapter (not scaled).
0159 define('EBML_ID_CHAPTERTIMEEND',                  0x12); //             [92] -- Timecode of the end of Chapter (timecode excluded, not scaled).
0160 define('EBML_ID_CUEREFTIME',                      0x16); //             [96] -- Timecode of the referenced Block.
0161 define('EBML_ID_CUEREFCLUSTER',                   0x17); //             [97] -- Position of the Cluster containing the referenced Block.
0162 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).
0163 define('EBML_ID_FLAGINTERLACED',                  0x1A); //             [9A] -- Set if the video is interlaced.
0164 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.
0165 define('EBML_ID_FLAGLACING',                      0x1C); //             [9C] -- Set if the track may contain blocks using lacing.
0166 define('EBML_ID_CHANNELS',                        0x1F); //             [9F] -- Numbers of channels in the track.
0167 define('EBML_ID_CLUSTERBLOCKGROUP',               0x20); //             [A0] -- Basic container of information containing a single Block or BlockVirtual, and information specific to that Block/VirtualBlock.
0168 define('EBML_ID_CLUSTERBLOCK',                    0x21); //             [A1] -- Block containing the actual data to be rendered and a timecode relative to the Cluster Timecode.
0169 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.
0170 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.
0171 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.
0172 define('EBML_ID_CLUSTERBLOCKADDITIONAL',          0x25); //             [A5] -- Interpreted by the codec as it wishes (using the BlockAddID).
0173 define('EBML_ID_CLUSTERBLOCKMORE',                0x26); //             [A6] -- Contain the BlockAdditional and some parameters.
0174 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.
0175 define('EBML_ID_CODECDECODEALL',                  0x2A); //             [AA] -- The codec can decode potentially damaged data.
0176 define('EBML_ID_CLUSTERPREVSIZE',                 0x2B); //             [AB] -- Size of the previous Cluster, in octets. Can be useful for backward playing.
0177 define('EBML_ID_TRACKENTRY',                      0x2E); //             [AE] -- Describes a track with all elements.
0178 define('EBML_ID_CLUSTERENCRYPTEDBLOCK',           0x2F); //             [AF] -- Similar to SimpleBlock but the data inside the Block are Transformed (encrypt and/or signed).
0179 define('EBML_ID_PIXELWIDTH',                      0x30); //             [B0] -- Width of the encoded video frames in pixels.
0180 define('EBML_ID_CUETIME',                         0x33); //             [B3] -- Absolute timecode according to the segment time base.
0181 define('EBML_ID_SAMPLINGFREQUENCY',               0x35); //             [B5] -- Sampling frequency in Hz.
0182 define('EBML_ID_CHAPTERATOM',                     0x36); //             [B6] -- Contains the atom information to use as the chapter atom (apply to all tracks).
0183 define('EBML_ID_CUETRACKPOSITIONS',               0x37); //             [B7] -- Contain positions for different tracks corresponding to the timecode.
0184 define('EBML_ID_FLAGENABLED',                     0x39); //             [B9] -- Set if the track is used.
0185 define('EBML_ID_PIXELHEIGHT',                     0x3A); //             [BA] -- Height of the encoded video frames in pixels.
0186 define('EBML_ID_CUEPOINT',                        0x3B); //             [BB] -- Contains all information relative to a seek point in the segment.
0187 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.
0188 define('EBML_ID_CLUSTERBLOCKADDITIONID',          0x4B); //             [CB] -- The ID of the BlockAdditional element (0 is the main Block).
0189 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.
0190 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).
0191 define('EBML_ID_CLUSTERDELAY',                    0x4E); //             [CE] -- The (scaled) delay to apply to the element.
0192 define('EBML_ID_CLUSTERDURATION',                 0x4F); //             [CF] -- The (scaled) duration to apply to the element.
0193 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).
0194 define('EBML_ID_CUEREFERENCE',                    0x5B); //             [DB] -- The Clusters containing the required referenced Blocks.
0195 define('EBML_ID_VIDEO',                           0x60); //             [E0] -- Video settings.
0196 define('EBML_ID_AUDIO',                           0x61); //             [E1] -- Audio settings.
0197 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.
0198 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.
0199 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.
0200 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.
0201 define('EBML_ID_CLUSTERTIMECODE',                 0x67); //             [E7] -- Absolute timecode of the cluster (based on TimecodeScale).
0202 define('EBML_ID_CLUSTERBLOCKADDID',               0x6E); //             [EE] -- An ID to identify the BlockAdditional level.
0203 define('EBML_ID_CUECLUSTERPOSITION',              0x71); //             [F1] -- The position of the Cluster containing the required Block.
0204 define('EBML_ID_CUETRACK',                        0x77); //             [F7] -- The track for which a position is given.
0205 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.
0206 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.
0207 define('EBML_ID_CLUSTERREFERENCEVIRTUAL',         0x7D); //             [FD] -- Relative position of the data that should be in position of the virtual block.
0208 
0209 
0210 /**
0211 * @tutorial http://www.matroska.org/technical/specs/index.html
0212 *
0213 * @todo Rewrite EBML parser to reduce it's size and honor default element values
0214 * @todo After rewrite implement stream size calculation, that will provide additional useful info and enable AAC/FLAC audio bitrate detection
0215 */
0216 class getid3_matroska extends getid3_handler
0217 {
0218   // public options
0219   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]
0220   public static $parse_whole_file = false; // true to parse the whole file, not only header [default: FALSE]
0221 
0222   // private parser settings/placeholders
0223   private $EBMLbuffer        = '';
0224   private $EBMLbuffer_offset = 0;
0225   private $EBMLbuffer_length = 0;
0226   private $current_offset    = 0;
0227   private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID);
0228 
0229   public function Analyze()
0230   {
0231     $info = &$this->getid3->info;
0232 
0233     // parse container
0234     try {
0235       $this->parseEBML($info);
0236     } catch (Exception $e) {
0237       $info['error'][] = 'EBML parser: '.$e->getMessage();
0238     }
0239 
0240     // calculate playtime
0241     if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
0242       foreach ($info['matroska']['info'] as $key => $infoarray) {
0243         if (isset($infoarray['Duration'])) {
0244           // TimecodeScale is how many nanoseconds each Duration unit is
0245           $info['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000);
0246           break;
0247         }
0248       }
0249     }
0250 
0251     // extract tags
0252     if (isset($info['matroska']['tags']) && is_array($info['matroska']['tags'])) {
0253       foreach ($info['matroska']['tags'] as $key => $infoarray) {
0254         $this->ExtractCommentsSimpleTag($infoarray);
0255       }
0256     }
0257 
0258     // process tracks
0259     if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
0260       foreach ($info['matroska']['tracks']['tracks'] as $key => $trackarray) {
0261 
0262         $track_info = array();
0263         $track_info['dataformat'] = self::CodecIDtoCommonName($trackarray['CodecID']);
0264         $track_info['default'] = (isset($trackarray['FlagDefault']) ? $trackarray['FlagDefault'] : true);
0265         if (isset($trackarray['Name'])) { $track_info['name'] = $trackarray['Name']; }
0266 
0267         switch ($trackarray['TrackType']) {
0268 
0269           case 1: // Video
0270             $track_info['resolution_x'] = $trackarray['PixelWidth'];
0271             $track_info['resolution_y'] = $trackarray['PixelHeight'];
0272             $track_info['display_unit'] = self::displayUnit(isset($trackarray['DisplayUnit']) ? $trackarray['DisplayUnit'] : 0);
0273             $track_info['display_x']    = (isset($trackarray['DisplayWidth']) ? $trackarray['DisplayWidth'] : $trackarray['PixelWidth']);
0274             $track_info['display_y']    = (isset($trackarray['DisplayHeight']) ? $trackarray['DisplayHeight'] : $trackarray['PixelHeight']);
0275 
0276             if (isset($trackarray['PixelCropBottom'])) { $track_info['crop_bottom'] = $trackarray['PixelCropBottom']; }
0277             if (isset($trackarray['PixelCropTop']))    { $track_info['crop_top']    = $trackarray['PixelCropTop']; }
0278             if (isset($trackarray['PixelCropLeft']))   { $track_info['crop_left']   = $trackarray['PixelCropLeft']; }
0279             if (isset($trackarray['PixelCropRight']))  { $track_info['crop_right']  = $trackarray['PixelCropRight']; }
0280             if (isset($trackarray['DefaultDuration'])) { $track_info['frame_rate']  = round(1000000000 / $trackarray['DefaultDuration'], 3); }
0281             if (isset($trackarray['CodecName']))       { $track_info['codec']       = $trackarray['CodecName']; }
0282 
0283             switch ($trackarray['CodecID']) {
0284               case 'V_MS/VFW/FOURCC':
0285                 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
0286 
0287                 $parsed = getid3_riff::ParseBITMAPINFOHEADER($trackarray['CodecPrivate']);
0288                 $track_info['codec'] = getid3_riff::fourccLookup($parsed['fourcc']);
0289                 $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
0290                 break;
0291 
0292               /*case 'V_MPEG4/ISO/AVC':
0293                 $h264['profile']    = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 1, 1));
0294                 $h264['level']      = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 3, 1));
0295                 $rn                 = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 4, 1));
0296                 $h264['NALUlength'] = ($rn & 3) + 1;
0297                 $rn                 = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 5, 1));
0298                 $nsps               = ($rn & 31);
0299                 $offset             = 6;
0300                 for ($i = 0; $i < $nsps; $i ++) {
0301                   $length        = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2));
0302                   $h264['SPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length);
0303                   $offset       += 2 + $length;
0304                 }
0305                 $npps               = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 1));
0306                 $offset            += 1;
0307                 for ($i = 0; $i < $npps; $i ++) {
0308                   $length        = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2));
0309                   $h264['PPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length);
0310                   $offset       += 2 + $length;
0311                 }
0312                 $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $h264;
0313                 break;*/
0314             }
0315 
0316             $info['video']['streams'][] = $track_info;
0317             break;
0318 
0319           case 2: // Audio
0320             $track_info['sample_rate'] = (isset($trackarray['SamplingFrequency']) ? $trackarray['SamplingFrequency'] : 8000.0);
0321             $track_info['channels']    = (isset($trackarray['Channels']) ? $trackarray['Channels'] : 1);
0322             $track_info['language']    = (isset($trackarray['Language']) ? $trackarray['Language'] : 'eng');
0323             if (isset($trackarray['BitDepth']))  { $track_info['bits_per_sample'] = $trackarray['BitDepth']; }
0324             if (isset($trackarray['CodecName'])) { $track_info['codec']           = $trackarray['CodecName']; }
0325 
0326             switch ($trackarray['CodecID']) {
0327               case 'A_PCM/INT/LIT':
0328               case 'A_PCM/INT/BIG':
0329                 $track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth'];
0330                 break;
0331 
0332               case 'A_AC3':
0333               case 'A_DTS':
0334               case 'A_MPEG/L3':
0335               case 'A_MPEG/L2':
0336               case 'A_FLAC':
0337                 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']).'.php', __FILE__, true);
0338 
0339                 if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) {
0340                   $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set');
0341                   break;
0342                 }
0343 
0344                 // create temp instance
0345                 $getid3_temp = new getID3();
0346                 if ($track_info['dataformat'] != 'flac') {
0347                   $getid3_temp->openfile($this->getid3->filename);
0348                 }
0349                 $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
0350                 if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') {
0351                   $getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length'];
0352                 }
0353 
0354                 // analyze
0355                 $class = 'getid3_'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']);
0356                 $header_data_key = $track_info['dataformat'][0] == 'm' ? 'mpeg' : $track_info['dataformat'];
0357                 $getid3_audio = new $class($getid3_temp, __CLASS__);
0358                 if ($track_info['dataformat'] == 'flac') {
0359                   $getid3_audio->AnalyzeString($trackarray['CodecPrivate']);
0360                 }
0361                 else {
0362                   $getid3_audio->Analyze();
0363                 }
0364                 if (!empty($getid3_temp->info[$header_data_key])) {
0365                   $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key];
0366                   if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
0367                     foreach ($getid3_temp->info['audio'] as $key => $value) {
0368                       $track_info[$key] = $value;
0369                     }
0370                   }
0371                 }
0372                 else {
0373                   $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because '.$class.'::Analyze() failed at offset '.$getid3_temp->info['avdataoffset']);
0374                 }
0375 
0376                 // copy errors and warnings
0377                 if (!empty($getid3_temp->info['error'])) {
0378                   foreach ($getid3_temp->info['error'] as $newerror) {
0379                     $this->warning($class.'() says: ['.$newerror.']');
0380                   }
0381                 }
0382                 if (!empty($getid3_temp->info['warning'])) {
0383                   foreach ($getid3_temp->info['warning'] as $newerror) {
0384                     $this->warning($class.'() says: ['.$newerror.']');
0385                   }
0386                 }
0387                 unset($getid3_temp, $getid3_audio);
0388                 break;
0389 
0390               case 'A_AAC':
0391               case 'A_AAC/MPEG2/LC':
0392               case 'A_AAC/MPEG2/LC/SBR':
0393               case 'A_AAC/MPEG4/LC':
0394               case 'A_AAC/MPEG4/LC/SBR':
0395                 $this->warning($trackarray['CodecID'].' audio data contains no header, audio/video bitrates can\'t be calculated');
0396                 break;
0397 
0398               case 'A_VORBIS':
0399                 if (!isset($trackarray['CodecPrivate'])) {
0400                   $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data not set');
0401                   break;
0402                 }
0403                 $vorbis_offset = strpos($trackarray['CodecPrivate'], 'vorbis', 1);
0404                 if ($vorbis_offset === false) {
0405                   $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data does not contain "vorbis" keyword');
0406                   break;
0407                 }
0408                 $vorbis_offset -= 1;
0409 
0410                 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
0411 
0412                 // create temp instance
0413                 $getid3_temp = new getID3();
0414 
0415                 // analyze
0416                 $getid3_ogg = new getid3_ogg($getid3_temp);
0417                 $oggpageinfo['page_seqno'] = 0;
0418                 $getid3_ogg->ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $oggpageinfo);
0419                 if (!empty($getid3_temp->info['ogg'])) {
0420                   $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg'];
0421                   if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
0422                     foreach ($getid3_temp->info['audio'] as $key => $value) {
0423                       $track_info[$key] = $value;
0424                     }
0425                   }
0426                 }
0427 
0428                 // copy errors and warnings
0429                 if (!empty($getid3_temp->info['error'])) {
0430                   foreach ($getid3_temp->info['error'] as $newerror) {
0431                     $this->warning('getid3_ogg() says: ['.$newerror.']');
0432                   }
0433                 }
0434                 if (!empty($getid3_temp->info['warning'])) {
0435                   foreach ($getid3_temp->info['warning'] as $newerror) {
0436                     $this->warning('getid3_ogg() says: ['.$newerror.']');
0437                   }
0438                 }
0439 
0440                 if (!empty($getid3_temp->info['ogg']['bitrate_nominal'])) {
0441                   $track_info['bitrate'] = $getid3_temp->info['ogg']['bitrate_nominal'];
0442                 }
0443                 unset($getid3_temp, $getid3_ogg, $oggpageinfo, $vorbis_offset);
0444                 break;
0445 
0446               case 'A_MS/ACM':
0447                 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
0448 
0449                 $parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']);
0450                 foreach ($parsed as $key => $value) {
0451                   if ($key != 'raw') {
0452                     $track_info[$key] = $value;
0453                   }
0454                 }
0455                 $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
0456                 break;
0457 
0458               default:
0459                 $this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"');
0460             }
0461 
0462             $info['audio']['streams'][] = $track_info;
0463             break;
0464         }
0465       }
0466 
0467       if (!empty($info['video']['streams'])) {
0468         $info['video'] = self::getDefaultStreamInfo($info['video']['streams']);
0469       }
0470       if (!empty($info['audio']['streams'])) {
0471         $info['audio'] = self::getDefaultStreamInfo($info['audio']['streams']);
0472       }
0473     }
0474 
0475     // process attachments
0476     if (isset($info['matroska']['attachments']) && $this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE) {
0477       foreach ($info['matroska']['attachments'] as $i => $entry) {
0478         if (strpos($entry['FileMimeType'], 'image/') === 0 && !empty($entry['FileData'])) {
0479           $info['matroska']['comments']['picture'][] = array('data' => $entry['FileData'], 'image_mime' => $entry['FileMimeType'], 'filename' => $entry['FileName']);
0480         }
0481       }
0482     }
0483 
0484     // determine mime type
0485     if (!empty($info['video']['streams'])) {
0486       $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'video/webm' : 'video/x-matroska');
0487     } elseif (!empty($info['audio']['streams'])) {
0488       $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'audio/webm' : 'audio/x-matroska');
0489     } elseif (isset($info['mime_type'])) {
0490       unset($info['mime_type']);
0491     }
0492 
0493     return true;
0494   }
0495 
0496   private function parseEBML(&$info) {
0497     // http://www.matroska.org/technical/specs/index.html#EBMLBasics
0498     $this->current_offset = $info['avdataoffset'];
0499 
0500     while ($this->getEBMLelement($top_element, $info['avdataend'])) {
0501       switch ($top_element['id']) {
0502 
0503         case EBML_ID_EBML:
0504           $info['matroska']['header']['offset'] = $top_element['offset'];
0505           $info['matroska']['header']['length'] = $top_element['length'];
0506 
0507           while ($this->getEBMLelement($element_data, $top_element['end'], true)) {
0508             switch ($element_data['id']) {
0509 
0510               case EBML_ID_EBMLVERSION:
0511               case EBML_ID_EBMLREADVERSION:
0512               case EBML_ID_EBMLMAXIDLENGTH:
0513               case EBML_ID_EBMLMAXSIZELENGTH:
0514               case EBML_ID_DOCTYPEVERSION:
0515               case EBML_ID_DOCTYPEREADVERSION:
0516                 $element_data['data'] = getid3_lib::BigEndian2Int($element_data['data']);
0517                 break;
0518 
0519               case EBML_ID_DOCTYPE:
0520                 $element_data['data'] = getid3_lib::trimNullByte($element_data['data']);
0521                 $info['matroska']['doctype'] = $element_data['data'];
0522                 $info['fileformat'] = $element_data['data'];
0523                 break;
0524 
0525               default:
0526                 $this->unhandledElement('header', __LINE__, $element_data);
0527             }
0528 
0529             unset($element_data['offset'], $element_data['end']);
0530             $info['matroska']['header']['elements'][] = $element_data;
0531           }
0532           break;
0533 
0534         case EBML_ID_SEGMENT:
0535           $info['matroska']['segment'][0]['offset'] = $top_element['offset'];
0536           $info['matroska']['segment'][0]['length'] = $top_element['length'];
0537 
0538           while ($this->getEBMLelement($element_data, $top_element['end'])) {
0539             if ($element_data['id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required
0540               $info['matroska']['segments'][] = $element_data;
0541             }
0542             switch ($element_data['id']) {
0543 
0544               case EBML_ID_SEEKHEAD: // Contains the position of other level 1 elements.
0545 
0546                 while ($this->getEBMLelement($seek_entry, $element_data['end'])) {
0547                   switch ($seek_entry['id']) {
0548 
0549                     case EBML_ID_SEEK: // Contains a single seek entry to an EBML element
0550                       while ($this->getEBMLelement($sub_seek_entry, $seek_entry['end'], true)) {
0551 
0552                         switch ($sub_seek_entry['id']) {
0553 
0554                           case EBML_ID_SEEKID:
0555                             $seek_entry['target_id']   = self::EBML2Int($sub_seek_entry['data']);
0556                             $seek_entry['target_name'] = self::EBMLidName($seek_entry['target_id']);
0557                             break;
0558 
0559                           case EBML_ID_SEEKPOSITION:
0560                             $seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($sub_seek_entry['data']);
0561                             break;
0562 
0563                           default:
0564                             $this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry);                        }
0565                       }
0566 
0567                       if ($seek_entry['target_id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required
0568                         $info['matroska']['seek'][] = $seek_entry;
0569                       }
0570                       break;
0571 
0572                     default:
0573                       $this->unhandledElement('seekhead', __LINE__, $seek_entry);
0574                   }
0575                 }
0576                 break;
0577 
0578               case EBML_ID_TRACKS: // A top-level block of information with many tracks described.
0579                 $info['matroska']['tracks'] = $element_data;
0580 
0581                 while ($this->getEBMLelement($track_entry, $element_data['end'])) {
0582                   switch ($track_entry['id']) {
0583 
0584                     case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements.
0585 
0586                       while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) {
0587                         switch ($subelement['id']) {
0588 
0589                           case EBML_ID_TRACKNUMBER:
0590                           case EBML_ID_TRACKUID:
0591                           case EBML_ID_TRACKTYPE:
0592                           case EBML_ID_MINCACHE:
0593                           case EBML_ID_MAXCACHE:
0594                           case EBML_ID_MAXBLOCKADDITIONID:
0595                           case EBML_ID_DEFAULTDURATION: // nanoseconds per frame
0596                             $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
0597                             break;
0598 
0599                           case EBML_ID_TRACKTIMECODESCALE:
0600                             $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']);
0601                             break;
0602 
0603                           case EBML_ID_CODECID:
0604                           case EBML_ID_LANGUAGE:
0605                           case EBML_ID_NAME:
0606                           case EBML_ID_CODECNAME:
0607                             $track_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
0608                             break;
0609 
0610                           case EBML_ID_CODECPRIVATE:
0611                             $track_entry[$subelement['id_name']] = $this->readEBMLelementData($subelement['length'], true);
0612                             break;
0613 
0614                           case EBML_ID_FLAGENABLED:
0615                           case EBML_ID_FLAGDEFAULT:
0616                           case EBML_ID_FLAGFORCED:
0617                           case EBML_ID_FLAGLACING:
0618                           case EBML_ID_CODECDECODEALL:
0619                             $track_entry[$subelement['id_name']] = (bool) getid3_lib::BigEndian2Int($subelement['data']);
0620                             break;
0621 
0622                           case EBML_ID_VIDEO:
0623 
0624                             while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
0625                               switch ($sub_subelement['id']) {
0626 
0627                                 case EBML_ID_PIXELWIDTH:
0628                                 case EBML_ID_PIXELHEIGHT:
0629                                 case EBML_ID_PIXELCROPBOTTOM:
0630                                 case EBML_ID_PIXELCROPTOP:
0631                                 case EBML_ID_PIXELCROPLEFT:
0632                                 case EBML_ID_PIXELCROPRIGHT:
0633                                 case EBML_ID_DISPLAYWIDTH:
0634                                 case EBML_ID_DISPLAYHEIGHT:
0635                                 case EBML_ID_DISPLAYUNIT:
0636                                 case EBML_ID_ASPECTRATIOTYPE:
0637                                 case EBML_ID_STEREOMODE:
0638                                 case EBML_ID_OLDSTEREOMODE:
0639                                   $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
0640                                   break;
0641 
0642                                 case EBML_ID_FLAGINTERLACED:
0643                                   $track_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']);
0644                                   break;
0645 
0646                                 case EBML_ID_GAMMAVALUE:
0647                                   $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']);
0648                                   break;
0649 
0650                                 case EBML_ID_COLOURSPACE:
0651                                   $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
0652                                   break;
0653 
0654                                 default:
0655                                   $this->unhandledElement('track.video', __LINE__, $sub_subelement);
0656                               }
0657                             }
0658                             break;
0659 
0660                           case EBML_ID_AUDIO:
0661 
0662                             while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
0663                               switch ($sub_subelement['id']) {
0664 
0665                                 case EBML_ID_CHANNELS:
0666                                 case EBML_ID_BITDEPTH:
0667                                   $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
0668                                   break;
0669 
0670                                 case EBML_ID_SAMPLINGFREQUENCY:
0671                                 case EBML_ID_OUTPUTSAMPLINGFREQUENCY:
0672                                   $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']);
0673                                   break;
0674 
0675                                 case EBML_ID_CHANNELPOSITIONS:
0676                                   $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
0677                                   break;
0678 
0679                                 default:
0680                                   $this->unhandledElement('track.audio', __LINE__, $sub_subelement);
0681                               }
0682                             }
0683                             break;
0684 
0685                           case EBML_ID_CONTENTENCODINGS:
0686 
0687                             while ($this->getEBMLelement($sub_subelement, $subelement['end'])) {
0688                               switch ($sub_subelement['id']) {
0689 
0690                                 case EBML_ID_CONTENTENCODING:
0691 
0692                                   while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CONTENTCOMPRESSION, EBML_ID_CONTENTENCRYPTION))) {
0693                                     switch ($sub_sub_subelement['id']) {
0694 
0695                                       case EBML_ID_CONTENTENCODINGORDER:
0696                                       case EBML_ID_CONTENTENCODINGSCOPE:
0697                                       case EBML_ID_CONTENTENCODINGTYPE:
0698                                         $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
0699                                         break;
0700 
0701                                       case EBML_ID_CONTENTCOMPRESSION:
0702 
0703                                         while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
0704                                           switch ($sub_sub_sub_subelement['id']) {
0705 
0706                                             case EBML_ID_CONTENTCOMPALGO:
0707                                               $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']);
0708                                               break;
0709 
0710                                             case EBML_ID_CONTENTCOMPSETTINGS:
0711                                               $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
0712                                               break;
0713 
0714                                             default:
0715                                               $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
0716                                           }
0717                                         }
0718                                         break;
0719 
0720                                       case EBML_ID_CONTENTENCRYPTION:
0721 
0722                                         while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
0723                                           switch ($sub_sub_sub_subelement['id']) {
0724 
0725                                             case EBML_ID_CONTENTENCALGO:
0726                                             case EBML_ID_CONTENTSIGALGO:
0727                                             case EBML_ID_CONTENTSIGHASHALGO:
0728                                               $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']);
0729                                               break;
0730 
0731                                             case EBML_ID_CONTENTENCKEYID:
0732                                             case EBML_ID_CONTENTSIGNATURE:
0733                                             case EBML_ID_CONTENTSIGKEYID:
0734                                               $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
0735                                               break;
0736 
0737                                             default:
0738                                               $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
0739                                           }
0740                                         }
0741                                         break;
0742 
0743                                       default:
0744                                         $this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement);
0745                                     }
0746                                   }
0747                                   break;
0748 
0749                                 default:
0750                                   $this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement);
0751                               }
0752                             }
0753                             break;
0754 
0755                           default:
0756                             $this->unhandledElement('track', __LINE__, $subelement);
0757                         }
0758                       }
0759 
0760                       $info['matroska']['tracks']['tracks'][] = $track_entry;
0761                       break;
0762 
0763                     default:
0764                       $this->unhandledElement('tracks', __LINE__, $track_entry);
0765                   }
0766                 }
0767                 break;
0768 
0769               case EBML_ID_INFO: // Contains miscellaneous general information and statistics on the file.
0770                 $info_entry = array();
0771 
0772                 while ($this->getEBMLelement($subelement, $element_data['end'], true)) {
0773                   switch ($subelement['id']) {
0774 
0775                     case EBML_ID_TIMECODESCALE:
0776                       $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
0777                       break;
0778 
0779                     case EBML_ID_DURATION:
0780                       $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']);
0781                       break;
0782 
0783                     case EBML_ID_DATEUTC:
0784                       $info_entry[$subelement['id_name']]         = getid3_lib::BigEndian2Int($subelement['data']);
0785                       $info_entry[$subelement['id_name'].'_unix'] = self::EBMLdate2unix($info_entry[$subelement['id_name']]);
0786                       break;
0787 
0788                     case EBML_ID_SEGMENTUID:
0789                     case EBML_ID_PREVUID:
0790                     case EBML_ID_NEXTUID:
0791                       $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
0792                       break;
0793 
0794                     case EBML_ID_SEGMENTFAMILY:
0795                       $info_entry[$subelement['id_name']][] = getid3_lib::trimNullByte($subelement['data']);
0796                       break;
0797 
0798                     case EBML_ID_SEGMENTFILENAME:
0799                     case EBML_ID_PREVFILENAME:
0800                     case EBML_ID_NEXTFILENAME:
0801                     case EBML_ID_TITLE:
0802                     case EBML_ID_MUXINGAPP:
0803                     case EBML_ID_WRITINGAPP:
0804                       $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
0805                       $info['matroska']['comments'][strtolower($subelement['id_name'])][] = $info_entry[$subelement['id_name']];
0806                       break;
0807 
0808                     case EBML_ID_CHAPTERTRANSLATE:
0809                       $chaptertranslate_entry = array();
0810 
0811                       while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
0812                         switch ($sub_subelement['id']) {
0813 
0814                           case EBML_ID_CHAPTERTRANSLATEEDITIONUID:
0815                             $chaptertranslate_entry[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data']);
0816                             break;
0817 
0818                           case EBML_ID_CHAPTERTRANSLATECODEC:
0819                             $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
0820                             break;
0821 
0822                           case EBML_ID_CHAPTERTRANSLATEID:
0823                             $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
0824                             break;
0825 
0826                           default:
0827                             $this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement);
0828                         }
0829                       }
0830                       $info_entry[$subelement['id_name']] = $chaptertranslate_entry;
0831                       break;
0832 
0833                     default:
0834                       $this->unhandledElement('info', __LINE__, $subelement);
0835                   }
0836                 }
0837                 $info['matroska']['info'][] = $info_entry;
0838                 break;
0839 
0840               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.
0841                 if (self::$hide_clusters) { // do not parse cues if hide clusters is "ON" till they point to clusters anyway
0842                   $this->current_offset = $element_data['end'];
0843                   break;
0844                 }
0845                 $cues_entry = array();
0846 
0847                 while ($this->getEBMLelement($subelement, $element_data['end'])) {
0848                   switch ($subelement['id']) {
0849 
0850                     case EBML_ID_CUEPOINT:
0851                       $cuepoint_entry = array();
0852 
0853                       while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CUETRACKPOSITIONS))) {
0854                         switch ($sub_subelement['id']) {
0855 
0856                           case EBML_ID_CUETRACKPOSITIONS:
0857                             $cuetrackpositions_entry = array();
0858 
0859                             while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
0860                               switch ($sub_sub_subelement['id']) {
0861 
0862                                 case EBML_ID_CUETRACK:
0863                                 case EBML_ID_CUECLUSTERPOSITION:
0864                                 case EBML_ID_CUEBLOCKNUMBER:
0865                                 case EBML_ID_CUECODECSTATE:
0866                                   $cuetrackpositions_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
0867                                   break;
0868 
0869                                 default:
0870                                   $this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement);
0871                               }
0872                             }
0873                             $cuepoint_entry[$sub_subelement['id_name']][] = $cuetrackpositions_entry;
0874                             break;
0875 
0876                           case EBML_ID_CUETIME:
0877                             $cuepoint_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
0878                             break;
0879 
0880                           default:
0881                             $this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement);
0882                         }
0883                       }
0884                       $cues_entry[] = $cuepoint_entry;
0885                       break;
0886 
0887                     default:
0888                       $this->unhandledElement('cues', __LINE__, $subelement);
0889                   }
0890                 }
0891                 $info['matroska']['cues'] = $cues_entry;
0892                 break;
0893 
0894               case EBML_ID_TAGS: // Element containing elements specific to Tracks/Chapters.
0895                 $tags_entry = array();
0896 
0897                 while ($this->getEBMLelement($subelement, $element_data['end'], false)) {
0898                   switch ($subelement['id']) {
0899 
0900                     case EBML_ID_TAG:
0901                       $tag_entry = array();
0902 
0903                       while ($this->getEBMLelement($sub_subelement, $subelement['end'], false)) {
0904                         switch ($sub_subelement['id']) {
0905 
0906                           case EBML_ID_TARGETS:
0907                             $targets_entry = array();
0908 
0909                             while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
0910                               switch ($sub_sub_subelement['id']) {
0911 
0912                                 case EBML_ID_TARGETTYPEVALUE:
0913                                   $targets_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
0914                                   $targets_entry[strtolower($sub_sub_subelement['id_name']).'_long'] = self::TargetTypeValue($targets_entry[$sub_sub_subelement['id_name']]);
0915                                   break;
0916 
0917                                 case EBML_ID_TARGETTYPE:
0918                                   $targets_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
0919                                   break;
0920 
0921                                 case EBML_ID_TAGTRACKUID:
0922                                 case EBML_ID_TAGEDITIONUID:
0923                                 case EBML_ID_TAGCHAPTERUID:
0924                                 case EBML_ID_TAGATTACHMENTUID:
0925                                   $targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
0926                                   break;
0927 
0928                                 default:
0929                                   $this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement);
0930                               }
0931                             }
0932                             $tag_entry[$sub_subelement['id_name']] = $targets_entry;
0933                             break;
0934 
0935                           case EBML_ID_SIMPLETAG:
0936                             $tag_entry[$sub_subelement['id_name']][] = $this->HandleEMBLSimpleTag($sub_subelement['end']);
0937                             break;
0938 
0939                           default:
0940                             $this->unhandledElement('tags.tag', __LINE__, $sub_subelement);
0941                         }
0942                       }
0943                       $tags_entry[] = $tag_entry;
0944                       break;
0945 
0946                     default:
0947                       $this->unhandledElement('tags', __LINE__, $subelement);
0948                   }
0949                 }
0950                 $info['matroska']['tags'] = $tags_entry;
0951                 break;
0952 
0953               case EBML_ID_ATTACHMENTS: // Contain attached files.
0954 
0955                 while ($this->getEBMLelement($subelement, $element_data['end'])) {
0956                   switch ($subelement['id']) {
0957 
0958                     case EBML_ID_ATTACHEDFILE:
0959                       $attachedfile_entry = array();
0960 
0961                       while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_FILEDATA))) {
0962                         switch ($sub_subelement['id']) {
0963 
0964                           case EBML_ID_FILEDESCRIPTION:
0965                           case EBML_ID_FILENAME:
0966                           case EBML_ID_FILEMIMETYPE:
0967                             $attachedfile_entry[$sub_subelement['id_name']] = $sub_subelement['data'];
0968                             break;
0969 
0970                           case EBML_ID_FILEDATA:
0971                             $attachedfile_entry['data_offset'] = $this->current_offset;
0972                             $attachedfile_entry['data_length'] = $sub_subelement['length'];
0973 
0974                             $attachedfile_entry[$sub_subelement['id_name']] = $this->saveAttachment(
0975                               $attachedfile_entry['FileName'],
0976                               $attachedfile_entry['data_offset'],
0977                               $attachedfile_entry['data_length']);
0978 
0979                             $this->current_offset = $sub_subelement['end'];
0980                             break;
0981 
0982                           case EBML_ID_FILEUID:
0983                             $attachedfile_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
0984                             break;
0985 
0986                           default:
0987                             $this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement);
0988                         }
0989                       }
0990                       $info['matroska']['attachments'][] = $attachedfile_entry;
0991                       break;
0992 
0993                     default:
0994                       $this->unhandledElement('attachments', __LINE__, $subelement);
0995                   }
0996                 }
0997                 break;
0998 
0999               case EBML_ID_CHAPTERS:
1000 
1001                 while ($this->getEBMLelement($subelement, $element_data['end'])) {
1002                   switch ($subelement['id']) {
1003 
1004                     case EBML_ID_EDITIONENTRY:
1005                       $editionentry_entry = array();
1006 
1007                       while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CHAPTERATOM))) {
1008                         switch ($sub_subelement['id']) {
1009 
1010                           case EBML_ID_EDITIONUID:
1011                             $editionentry_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1012                             break;
1013 
1014                           case EBML_ID_EDITIONFLAGHIDDEN:
1015                           case EBML_ID_EDITIONFLAGDEFAULT:
1016                           case EBML_ID_EDITIONFLAGORDERED:
1017                             $editionentry_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']);
1018                             break;
1019 
1020                           case EBML_ID_CHAPTERATOM:
1021                             $chapteratom_entry = array();
1022 
1023                             while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CHAPTERTRACK, EBML_ID_CHAPTERDISPLAY))) {
1024                               switch ($sub_sub_subelement['id']) {
1025 
1026                                 case EBML_ID_CHAPTERSEGMENTUID:
1027                                 case EBML_ID_CHAPTERSEGMENTEDITIONUID:
1028                                   $chapteratom_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
1029                                   break;
1030 
1031                                 case EBML_ID_CHAPTERFLAGENABLED:
1032                                 case EBML_ID_CHAPTERFLAGHIDDEN:
1033                                   $chapteratom_entry[$sub_sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
1034                                   break;
1035 
1036                                 case EBML_ID_CHAPTERUID:
1037                                 case EBML_ID_CHAPTERTIMESTART:
1038                                 case EBML_ID_CHAPTERTIMEEND:
1039                                   $chapteratom_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
1040                                   break;
1041 
1042                                 case EBML_ID_CHAPTERTRACK:
1043                                   $chaptertrack_entry = array();
1044 
1045                                   while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
1046                                     switch ($sub_sub_sub_subelement['id']) {
1047 
1048                                       case EBML_ID_CHAPTERTRACKNUMBER:
1049                                         $chaptertrack_entry[$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
1050                                         break;
1051 
1052                                       default:
1053                                         $this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement);
1054                                     }
1055                                   }
1056                                   $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chaptertrack_entry;
1057                                   break;
1058 
1059                                 case EBML_ID_CHAPTERDISPLAY:
1060                                   $chapterdisplay_entry = array();
1061 
1062                                   while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
1063                                     switch ($sub_sub_sub_subelement['id']) {
1064 
1065                                       case EBML_ID_CHAPSTRING:
1066                                       case EBML_ID_CHAPLANGUAGE:
1067                                       case EBML_ID_CHAPCOUNTRY:
1068                                         $chapterdisplay_entry[$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
1069                                         break;
1070 
1071                                       default:
1072                                         $this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement);
1073                                     }
1074                                   }
1075                                   $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chapterdisplay_entry;
1076                                   break;
1077 
1078                                 default:
1079                                   $this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement);
1080                               }
1081                             }
1082                             $editionentry_entry[$sub_subelement['id_name']][] = $chapteratom_entry;
1083                             break;
1084 
1085                           default:
1086                             $this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement);
1087                         }
1088                       }
1089                       $info['matroska']['chapters'][] = $editionentry_entry;
1090                       break;
1091 
1092                     default:
1093                       $this->unhandledElement('chapters', __LINE__, $subelement);
1094                   }
1095                 }
1096                 break;
1097 
1098               case EBML_ID_CLUSTER: // The lower level element containing the (monolithic) Block structure.
1099                 $cluster_entry = array();
1100 
1101                 while ($this->getEBMLelement($subelement, $element_data['end'], array(EBML_ID_CLUSTERSILENTTRACKS, EBML_ID_CLUSTERBLOCKGROUP, EBML_ID_CLUSTERSIMPLEBLOCK))) {
1102                   switch ($subelement['id']) {
1103 
1104                     case EBML_ID_CLUSTERTIMECODE:
1105                     case EBML_ID_CLUSTERPOSITION:
1106                     case EBML_ID_CLUSTERPREVSIZE:
1107                       $cluster_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
1108                       break;
1109 
1110                     case EBML_ID_CLUSTERSILENTTRACKS:
1111                       $cluster_silent_tracks = array();
1112 
1113                       while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
1114                         switch ($sub_subelement['id']) {
1115 
1116                           case EBML_ID_CLUSTERSILENTTRACKNUMBER:
1117                             $cluster_silent_tracks[] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1118                             break;
1119 
1120                           default:
1121                             $this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement);
1122                         }
1123                       }
1124                       $cluster_entry[$subelement['id_name']][] = $cluster_silent_tracks;
1125                       break;
1126 
1127                     case EBML_ID_CLUSTERBLOCKGROUP:
1128                       $cluster_block_group = array('offset' => $this->current_offset);
1129 
1130                       while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CLUSTERBLOCK))) {
1131                         switch ($sub_subelement['id']) {
1132 
1133                           case EBML_ID_CLUSTERBLOCK:
1134                             $cluster_block_group[$sub_subelement['id_name']] = $this->HandleEMBLClusterBlock($sub_subelement, EBML_ID_CLUSTERBLOCK, $info);
1135                             break;
1136 
1137                           case EBML_ID_CLUSTERREFERENCEPRIORITY: // unsigned-int
1138                           case EBML_ID_CLUSTERBLOCKDURATION:     // unsigned-int
1139                             $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1140                             break;
1141 
1142                           case EBML_ID_CLUSTERREFERENCEBLOCK:    // signed-int
1143                             $cluster_block_group[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data'], false, true);
1144                             break;
1145 
1146                           case EBML_ID_CLUSTERCODECSTATE:
1147                             $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
1148                             break;
1149 
1150                           default:
1151                             $this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement);
1152                         }
1153                       }
1154                       $cluster_entry[$subelement['id_name']][] = $cluster_block_group;
1155                       break;
1156 
1157                     case EBML_ID_CLUSTERSIMPLEBLOCK:
1158                       $cluster_entry[$subelement['id_name']][] = $this->HandleEMBLClusterBlock($subelement, EBML_ID_CLUSTERSIMPLEBLOCK, $info);
1159                       break;
1160 
1161                     default:
1162                       $this->unhandledElement('cluster', __LINE__, $subelement);
1163                   }
1164                   $this->current_offset = $subelement['end'];
1165                 }
1166                 if (!self::$hide_clusters) {
1167                   $info['matroska']['cluster'][] = $cluster_entry;
1168                 }
1169 
1170                 // check to see if all the data we need exists already, if so, break out of the loop
1171                 if (!self::$parse_whole_file) {
1172                   if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
1173                     if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
1174                       if (count($info['matroska']['track_data_offsets']) == count($info['matroska']['tracks']['tracks'])) {
1175                         return;
1176                       }
1177                     }
1178                   }
1179                 }
1180                 break;
1181 
1182               default:
1183                 $this->unhandledElement('segment', __LINE__, $element_data);
1184             }
1185           }
1186           break;
1187 
1188         default:
1189           $this->unhandledElement('root', __LINE__, $top_element);
1190       }
1191     }
1192   }
1193 
1194   private function EnsureBufferHasEnoughData($min_data=1024) {
1195     if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) {
1196       $read_bytes = max($min_data, $this->getid3->fread_buffer_size());
1197 
1198       try {
1199         $this->fseek($this->current_offset);
1200         $this->EBMLbuffer_offset = $this->current_offset;
1201         $this->EBMLbuffer        = $this->fread($read_bytes);
1202         $this->EBMLbuffer_length = strlen($this->EBMLbuffer);
1203       } catch (getid3_exception $e) {
1204         $this->warning('EBML parser: '.$e->getMessage());
1205         return false;
1206       }
1207 
1208       if ($this->EBMLbuffer_length == 0 && $this->feof()) {
1209         return $this->error('EBML parser: ran out of file at offset '.$this->current_offset);
1210       }
1211     }
1212     return true;
1213   }
1214 
1215   private function readEBMLint() {
1216     $actual_offset = $this->current_offset - $this->EBMLbuffer_offset;
1217 
1218     // get length of integer
1219     $first_byte_int = ord($this->EBMLbuffer[$actual_offset]);
1220     if       (0x80 & $first_byte_int) {
1221       $length = 1;
1222     } elseif (0x40 & $first_byte_int) {
1223       $length = 2;
1224     } elseif (0x20 & $first_byte_int) {
1225       $length = 3;
1226     } elseif (0x10 & $first_byte_int) {
1227       $length = 4;
1228     } elseif (0x08 & $first_byte_int) {
1229       $length = 5;
1230     } elseif (0x04 & $first_byte_int) {
1231       $length = 6;
1232     } elseif (0x02 & $first_byte_int) {
1233       $length = 7;
1234     } elseif (0x01 & $first_byte_int) {
1235       $length = 8;
1236     } else {
1237       throw new Exception('invalid EBML integer (leading 0x00) at '.$this->current_offset);
1238     }
1239 
1240     // read
1241     $int_value = self::EBML2Int(substr($this->EBMLbuffer, $actual_offset, $length));
1242     $this->current_offset += $length;
1243 
1244     return $int_value;
1245   }
1246 
1247   private function readEBMLelementData($length, $check_buffer=false) {
1248     if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) {
1249       return false;
1250     }
1251     $data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length);
1252     $this->current_offset += $length;
1253     return $data;
1254   }
1255 
1256   private function getEBMLelement(&$element, $parent_end, $get_data=false) {
1257     if ($this->current_offset >= $parent_end) {
1258       return false;
1259     }
1260 
1261     if (!$this->EnsureBufferHasEnoughData()) {
1262       $this->current_offset = PHP_INT_MAX; // do not exit parser right now, allow to finish current loop to gather maximum information
1263       return false;
1264     }
1265 
1266     $element = array();
1267 
1268     // set offset
1269     $element['offset'] = $this->current_offset;
1270 
1271     // get ID
1272     $element['id'] = $this->readEBMLint();
1273 
1274     // get name
1275     $element['id_name'] = self::EBMLidName($element['id']);
1276 
1277     // get length
1278     $element['length'] = $this->readEBMLint();
1279 
1280     // get end offset
1281     $element['end'] = $this->current_offset + $element['length'];
1282 
1283     // get raw data
1284     $dont_parse = (in_array($element['id'], $this->unuseful_elements) || $element['id_name'] == dechex($element['id']));
1285     if (($get_data === true || (is_array($get_data) && !in_array($element['id'], $get_data))) && !$dont_parse) {
1286       $element['data'] = $this->readEBMLelementData($element['length'], $element);
1287     }
1288 
1289     return true;
1290   }
1291 
1292   private function unhandledElement($type, $line, $element) {
1293     // warn only about unknown and missed elements, not about unuseful
1294     if (!in_array($element['id'], $this->unuseful_elements)) {
1295       $this->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']);
1296     }
1297 
1298     // increase offset for unparsed elements
1299     if (!isset($element['data'])) {
1300       $this->current_offset = $element['end'];
1301     }
1302   }
1303 
1304   private function ExtractCommentsSimpleTag($SimpleTagArray) {
1305     if (!empty($SimpleTagArray['SimpleTag'])) {
1306       foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) {
1307         if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) {
1308           $this->getid3->info['matroska']['comments'][strtolower($SimpleTagData['TagName'])][] = $SimpleTagData['TagString'];
1309         }
1310         if (!empty($SimpleTagData['SimpleTag'])) {
1311           $this->ExtractCommentsSimpleTag($SimpleTagData);
1312         }
1313       }
1314     }
1315 
1316     return true;
1317   }
1318 
1319   private function HandleEMBLSimpleTag($parent_end) {
1320     $simpletag_entry = array();
1321 
1322     while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) {
1323       switch ($element['id']) {
1324 
1325         case EBML_ID_TAGNAME:
1326         case EBML_ID_TAGLANGUAGE:
1327         case EBML_ID_TAGSTRING:
1328         case EBML_ID_TAGBINARY:
1329           $simpletag_entry[$element['id_name']] = $element['data'];
1330           break;
1331 
1332         case EBML_ID_SIMPLETAG:
1333           $simpletag_entry[$element['id_name']][] = $this->HandleEMBLSimpleTag($element['end']);
1334           break;
1335 
1336         case EBML_ID_TAGDEFAULT:
1337           $simpletag_entry[$element['id_name']] = (bool)getid3_lib::BigEndian2Int($element['data']);
1338           break;
1339 
1340         default:
1341           $this->unhandledElement('tag.simpletag', __LINE__, $element);
1342       }
1343     }
1344 
1345     return $simpletag_entry;
1346   }
1347 
1348   private function HandleEMBLClusterBlock($element, $block_type, &$info) {
1349     // http://www.matroska.org/technical/specs/index.html#block_structure
1350     // http://www.matroska.org/technical/specs/index.html#simpleblock_structure
1351 
1352     $block_data = array();
1353     $block_data['tracknumber'] = $this->readEBMLint();
1354     $block_data['timecode']    = getid3_lib::BigEndian2Int($this->readEBMLelementData(2), false, true);
1355     $block_data['flags_raw']   = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
1356 
1357     if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
1358       $block_data['flags']['keyframe']  = (($block_data['flags_raw'] & 0x80) >> 7);
1359       //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0x70) >> 4);
1360     }
1361     else {
1362       //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0xF0) >> 4);
1363     }
1364     $block_data['flags']['invisible'] = (bool)(($block_data['flags_raw'] & 0x08) >> 3);
1365     $block_data['flags']['lacing']    =       (($block_data['flags_raw'] & 0x06) >> 1);  // 00=no lacing; 01=Xiph lacing; 11=EBML lacing; 10=fixed-size lacing
1366     if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
1367       $block_data['flags']['discardable'] = (($block_data['flags_raw'] & 0x01));
1368     }
1369     else {
1370       //$block_data['flags']['reserved2'] = (($block_data['flags_raw'] & 0x01) >> 0);
1371     }
1372     $block_data['flags']['lacing_type'] = self::BlockLacingType($block_data['flags']['lacing']);
1373 
1374     // Lace (when lacing bit is set)
1375     if ($block_data['flags']['lacing'] > 0) {
1376       $block_data['lace_frames'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)) + 1; // Number of frames in the lace-1 (uint8)
1377       if ($block_data['flags']['lacing'] != 0x02) {
1378         for ($i = 1; $i < $block_data['lace_frames']; $i ++) { // Lace-coded size of each frame of the lace, except for the last one (multiple uint8). *This is not used with Fixed-size lacing as it is calculated automatically from (total size of lace) / (number of frames in lace).
1379           if ($block_data['flags']['lacing'] == 0x03) { // EBML lacing
1380             $block_data['lace_frames_size'][$i] = $this->readEBMLint(); // TODO: read size correctly, calc size for the last frame. For now offsets are deteminded OK with readEBMLint() and that's the most important thing.
1381           }
1382           else { // Xiph lacing
1383             $block_data['lace_frames_size'][$i] = 0;
1384             do {
1385               $size = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
1386               $block_data['lace_frames_size'][$i] += $size;
1387             }
1388             while ($size == 255);
1389           }
1390         }
1391         if ($block_data['flags']['lacing'] == 0x01) { // calc size of the last frame only for Xiph lacing, till EBML sizes are now anyway determined incorrectly
1392           $block_data['lace_frames_size'][] = $element['end'] - $this->current_offset - array_sum($block_data['lace_frames_size']);
1393         }
1394       }
1395     }
1396 
1397     if (!isset($info['matroska']['track_data_offsets'][$block_data['tracknumber']])) {
1398       $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['offset'] = $this->current_offset;
1399       $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'] = $element['end'] - $this->current_offset;
1400       //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] = 0;
1401     }
1402     //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] += $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'];
1403     //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['duration']      = $block_data['timecode'] * ((isset($info['matroska']['info'][0]['TimecodeScale']) ? $info['matroska']['info'][0]['TimecodeScale'] : 1000000) / 1000000000);
1404 
1405     // set offset manually
1406     $this->current_offset = $element['end'];
1407 
1408     return $block_data;
1409   }
1410 
1411   private static function EBML2Int($EBMLstring) {
1412     // http://matroska.org/specs/
1413 
1414     // Element ID coded with an UTF-8 like system:
1415     // 1xxx xxxx                                  - Class A IDs (2^7 -2 possible values) (base 0x8X)
1416     // 01xx xxxx  xxxx xxxx                       - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX)
1417     // 001x xxxx  xxxx xxxx  xxxx xxxx            - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX)
1418     // 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX)
1419     // Values with all x at 0 and 1 are reserved (hence the -2).
1420 
1421     // Data size, in octets, is also coded with an UTF-8 like system :
1422     // 1xxx xxxx                                                                              - value 0 to  2^7-2
1423     // 01xx xxxx  xxxx xxxx                                                                   - value 0 to 2^14-2
1424     // 001x xxxx  xxxx xxxx  xxxx xxxx                                                        - value 0 to 2^21-2
1425     // 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                             - value 0 to 2^28-2
1426     // 0000 1xxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                  - value 0 to 2^35-2
1427     // 0000 01xx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                       - value 0 to 2^42-2
1428     // 0000 001x  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx            - value 0 to 2^49-2
1429     // 0000 0001  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - value 0 to 2^56-2
1430 
1431     $first_byte_int = ord($EBMLstring[0]);
1432     if (0x80 & $first_byte_int) {
1433       $EBMLstring[0] = chr($first_byte_int & 0x7F);
1434     } elseif (0x40 & $first_byte_int) {
1435       $EBMLstring[0] = chr($first_byte_int & 0x3F);
1436     } elseif (0x20 & $first_byte_int) {
1437       $EBMLstring[0] = chr($first_byte_int & 0x1F);
1438     } elseif (0x10 & $first_byte_int) {
1439       $EBMLstring[0] = chr($first_byte_int & 0x0F);
1440     } elseif (0x08 & $first_byte_int) {
1441       $EBMLstring[0] = chr($first_byte_int & 0x07);
1442     } elseif (0x04 & $first_byte_int) {
1443       $EBMLstring[0] = chr($first_byte_int & 0x03);
1444     } elseif (0x02 & $first_byte_int) {
1445       $EBMLstring[0] = chr($first_byte_int & 0x01);
1446     } elseif (0x01 & $first_byte_int) {
1447       $EBMLstring[0] = chr($first_byte_int & 0x00);
1448     }
1449 
1450     return getid3_lib::BigEndian2Int($EBMLstring);
1451   }
1452 
1453   private static function EBMLdate2unix($EBMLdatestamp) {
1454     // Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC)
1455     // 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC
1456     return round(($EBMLdatestamp / 1000000000) + 978307200);
1457   }
1458 
1459   public static function TargetTypeValue($target_type) {
1460     // http://www.matroska.org/technical/specs/tagging/index.html
1461     static $TargetTypeValue = array();
1462     if (empty($TargetTypeValue)) {
1463       $TargetTypeValue[10] = 'A: ~ V:shot';                                           // the lowest hierarchy found in music or movies
1464       $TargetTypeValue[20] = 'A:subtrack/part/movement ~ V:scene';                    // corresponds to parts of a track for audio (like a movement)
1465       $TargetTypeValue[30] = 'A:track/song ~ V:chapter';                              // the common parts of an album or a movie
1466       $TargetTypeValue[40] = 'A:part/session ~ V:part/session';                       // when an album or episode has different logical parts
1467       $TargetTypeValue[50] = 'A:album/opera/concert ~ V:movie/episode/concert';       // the most common grouping level of music and video (equals to an episode for TV series)
1468       $TargetTypeValue[60] = 'A:edition/issue/volume/opus ~ V:season/sequel/volume';  // a list of lower levels grouped together
1469       $TargetTypeValue[70] = 'A:collection ~ V:collection';                           // the high hierarchy consisting of many different lower items
1470     }
1471     return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type);
1472   }
1473 
1474   public static function BlockLacingType($lacingtype) {
1475     // http://matroska.org/technical/specs/index.html#block_structure
1476     static $BlockLacingType = array();
1477     if (empty($BlockLacingType)) {
1478       $BlockLacingType[0x00] = 'no lacing';
1479       $BlockLacingType[0x01] = 'Xiph lacing';
1480       $BlockLacingType[0x02] = 'fixed-size lacing';
1481       $BlockLacingType[0x03] = 'EBML lacing';
1482     }
1483     return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype);
1484   }
1485 
1486   public static function CodecIDtoCommonName($codecid) {
1487     // http://www.matroska.org/technical/specs/codecid/index.html
1488     static $CodecIDlist = array();
1489     if (empty($CodecIDlist)) {
1490       $CodecIDlist['A_AAC']            = 'aac';
1491       $CodecIDlist['A_AAC/MPEG2/LC']   = 'aac';
1492       $CodecIDlist['A_AC3']            = 'ac3';
1493       $CodecIDlist['A_DTS']            = 'dts';
1494       $CodecIDlist['A_FLAC']           = 'flac';
1495       $CodecIDlist['A_MPEG/L1']        = 'mp1';
1496       $CodecIDlist['A_MPEG/L2']        = 'mp2';
1497       $CodecIDlist['A_MPEG/L3']        = 'mp3';
1498       $CodecIDlist['A_PCM/INT/LIT']    = 'pcm';       // PCM Integer Little Endian
1499       $CodecIDlist['A_PCM/INT/BIG']    = 'pcm';       // PCM Integer Big Endian
1500       $CodecIDlist['A_QUICKTIME/QDMC'] = 'quicktime'; // Quicktime: QDesign Music
1501       $CodecIDlist['A_QUICKTIME/QDM2'] = 'quicktime'; // Quicktime: QDesign Music v2
1502       $CodecIDlist['A_VORBIS']         = 'vorbis';
1503       $CodecIDlist['V_MPEG1']          = 'mpeg';
1504       $CodecIDlist['V_THEORA']         = 'theora';
1505       $CodecIDlist['V_REAL/RV40']      = 'real';
1506       $CodecIDlist['V_REAL/RV10']      = 'real';
1507       $CodecIDlist['V_REAL/RV20']      = 'real';
1508       $CodecIDlist['V_REAL/RV30']      = 'real';
1509       $CodecIDlist['V_QUICKTIME']      = 'quicktime'; // Quicktime
1510       $CodecIDlist['V_MPEG4/ISO/AP']   = 'mpeg4';
1511       $CodecIDlist['V_MPEG4/ISO/ASP']  = 'mpeg4';
1512       $CodecIDlist['V_MPEG4/ISO/AVC']  = 'h264';
1513       $CodecIDlist['V_MPEG4/ISO/SP']   = 'mpeg4';
1514       $CodecIDlist['V_VP8']            = 'vp8';
1515       $CodecIDlist['V_MS/VFW/FOURCC']  = 'vcm'; // Microsoft (TM) Video Codec Manager (VCM)
1516       $CodecIDlist['A_MS/ACM']         = 'acm'; // Microsoft (TM) Audio Codec Manager (ACM)
1517     }
1518     return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid);
1519   }
1520 
1521   private static function EBMLidName($value) {
1522     static $EBMLidList = array();
1523     if (empty($EBMLidList)) {
1524       $EBMLidList[EBML_ID_ASPECTRATIOTYPE]            = 'AspectRatioType';
1525       $EBMLidList[EBML_ID_ATTACHEDFILE]               = 'AttachedFile';
1526       $EBMLidList[EBML_ID_ATTACHMENTLINK]             = 'AttachmentLink';
1527       $EBMLidList[EBML_ID_ATTACHMENTS]                = 'Attachments';
1528       $EBMLidList[EBML_ID_AUDIO]                      = 'Audio';
1529       $EBMLidList[EBML_ID_BITDEPTH]                   = 'BitDepth';
1530       $EBMLidList[EBML_ID_CHANNELPOSITIONS]           = 'ChannelPositions';
1531       $EBMLidList[EBML_ID_CHANNELS]                   = 'Channels';
1532       $EBMLidList[EBML_ID_CHAPCOUNTRY]                = 'ChapCountry';
1533       $EBMLidList[EBML_ID_CHAPLANGUAGE]               = 'ChapLanguage';
1534       $EBMLidList[EBML_ID_CHAPPROCESS]                = 'ChapProcess';
1535       $EBMLidList[EBML_ID_CHAPPROCESSCODECID]         = 'ChapProcessCodecID';
1536       $EBMLidList[EBML_ID_CHAPPROCESSCOMMAND]         = 'ChapProcessCommand';
1537       $EBMLidList[EBML_ID_CHAPPROCESSDATA]            = 'ChapProcessData';
1538       $EBMLidList[EBML_ID_CHAPPROCESSPRIVATE]         = 'ChapProcessPrivate';
1539       $EBMLidList[EBML_ID_CHAPPROCESSTIME]            = 'ChapProcessTime';
1540       $EBMLidList[EBML_ID_CHAPSTRING]                 = 'ChapString';
1541       $EBMLidList[EBML_ID_CHAPTERATOM]                = 'ChapterAtom';
1542       $EBMLidList[EBML_ID_CHAPTERDISPLAY]             = 'ChapterDisplay';
1543       $EBMLidList[EBML_ID_CHAPTERFLAGENABLED]         = 'ChapterFlagEnabled';
1544       $EBMLidList[EBML_ID_CHAPTERFLAGHIDDEN]          = 'ChapterFlagHidden';
1545       $EBMLidList[EBML_ID_CHAPTERPHYSICALEQUIV]       = 'ChapterPhysicalEquiv';
1546       $EBMLidList[EBML_ID_CHAPTERS]                   = 'Chapters';
1547       $EBMLidList[EBML_ID_CHAPTERSEGMENTEDITIONUID]   = 'ChapterSegmentEditionUID';
1548       $EBMLidList[EBML_ID_CHAPTERSEGMENTUID]          = 'ChapterSegmentUID';
1549       $EBMLidList[EBML_ID_CHAPTERTIMEEND]             = 'ChapterTimeEnd';
1550       $EBMLidList[EBML_ID_CHAPTERTIMESTART]           = 'ChapterTimeStart';
1551       $EBMLidList[EBML_ID_CHAPTERTRACK]               = 'ChapterTrack';
1552       $EBMLidList[EBML_ID_CHAPTERTRACKNUMBER]         = 'ChapterTrackNumber';
1553       $EBMLidList[EBML_ID_CHAPTERTRANSLATE]           = 'ChapterTranslate';
1554       $EBMLidList[EBML_ID_CHAPTERTRANSLATECODEC]      = 'ChapterTranslateCodec';
1555       $EBMLidList[EBML_ID_CHAPTERTRANSLATEEDITIONUID] = 'ChapterTranslateEditionUID';
1556       $EBMLidList[EBML_ID_CHAPTERTRANSLATEID]         = 'ChapterTranslateID';
1557       $EBMLidList[EBML_ID_CHAPTERUID]                 = 'ChapterUID';
1558       $EBMLidList[EBML_ID_CLUSTER]                    = 'Cluster';
1559       $EBMLidList[EBML_ID_CLUSTERBLOCK]               = 'ClusterBlock';
1560       $EBMLidList[EBML_ID_CLUSTERBLOCKADDID]          = 'ClusterBlockAddID';
1561       $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONAL]     = 'ClusterBlockAdditional';
1562       $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONID]     = 'ClusterBlockAdditionID';
1563       $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONS]      = 'ClusterBlockAdditions';
1564       $EBMLidList[EBML_ID_CLUSTERBLOCKDURATION]       = 'ClusterBlockDuration';
1565       $EBMLidList[EBML_ID_CLUSTERBLOCKGROUP]          = 'ClusterBlockGroup';
1566       $EBMLidList[EBML_ID_CLUSTERBLOCKMORE]           = 'ClusterBlockMore';
1567       $EBMLidList[EBML_ID_CLUSTERBLOCKVIRTUAL]        = 'ClusterBlockVirtual';
1568       $EBMLidList[EBML_ID_CLUSTERCODECSTATE]          = 'ClusterCodecState';
1569       $EBMLidList[EBML_ID_CLUSTERDELAY]               = 'ClusterDelay';
1570       $EBMLidList[EBML_ID_CLUSTERDURATION]            = 'ClusterDuration';
1571       $EBMLidList[EBML_ID_CLUSTERENCRYPTEDBLOCK]      = 'ClusterEncryptedBlock';
1572       $EBMLidList[EBML_ID_CLUSTERFRAMENUMBER]         = 'ClusterFrameNumber';
1573       $EBMLidList[EBML_ID_CLUSTERLACENUMBER]          = 'ClusterLaceNumber';
1574       $EBMLidList[EBML_ID_CLUSTERPOSITION]            = 'ClusterPosition';
1575       $EBMLidList[EBML_ID_CLUSTERPREVSIZE]            = 'ClusterPrevSize';
1576       $EBMLidList[EBML_ID_CLUSTERREFERENCEBLOCK]      = 'ClusterReferenceBlock';
1577       $EBMLidList[EBML_ID_CLUSTERREFERENCEPRIORITY]   = 'ClusterReferencePriority';
1578       $EBMLidList[EBML_ID_CLUSTERREFERENCEVIRTUAL]    = 'ClusterReferenceVirtual';
1579       $EBMLidList[EBML_ID_CLUSTERSILENTTRACKNUMBER]   = 'ClusterSilentTrackNumber';
1580       $EBMLidList[EBML_ID_CLUSTERSILENTTRACKS]        = 'ClusterSilentTracks';
1581       $EBMLidList[EBML_ID_CLUSTERSIMPLEBLOCK]         = 'ClusterSimpleBlock';
1582       $EBMLidList[EBML_ID_CLUSTERTIMECODE]            = 'ClusterTimecode';
1583       $EBMLidList[EBML_ID_CLUSTERTIMESLICE]           = 'ClusterTimeSlice';
1584       $EBMLidList[EBML_ID_CODECDECODEALL]             = 'CodecDecodeAll';
1585       $EBMLidList[EBML_ID_CODECDOWNLOADURL]           = 'CodecDownloadURL';
1586       $EBMLidList[EBML_ID_CODECID]                    = 'CodecID';
1587       $EBMLidList[EBML_ID_CODECINFOURL]               = 'CodecInfoURL';
1588       $EBMLidList[EBML_ID_CODECNAME]                  = 'CodecName';
1589       $EBMLidList[EBML_ID_CODECPRIVATE]               = 'CodecPrivate';
1590       $EBMLidList[EBML_ID_CODECSETTINGS]              = 'CodecSettings';
1591       $EBMLidList[EBML_ID_COLOURSPACE]                = 'ColourSpace';
1592       $EBMLidList[EBML_ID_CONTENTCOMPALGO]            = 'ContentCompAlgo';
1593       $EBMLidList[EBML_ID_CONTENTCOMPRESSION]         = 'ContentCompression';
1594       $EBMLidList[EBML_ID_CONTENTCOMPSETTINGS]        = 'ContentCompSettings';
1595       $EBMLidList[EBML_ID_CONTENTENCALGO]             = 'ContentEncAlgo';
1596       $EBMLidList[EBML_ID_CONTENTENCKEYID]            = 'ContentEncKeyID';
1597       $EBMLidList[EBML_ID_CONTENTENCODING]            = 'ContentEncoding';
1598       $EBMLidList[EBML_ID_CONTENTENCODINGORDER]       = 'ContentEncodingOrder';
1599       $EBMLidList[EBML_ID_CONTENTENCODINGS]           = 'ContentEncodings';
1600       $EBMLidList[EBML_ID_CONTENTENCODINGSCOPE]       = 'ContentEncodingScope';
1601       $EBMLidList[EBML_ID_CONTENTENCODINGTYPE]        = 'ContentEncodingType';
1602       $EBMLidList[EBML_ID_CONTENTENCRYPTION]          = 'ContentEncryption';
1603       $EBMLidList[EBML_ID_CONTENTSIGALGO]             = 'ContentSigAlgo';
1604       $EBMLidList[EBML_ID_CONTENTSIGHASHALGO]         = 'ContentSigHashAlgo';
1605       $EBMLidList[EBML_ID_CONTENTSIGKEYID]            = 'ContentSigKeyID';
1606       $EBMLidList[EBML_ID_CONTENTSIGNATURE]           = 'ContentSignature';
1607       $EBMLidList[EBML_ID_CRC32]                      = 'CRC32';
1608       $EBMLidList[EBML_ID_CUEBLOCKNUMBER]             = 'CueBlockNumber';
1609       $EBMLidList[EBML_ID_CUECLUSTERPOSITION]         = 'CueClusterPosition';
1610       $EBMLidList[EBML_ID_CUECODECSTATE]              = 'CueCodecState';
1611       $EBMLidList[EBML_ID_CUEPOINT]                   = 'CuePoint';
1612       $EBMLidList[EBML_ID_CUEREFCLUSTER]              = 'CueRefCluster';
1613       $EBMLidList[EBML_ID_CUEREFCODECSTATE]           = 'CueRefCodecState';
1614       $EBMLidList[EBML_ID_CUEREFERENCE]               = 'CueReference';
1615       $EBMLidList[EBML_ID_CUEREFNUMBER]               = 'CueRefNumber';
1616       $EBMLidList[EBML_ID_CUEREFTIME]                 = 'CueRefTime';
1617       $EBMLidList[EBML_ID_CUES]                       = 'Cues';
1618       $EBMLidList[EBML_ID_CUETIME]                    = 'CueTime';
1619       $EBMLidList[EBML_ID_CUETRACK]                   = 'CueTrack';
1620       $EBMLidList[EBML_ID_CUETRACKPOSITIONS]          = 'CueTrackPositions';
1621       $EBMLidList[EBML_ID_DATEUTC]                    = 'DateUTC';
1622       $EBMLidList[EBML_ID_DEFAULTDURATION]            = 'DefaultDuration';
1623       $EBMLidList[EBML_ID_DISPLAYHEIGHT]              = 'DisplayHeight';
1624       $EBMLidList[EBML_ID_DISPLAYUNIT]                = 'DisplayUnit';
1625       $EBMLidList[EBML_ID_DISPLAYWIDTH]               = 'DisplayWidth';
1626       $EBMLidList[EBML_ID_DOCTYPE]                    = 'DocType';
1627       $EBMLidList[EBML_ID_DOCTYPEREADVERSION]         = 'DocTypeReadVersion';
1628       $EBMLidList[EBML_ID_DOCTYPEVERSION]             = 'DocTypeVersion';
1629       $EBMLidList[EBML_ID_DURATION]                   = 'Duration';
1630       $EBMLidList[EBML_ID_EBML]                       = 'EBML';
1631       $EBMLidList[EBML_ID_EBMLMAXIDLENGTH]            = 'EBMLMaxIDLength';
1632       $EBMLidList[EBML_ID_EBMLMAXSIZELENGTH]          = 'EBMLMaxSizeLength';
1633       $EBMLidList[EBML_ID_EBMLREADVERSION]            = 'EBMLReadVersion';
1634       $EBMLidList[EBML_ID_EBMLVERSION]                = 'EBMLVersion';
1635       $EBMLidList[EBML_ID_EDITIONENTRY]               = 'EditionEntry';
1636       $EBMLidList[EBML_ID_EDITIONFLAGDEFAULT]         = 'EditionFlagDefault';
1637       $EBMLidList[EBML_ID_EDITIONFLAGHIDDEN]          = 'EditionFlagHidden';
1638       $EBMLidList[EBML_ID_EDITIONFLAGORDERED]         = 'EditionFlagOrdered';
1639       $EBMLidList[EBML_ID_EDITIONUID]                 = 'EditionUID';
1640       $EBMLidList[EBML_ID_FILEDATA]                   = 'FileData';
1641       $EBMLidList[EBML_ID_FILEDESCRIPTION]            = 'FileDescription';
1642       $EBMLidList[EBML_ID_FILEMIMETYPE]               = 'FileMimeType';
1643       $EBMLidList[EBML_ID_FILENAME]                   = 'FileName';
1644       $EBMLidList[EBML_ID_FILEREFERRAL]               = 'FileReferral';
1645       $EBMLidList[EBML_ID_FILEUID]                    = 'FileUID';
1646       $EBMLidList[EBML_ID_FLAGDEFAULT]                = 'FlagDefault';
1647       $EBMLidList[EBML_ID_FLAGENABLED]                = 'FlagEnabled';
1648       $EBMLidList[EBML_ID_FLAGFORCED]                 = 'FlagForced';
1649       $EBMLidList[EBML_ID_FLAGINTERLACED]             = 'FlagInterlaced';
1650       $EBMLidList[EBML_ID_FLAGLACING]                 = 'FlagLacing';
1651       $EBMLidList[EBML_ID_GAMMAVALUE]                 = 'GammaValue';
1652       $EBMLidList[EBML_ID_INFO]                       = 'Info';
1653       $EBMLidList[EBML_ID_LANGUAGE]                   = 'Language';
1654       $EBMLidList[EBML_ID_MAXBLOCKADDITIONID]         = 'MaxBlockAdditionID';
1655       $EBMLidList[EBML_ID_MAXCACHE]                   = 'MaxCache';
1656       $EBMLidList[EBML_ID_MINCACHE]                   = 'MinCache';
1657       $EBMLidList[EBML_ID_MUXINGAPP]                  = 'MuxingApp';
1658       $EBMLidList[EBML_ID_NAME]                       = 'Name';
1659       $EBMLidList[EBML_ID_NEXTFILENAME]               = 'NextFilename';
1660       $EBMLidList[EBML_ID_NEXTUID]                    = 'NextUID';
1661       $EBMLidList[EBML_ID_OUTPUTSAMPLINGFREQUENCY]    = 'OutputSamplingFrequency';
1662       $EBMLidList[EBML_ID_PIXELCROPBOTTOM]            = 'PixelCropBottom';
1663       $EBMLidList[EBML_ID_PIXELCROPLEFT]              = 'PixelCropLeft';
1664       $EBMLidList[EBML_ID_PIXELCROPRIGHT]             = 'PixelCropRight';
1665       $EBMLidList[EBML_ID_PIXELCROPTOP]               = 'PixelCropTop';
1666       $EBMLidList[EBML_ID_PIXELHEIGHT]                = 'PixelHeight';
1667       $EBMLidList[EBML_ID_PIXELWIDTH]                 = 'PixelWidth';
1668       $EBMLidList[EBML_ID_PREVFILENAME]               = 'PrevFilename';
1669       $EBMLidList[EBML_ID_PREVUID]                    = 'PrevUID';
1670       $EBMLidList[EBML_ID_SAMPLINGFREQUENCY]          = 'SamplingFrequency';
1671       $EBMLidList[EBML_ID_SEEK]                       = 'Seek';
1672       $EBMLidList[EBML_ID_SEEKHEAD]                   = 'SeekHead';
1673       $EBMLidList[EBML_ID_SEEKID]                     = 'SeekID';
1674       $EBMLidList[EBML_ID_SEEKPOSITION]               = 'SeekPosition';
1675       $EBMLidList[EBML_ID_SEGMENT]                    = 'Segment';
1676       $EBMLidList[EBML_ID_SEGMENTFAMILY]              = 'SegmentFamily';
1677       $EBMLidList[EBML_ID_SEGMENTFILENAME]            = 'SegmentFilename';
1678       $EBMLidList[EBML_ID_SEGMENTUID]                 = 'SegmentUID';
1679       $EBMLidList[EBML_ID_SIMPLETAG]                  = 'SimpleTag';
1680       $EBMLidList[EBML_ID_CLUSTERSLICES]              = 'ClusterSlices';
1681       $EBMLidList[EBML_ID_STEREOMODE]                 = 'StereoMode';
1682       $EBMLidList[EBML_ID_OLDSTEREOMODE]              = 'OldStereoMode';
1683       $EBMLidList[EBML_ID_TAG]                        = 'Tag';
1684       $EBMLidList[EBML_ID_TAGATTACHMENTUID]           = 'TagAttachmentUID';
1685       $EBMLidList[EBML_ID_TAGBINARY]                  = 'TagBinary';
1686       $EBMLidList[EBML_ID_TAGCHAPTERUID]              = 'TagChapterUID';
1687       $EBMLidList[EBML_ID_TAGDEFAULT]                 = 'TagDefault';
1688       $EBMLidList[EBML_ID_TAGEDITIONUID]              = 'TagEditionUID';
1689       $EBMLidList[EBML_ID_TAGLANGUAGE]                = 'TagLanguage';
1690       $EBMLidList[EBML_ID_TAGNAME]                    = 'TagName';
1691       $EBMLidList[EBML_ID_TAGTRACKUID]                = 'TagTrackUID';
1692       $EBMLidList[EBML_ID_TAGS]                       = 'Tags';
1693       $EBMLidList[EBML_ID_TAGSTRING]                  = 'TagString';
1694       $EBMLidList[EBML_ID_TARGETS]                    = 'Targets';
1695       $EBMLidList[EBML_ID_TARGETTYPE]                 = 'TargetType';
1696       $EBMLidList[EBML_ID_TARGETTYPEVALUE]            = 'TargetTypeValue';
1697       $EBMLidList[EBML_ID_TIMECODESCALE]              = 'TimecodeScale';
1698       $EBMLidList[EBML_ID_TITLE]                      = 'Title';
1699       $EBMLidList[EBML_ID_TRACKENTRY]                 = 'TrackEntry';
1700       $EBMLidList[EBML_ID_TRACKNUMBER]                = 'TrackNumber';
1701       $EBMLidList[EBML_ID_TRACKOFFSET]                = 'TrackOffset';
1702       $EBMLidList[EBML_ID_TRACKOVERLAY]               = 'TrackOverlay';
1703       $EBMLidList[EBML_ID_TRACKS]                     = 'Tracks';
1704       $EBMLidList[EBML_ID_TRACKTIMECODESCALE]         = 'TrackTimecodeScale';
1705       $EBMLidList[EBML_ID_TRACKTRANSLATE]             = 'TrackTranslate';
1706       $EBMLidList[EBML_ID_TRACKTRANSLATECODEC]        = 'TrackTranslateCodec';
1707       $EBMLidList[EBML_ID_TRACKTRANSLATEEDITIONUID]   = 'TrackTranslateEditionUID';
1708       $EBMLidList[EBML_ID_TRACKTRANSLATETRACKID]      = 'TrackTranslateTrackID';
1709       $EBMLidList[EBML_ID_TRACKTYPE]                  = 'TrackType';
1710       $EBMLidList[EBML_ID_TRACKUID]                   = 'TrackUID';
1711       $EBMLidList[EBML_ID_VIDEO]                      = 'Video';
1712       $EBMLidList[EBML_ID_VOID]                       = 'Void';
1713       $EBMLidList[EBML_ID_WRITINGAPP]                 = 'WritingApp';
1714     }
1715 
1716     return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
1717   }
1718 
1719   public static function displayUnit($value) {
1720     // http://www.matroska.org/technical/specs/index.html#DisplayUnit
1721     static $units = array(
1722       0 => 'pixels',
1723       1 => 'centimeters',
1724       2 => 'inches',
1725       3 => 'Display Aspect Ratio');
1726 
1727     return (isset($units[$value]) ? $units[$value] : 'unknown');
1728   }
1729 
1730   private static function getDefaultStreamInfo($streams)
1731   {
1732     foreach (array_reverse($streams) as $stream) {
1733       if ($stream['default']) {
1734         break;
1735       }
1736     }
1737 
1738     $unset = array('default', 'name');
1739     foreach ($unset as $u) {
1740       if (isset($stream[$u])) {
1741         unset($stream[$u]);
1742       }
1743     }
1744 
1745     $info = $stream;
1746     $info['streams'] = $streams;
1747 
1748     return $info;
1749   }
1750 
1751 }