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 }