r82055 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r82054‎ | r82055 | r82056 >
Date:13:55, 13 February 2011
Author:dale
Status:deferred
Tags:
Comment:
moved handlers and libraries into respective folders ( into subfolder 'handlers' )
Modified paths:
  • /trunk/extensions/TimedMediaHandler/OggHandler (deleted) (history)
  • /trunk/extensions/TimedMediaHandler/TimedMediaHandler.php (modified) (history)
  • /trunk/extensions/TimedMediaHandler/WebMHandler (deleted) (history)
  • /trunk/extensions/TimedMediaHandler/handlers/OggHandler (added) (history)
  • /trunk/extensions/TimedMediaHandler/handlers/WebMHandler (added) (history)

Diff [purge]

Index: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/getid3/module.audio-video.matroska.php
@@ -0,0 +1,1748 @@
 2+<?php
 3+/////////////////////////////////////////////////////////////////
 4+/// getID3() by James Heinrich <info@getid3.org> //
 5+// available at http://getid3.sourceforge.net //
 6+// or http://www.getid3.org //
 7+/////////////////////////////////////////////////////////////////
 8+// See readme.txt for more details //
 9+/////////////////////////////////////////////////////////////////
 10+// //
 11+// module.audio-video.matriska.php //
 12+// module for analyzing Matroska containers //
 13+// dependencies: NONE //
 14+// ///
 15+/////////////////////////////////////////////////////////////////
 16+
 17+
 18+define('EBML_ID_CHAPTERS', 0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation.
 19+define('EBML_ID_SEEKHEAD', 0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
 20+define('EBML_ID_TAGS', 0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found here.
 21+define('EBML_ID_INFO', 0x0549A966); // [15][49][A9][66] -- Contains miscellaneous general information and statistics on the file.
 22+define('EBML_ID_TRACKS', 0x0654AE6B); // [16][54][AE][6B] -- A top-level block of information with many tracks described.
 23+define('EBML_ID_SEGMENT', 0x08538067); // [18][53][80][67] -- This element contains all other top-level (level 1) elements. Typically a Matroska file is composed of 1 segment.
 24+define('EBML_ID_ATTACHMENTS', 0x0941A469); // [19][41][A4][69] -- Contain attached files.
 25+define('EBML_ID_EBML', 0x0A45DFA3); // [1A][45][DF][A3] -- Set the EBML characteristics of the data to follow. Each EBML document has to start with this.
 26+define('EBML_ID_CUES', 0x0C53BB6B); // [1C][53][BB][6B] -- A top-level element to speed seeking access. All entries are local to the segment.
 27+define('EBML_ID_CLUSTER', 0x0F43B675); // [1F][43][B6][75] -- The lower level element containing the (monolithic) Block structure.
 28+define('EBML_ID_LANGUAGE', 0x02B59C); // [22][B5][9C] -- Specifies the language of the track in the Matroska languages form.
 29+define('EBML_ID_TRACKTIMECODESCALE', 0x03314F); // [23][31][4F] -- The scale to apply on this track to work at normal speed in relation with other tracks (mostly used to adjust video speed when the audio length differs).
 30+define('EBML_ID_DEFAULTDURATION', 0x03E383); // [23][E3][83] -- Number of nanoseconds (i.e. not scaled) per frame.
 31+define('EBML_ID_CODECNAME', 0x058688); // [25][86][88] -- A human-readable string specifying the codec.
 32+define('EBML_ID_CODECDOWNLOADURL', 0x06B240); // [26][B2][40] -- A URL to download about the codec used.
 33+define('EBML_ID_TIMECODESCALE', 0x0AD7B1); // [2A][D7][B1] -- Timecode scale in nanoseconds (1.000.000 means all timecodes in the segment are expressed in milliseconds).
 34+define('EBML_ID_COLOURSPACE', 0x0EB524); // [2E][B5][24] -- Same value as in AVI (32 bits).
 35+define('EBML_ID_GAMMAVALUE', 0x0FB523); // [2F][B5][23] -- Gamma Value.
 36+define('EBML_ID_CODECSETTINGS', 0x1A9697); // [3A][96][97] -- A string describing the encoding setting used.
 37+define('EBML_ID_CODECINFOURL', 0x1B4040); // [3B][40][40] -- A URL to find information about the codec used.
 38+define('EBML_ID_PREVFILENAME', 0x1C83AB); // [3C][83][AB] -- An escaped filename corresponding to the previous segment.
 39+define('EBML_ID_PREVUID', 0x1CB923); // [3C][B9][23] -- A unique ID to identify the previous chained segment (128 bits).
 40+define('EBML_ID_NEXTFILENAME', 0x1E83BB); // [3E][83][BB] -- An escaped filename corresponding to the next segment.
 41+define('EBML_ID_NEXTUID', 0x1EB923); // [3E][B9][23] -- A unique ID to identify the next chained segment (128 bits).
 42+define('EBML_ID_CONTENTCOMPALGO', 0x0254); // [42][54] -- The compression algorithm used. Algorithms that have been specified so far are:
 43+define('EBML_ID_CONTENTCOMPSETTINGS', 0x0255); // [42][55] -- Settings that might be needed by the decompressor. For Header Stripping (ContentCompAlgo=3), the bytes that were removed from the beggining of each frames of the track.
 44+define('EBML_ID_DOCTYPE', 0x0282); // [42][82] -- A string that describes the type of document that follows this EBML header ('matroska' in our case).
 45+define('EBML_ID_DOCTYPEREADVERSION', 0x0285); // [42][85] -- The minimum DocType version an interpreter has to support to read this file.
 46+define('EBML_ID_EBMLVERSION', 0x0286); // [42][86] -- The version of EBML parser used to create the file.
 47+define('EBML_ID_DOCTYPEVERSION', 0x0287); // [42][87] -- The version of DocType interpreter used to create the file.
 48+define('EBML_ID_EBMLMAXIDLENGTH', 0x02F2); // [42][F2] -- The maximum length of the IDs you'll find in this file (4 or less in Matroska).
 49+define('EBML_ID_EBMLMAXSIZELENGTH', 0x02F3); // [42][F3] -- The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid.
 50+define('EBML_ID_EBMLREADVERSION', 0x02F7); // [42][F7] -- The minimum EBML version a parser has to support to read this file.
 51+define('EBML_ID_CHAPLANGUAGE', 0x037C); // [43][7C] -- The languages corresponding to the string, in the bibliographic ISO-639-2 form.
 52+define('EBML_ID_CHAPCOUNTRY', 0x037E); // [43][7E] -- The countries corresponding to the string, same 2 octets as in Internet domains.
 53+define('EBML_ID_SEGMENTFAMILY', 0x0444); // [44][44] -- A randomly generated unique ID that all segments related to each other must use (128 bits).
 54+define('EBML_ID_DATEUTC', 0x0461); // [44][61] -- Date of the origin of timecode (value 0), i.e. production date.
 55+define('EBML_ID_TAGLANGUAGE', 0x047A); // [44][7A] -- Specifies the language of the tag specified, in the Matroska languages form.
 56+define('EBML_ID_TAGDEFAULT', 0x0484); // [44][84] -- Indication to know if this is the default/original language to use for the given tag.
 57+define('EBML_ID_TAGBINARY', 0x0485); // [44][85] -- The values of the Tag if it is binary. Note that this cannot be used in the same SimpleTag as TagString.
 58+define('EBML_ID_TAGSTRING', 0x0487); // [44][87] -- The value of the Tag.
 59+define('EBML_ID_DURATION', 0x0489); // [44][89] -- Duration of the segment (based on TimecodeScale).
 60+define('EBML_ID_CHAPPROCESSPRIVATE', 0x050D); // [45][0D] -- Some optional data attached to the ChapProcessCodecID information. For ChapProcessCodecID = 1, it is the "DVD level" equivalent.
 61+define('EBML_ID_CHAPTERFLAGENABLED', 0x0598); // [45][98] -- Specify wether the chapter is enabled. It can be enabled/disabled by a Control Track. When disabled, the movie should skip all the content between the TimeStart and TimeEnd of this chapter.
 62+define('EBML_ID_TAGNAME', 0x05A3); // [45][A3] -- The name of the Tag that is going to be stored.
 63+define('EBML_ID_EDITIONENTRY', 0x05B9); // [45][B9] -- Contains all information about a segment edition.
 64+define('EBML_ID_EDITIONUID', 0x05BC); // [45][BC] -- A unique ID to identify the edition. It's useful for tagging an edition.
 65+define('EBML_ID_EDITIONFLAGHIDDEN', 0x05BD); // [45][BD] -- If an edition is hidden (1), it should not be available to the user interface (but still to Control Tracks).
 66+define('EBML_ID_EDITIONFLAGDEFAULT', 0x05DB); // [45][DB] -- If a flag is set (1) the edition should be used as the default one.
 67+define('EBML_ID_EDITIONFLAGORDERED', 0x05DD); // [45][DD] -- Specify if the chapters can be defined multiple times and the order to play them is enforced.
 68+define('EBML_ID_FILEDATA', 0x065C); // [46][5C] -- The data of the file.
 69+define('EBML_ID_FILEMIMETYPE', 0x0660); // [46][60] -- MIME type of the file.
 70+define('EBML_ID_FILENAME', 0x066E); // [46][6E] -- Filename of the attached file.
 71+define('EBML_ID_FILEREFERRAL', 0x0675); // [46][75] -- A binary value that a track/codec can refer to when the attachment is needed.
 72+define('EBML_ID_FILEDESCRIPTION', 0x067E); // [46][7E] -- A human-friendly name for the attached file.
 73+define('EBML_ID_FILEUID', 0x06AE); // [46][AE] -- Unique ID representing the file, as random as possible.
 74+define('EBML_ID_CONTENTENCALGO', 0x07E1); // [47][E1] -- The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values:
 75+define('EBML_ID_CONTENTENCKEYID', 0x07E2); // [47][E2] -- For public key algorithms this is the ID of the public key the the data was encrypted with.
 76+define('EBML_ID_CONTENTSIGNATURE', 0x07E3); // [47][E3] -- A cryptographic signature of the contents.
 77+define('EBML_ID_CONTENTSIGKEYID', 0x07E4); // [47][E4] -- This is the ID of the private key the data was signed with.
 78+define('EBML_ID_CONTENTSIGALGO', 0x07E5); // [47][E5] -- The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
 79+define('EBML_ID_CONTENTSIGHASHALGO', 0x07E6); // [47][E6] -- The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
 80+define('EBML_ID_MUXINGAPP', 0x0D80); // [4D][80] -- Muxing application or library ("libmatroska-0.4.3").
 81+define('EBML_ID_SEEK', 0x0DBB); // [4D][BB] -- Contains a single seek entry to an EBML element.
 82+define('EBML_ID_CONTENTENCODINGORDER', 0x1031); // [50][31] -- Tells when this modification was used during encoding/muxing starting with 0 and counting upwards. The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to be unique over all ContentEncodingOrder elements in the segment.
 83+define('EBML_ID_CONTENTENCODINGSCOPE', 0x1032); // [50][32] -- A bit field that describes which elements have been modified in this way. Values (big endian) can be OR'ed. Possible values:
 84+define('EBML_ID_CONTENTENCODINGTYPE', 0x1033); // [50][33] -- A value describing what kind of transformation has been done. Possible values:
 85+define('EBML_ID_CONTENTCOMPRESSION', 0x1034); // [50][34] -- Settings describing the compression used. Must be present if the value of ContentEncodingType is 0 and absent otherwise. Each block must be decompressable even if no previous block is available in order not to prevent seeking.
 86+define('EBML_ID_CONTENTENCRYPTION', 0x1035); // [50][35] -- Settings describing the encryption used. Must be present if the value of ContentEncodingType is 1 and absent otherwise.
 87+define('EBML_ID_CUEREFNUMBER', 0x135F); // [53][5F] -- Number of the referenced Block of Track X in the specified Cluster.
 88+define('EBML_ID_NAME', 0x136E); // [53][6E] -- A human-readable track name.
 89+define('EBML_ID_CUEBLOCKNUMBER', 0x1378); // [53][78] -- Number of the Block in the specified Cluster.
 90+define('EBML_ID_TRACKOFFSET', 0x137F); // [53][7F] -- A value to add to the Block's Timecode. This can be used to adjust the playback offset of a track.
 91+define('EBML_ID_SEEKID', 0x13AB); // [53][AB] -- The binary ID corresponding to the element name.
 92+define('EBML_ID_SEEKPOSITION', 0x13AC); // [53][AC] -- The position of the element in the segment in octets (0 = first level 1 element).
 93+define('EBML_ID_STEREOMODE', 0x13B8); // [53][B8] -- Stereo-3D video mode on 2 bits (0: mono, 1: right eye, 2: left eye, 3: both eyes).
 94+define('EBML_ID_PIXELCROPBOTTOM', 0x14AA); // [54][AA] -- The number of video pixels to remove at the bottom of the image (for HDTV content).
 95+define('EBML_ID_DISPLAYWIDTH', 0x14B0); // [54][B0] -- Width of the video frames to display.
 96+define('EBML_ID_DISPLAYUNIT', 0x14B2); // [54][B2] -- Type of the unit for DisplayWidth/Height (0: pixels, 1: centimeters, 2: inches).
 97+define('EBML_ID_ASPECTRATIOTYPE', 0x14B3); // [54][B3] -- Specify the possible modifications to the aspect ratio (0: free resizing, 1: keep aspect ratio, 2: fixed).
 98+define('EBML_ID_DISPLAYHEIGHT', 0x14BA); // [54][BA] -- Height of the video frames to display.
 99+define('EBML_ID_PIXELCROPTOP', 0x14BB); // [54][BB] -- The number of video pixels to remove at the top of the image.
 100+define('EBML_ID_PIXELCROPLEFT', 0x14CC); // [54][CC] -- The number of video pixels to remove on the left of the image.
 101+define('EBML_ID_PIXELCROPRIGHT', 0x14DD); // [54][DD] -- The number of video pixels to remove on the right of the image.
 102+define('EBML_ID_FLAGFORCED', 0x15AA); // [55][AA] -- Set if that track MUST be used during playback. There can be many forced track for a kind (audio, video or subs), the player should select the one which language matches the user preference or the default + forced track. Overlay MAY happen between a forced and non-forced track of the same kind.
 103+define('EBML_ID_MAXBLOCKADDITIONID', 0x15EE); // [55][EE] -- The maximum value of BlockAddID. A value 0 means there is no BlockAdditions for this track.
 104+define('EBML_ID_WRITINGAPP', 0x1741); // [57][41] -- Writing application ("mkvmerge-0.3.3").
 105+define('EBML_ID_CLUSTERSILENTTRACKS', 0x1854); // [58][54] -- The list of tracks that are not used in that part of the stream. It is useful when using overlay tracks on seeking. Then you should decide what track to use.
 106+define('EBML_ID_CLUSTERSILENTTRACKNUMBER', 0x18D7); // [58][D7] -- One of the track number that are not used from now on in the stream. It could change later if not specified as silent in a further Cluster.
 107+define('EBML_ID_ATTACHEDFILE', 0x21A7); // [61][A7] -- An attached file.
 108+define('EBML_ID_CONTENTENCODING', 0x2240); // [62][40] -- Settings for one content encoding like compression or encryption.
 109+define('EBML_ID_BITDEPTH', 0x2264); // [62][64] -- Bits per sample, mostly used for PCM.
 110+define('EBML_ID_CODECPRIVATE', 0x23A2); // [63][A2] -- Private data only known to the codec.
 111+define('EBML_ID_TARGETS', 0x23C0); // [63][C0] -- Contain all UIDs where the specified meta data apply. It is void to describe everything in the segment.
 112+define('EBML_ID_CHAPTERPHYSICALEQUIV', 0x23C3); // [63][C3] -- Specify the physical equivalent of this ChapterAtom like "DVD" (60) or "SIDE" (50), see complete list of values.
 113+define('EBML_ID_TAGCHAPTERUID', 0x23C4); // [63][C4] -- A unique ID to identify the Chapter(s) the tags belong to. If the value is 0 at this level, the tags apply to all chapters in the Segment.
 114+define('EBML_ID_TAGTRACKUID', 0x23C5); // [63][C5] -- A unique ID to identify the Track(s) the tags belong to. If the value is 0 at this level, the tags apply to all tracks in the Segment.
 115+define('EBML_ID_ATTACHMENTUID', 0x23C6); // [63][C6] -- A unique ID to identify the Attachment(s) the tags belong to. If the value is 0 at this level, the tags apply to all the attachments in the Segment.
 116+define('EBML_ID_TAGEDITIONUID', 0x23C9); // [63][C9] -- A unique ID to identify the EditionEntry(s) the tags belong to. If the value is 0 at this level, the tags apply to all editions in the Segment.
 117+define('EBML_ID_TARGETTYPE', 0x23CA); // [63][CA] -- An informational string that can be used to display the logical level of the target like "ALBUM", "TRACK", "MOVIE", "CHAPTER", etc (see TargetType).
 118+define('EBML_ID_TRACKTRANSLATE', 0x2624); // [66][24] -- The track identification for the given Chapter Codec.
 119+define('EBML_ID_TRACKTRANSLATETRACKID', 0x26A5); // [66][A5] -- The binary value used to represent this track in the chapter codec data. The format depends on the ChapProcessCodecID used.
 120+define('EBML_ID_TRACKTRANSLATECODEC', 0x26BF); // [66][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
 121+define('EBML_ID_TRACKTRANSLATEEDITIONUID', 0x26FC); // [66][FC] -- Specify an edition UID on which this translation applies. When not specified, it means for all editions found in the segment.
 122+define('EBML_ID_SIMPLETAG', 0x27C8); // [67][C8] -- Contains general information about the target.
 123+define('EBML_ID_TARGETTYPEVALUE', 0x28CA); // [68][CA] -- A number to indicate the logical level of the target (see TargetType).
 124+define('EBML_ID_CHAPPROCESSCOMMAND', 0x2911); // [69][11] -- Contains all the commands associated to the Atom.
 125+define('EBML_ID_CHAPPROCESSTIME', 0x2922); // [69][22] -- Defines when the process command should be handled (0: during the whole chapter, 1: before starting playback, 2: after playback of the chapter).
 126+define('EBML_ID_CHAPTERTRANSLATE', 0x2924); // [69][24] -- A tuple of corresponding ID used by chapter codecs to represent this segment.
 127+define('EBML_ID_CHAPPROCESSDATA', 0x2933); // [69][33] -- Contains the command information. The data should be interpreted depending on the ChapProcessCodecID value. For ChapProcessCodecID = 1, the data correspond to the binary DVD cell pre/post commands.
 128+define('EBML_ID_CHAPPROCESS', 0x2944); // [69][44] -- Contains all the commands associated to the Atom.
 129+define('EBML_ID_CHAPPROCESSCODECID', 0x2955); // [69][55] -- Contains the type of the codec used for the processing. A value of 0 means native Matroska processing (to be defined), a value of 1 means the DVD command set is used. More codec IDs can be added later.
 130+define('EBML_ID_CHAPTERTRANSLATEID', 0x29A5); // [69][A5] -- The binary value used to represent this segment in the chapter codec data. The format depends on the ChapProcessCodecID used.
 131+define('EBML_ID_CHAPTERTRANSLATECODEC', 0x29BF); // [69][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
 132+define('EBML_ID_CHAPTERTRANSLATEEDITIONUID', 0x29FC); // [69][FC] -- Specify an edition UID on which this correspondance applies. When not specified, it means for all editions found in the segment.
 133+define('EBML_ID_CONTENTENCODINGS', 0x2D80); // [6D][80] -- Settings for several content encoding mechanisms like compression or encryption.
 134+define('EBML_ID_MINCACHE', 0x2DE7); // [6D][E7] -- The minimum number of frames a player should be able to cache during playback. If set to 0, the reference pseudo-cache system is not used.
 135+define('EBML_ID_MAXCACHE', 0x2DF8); // [6D][F8] -- The maximum cache size required to store referenced frames in and the current frame. 0 means no cache is needed.
 136+define('EBML_ID_CHAPTERSEGMENTUID', 0x2E67); // [6E][67] -- A segment to play in place of this chapter. Edition ChapterSegmentEditionUID should be used for this segment, otherwise no edition is used.
 137+define('EBML_ID_CHAPTERSEGMENTEDITIONUID', 0x2EBC); // [6E][BC] -- The edition to play from the segment linked in ChapterSegmentUID.
 138+define('EBML_ID_TRACKOVERLAY', 0x2FAB); // [6F][AB] -- Specify that this track is an overlay track for the Track specified (in the u-integer). That means when this track has a gap (see SilentTracks) the overlay track should be used instead. The order of multiple TrackOverlay matters, the first one is the one that should be used. If not found it should be the second, etc.
 139+define('EBML_ID_TAG', 0x3373); // [73][73] -- Element containing elements specific to Tracks/Chapters.
 140+define('EBML_ID_SEGMENTFILENAME', 0x3384); // [73][84] -- A filename corresponding to this segment.
 141+define('EBML_ID_SEGMENTUID', 0x33A4); // [73][A4] -- A randomly generated unique ID to identify the current segment between many others (128 bits).
 142+define('EBML_ID_CHAPTERUID', 0x33C4); // [73][C4] -- A unique ID to identify the Chapter.
 143+define('EBML_ID_TRACKUID', 0x33C5); // [73][C5] -- A unique ID to identify the Track. This should be kept the same when making a direct stream copy of the Track to another file.
 144+define('EBML_ID_ATTACHMENTLINK', 0x3446); // [74][46] -- The UID of an attachment that is used by this codec.
 145+define('EBML_ID_CLUSTERBLOCKADDITIONS', 0x35A1); // [75][A1] -- Contain additional blocks to complete the main one. An EBML parser that has no knowledge of the Block structure could still see and use/skip these data.
 146+define('EBML_ID_CHANNELPOSITIONS', 0x347B); // [7D][7B] -- Table of horizontal angles for each successive channel, see appendix.
 147+define('EBML_ID_OUTPUTSAMPLINGFREQUENCY', 0x38B5); // [78][B5] -- Real output sampling frequency in Hz (used for SBR techniques).
 148+define('EBML_ID_TITLE', 0x3BA9); // [7B][A9] -- General name of the segment.
 149+define('EBML_ID_CHAPTERDISPLAY', 0x00); // [80] -- Contains all possible strings to use for the chapter display.
 150+define('EBML_ID_TRACKTYPE', 0x03); // [83] -- A set of track types coded on 8 bits (1: video, 2: audio, 3: complex, 0x10: logo, 0x11: subtitle, 0x12: buttons, 0x20: control).
 151+define('EBML_ID_CHAPSTRING', 0x05); // [85] -- Contains the string to use as the chapter atom.
 152+define('EBML_ID_CODECID', 0x06); // [86] -- An ID corresponding to the codec, see the codec page for more info.
 153+define('EBML_ID_FLAGDEFAULT', 0x08); // [88] -- Set if that track (audio, video or subs) SHOULD be used if no language found matches the user preference.
 154+define('EBML_ID_CHAPTERTRACKNUMBER', 0x09); // [89] -- UID of the Track to apply this chapter too. In the absense of a control track, choosing this chapter will select the listed Tracks and deselect unlisted tracks. Absense of this element indicates that the Chapter should be applied to any currently used Tracks.
 155+define('EBML_ID_CLUSTERSLICES', 0x0E); // [8E] -- Contains slices description.
 156+define('EBML_ID_CHAPTERTRACK', 0x0F); // [8F] -- List of tracks on which the chapter applies. If this element is not present, all tracks apply
 157+define('EBML_ID_CHAPTERTIMESTART', 0x11); // [91] -- Timecode of the start of Chapter (not scaled).
 158+define('EBML_ID_CHAPTERTIMEEND', 0x12); // [92] -- Timecode of the end of Chapter (timecode excluded, not scaled).
 159+define('EBML_ID_CUEREFTIME', 0x16); // [96] -- Timecode of the referenced Block.
 160+define('EBML_ID_CUEREFCLUSTER', 0x17); // [97] -- Position of the Cluster containing the referenced Block.
 161+define('EBML_ID_CHAPTERFLAGHIDDEN', 0x18); // [98] -- If a chapter is hidden (1), it should not be available to the user interface (but still to Control Tracks).
 162+define('EBML_ID_FLAGINTERLACED', 0x1A); // [9A] -- Set if the video is interlaced.
 163+define('EBML_ID_CLUSTERBLOCKDURATION', 0x1B); // [9B] -- The duration of the Block (based on TimecodeScale). This element is mandatory when DefaultDuration is set for the track. When not written and with no DefaultDuration, the value is assumed to be the difference between the timecode of this Block and the timecode of the next Block in "display" order (not coding order). This element can be useful at the end of a Track (as there is not other Block available), or when there is a break in a track like for subtitle tracks.
 164+define('EBML_ID_FLAGLACING', 0x1C); // [9C] -- Set if the track may contain blocks using lacing.
 165+define('EBML_ID_CHANNELS', 0x1F); // [9F] -- Numbers of channels in the track.
 166+define('EBML_ID_CLUSTERBLOCKGROUP', 0x20); // [A0] -- Basic container of information containing a single Block or BlockVirtual, and information specific to that Block/VirtualBlock.
 167+define('EBML_ID_CLUSTERBLOCK', 0x21); // [A1] -- Block containing the actual data to be rendered and a timecode relative to the Cluster Timecode.
 168+define('EBML_ID_CLUSTERBLOCKVIRTUAL', 0x22); // [A2] -- A Block with no data. It must be stored in the stream at the place the real Block should be in display order.
 169+define('EBML_ID_CLUSTERSIMPLEBLOCK', 0x23); // [A3] -- Similar to Block but without all the extra information, mostly used to reduced overhead when no extra feature is needed.
 170+define('EBML_ID_CLUSTERCODECSTATE', 0x24); // [A4] -- The new codec state to use. Data interpretation is private to the codec. This information should always be referenced by a seek entry.
 171+define('EBML_ID_CLUSTERBLOCKADDITIONAL', 0x25); // [A5] -- Interpreted by the codec as it wishes (using the BlockAddID).
 172+define('EBML_ID_CLUSTERBLOCKMORE', 0x26); // [A6] -- Contain the BlockAdditional and some parameters.
 173+define('EBML_ID_CLUSTERPOSITION', 0x27); // [A7] -- Position of the Cluster in the segment (0 in live broadcast streams). It might help to resynchronise offset on damaged streams.
 174+define('EBML_ID_CODECDECODEALL', 0x2A); // [AA] -- The codec can decode potentially damaged data.
 175+define('EBML_ID_CLUSTERPREVSIZE', 0x2B); // [AB] -- Size of the previous Cluster, in octets. Can be useful for backward playing.
 176+define('EBML_ID_TRACKENTRY', 0x2E); // [AE] -- Describes a track with all elements.
 177+define('EBML_ID_CLUSTERENCRYPTEDBLOCK', 0x2F); // [AF] -- Similar to SimpleBlock but the data inside the Block are Transformed (encrypt and/or signed).
 178+define('EBML_ID_PIXELWIDTH', 0x30); // [B0] -- Width of the encoded video frames in pixels.
 179+define('EBML_ID_CUETIME', 0x33); // [B3] -- Absolute timecode according to the segment time base.
 180+define('EBML_ID_SAMPLINGFREQUENCY', 0x35); // [B5] -- Sampling frequency in Hz.
 181+define('EBML_ID_CHAPTERATOM', 0x36); // [B6] -- Contains the atom information to use as the chapter atom (apply to all tracks).
 182+define('EBML_ID_CUETRACKPOSITIONS', 0x37); // [B7] -- Contain positions for different tracks corresponding to the timecode.
 183+define('EBML_ID_FLAGENABLED', 0x39); // [B9] -- Set if the track is used.
 184+define('EBML_ID_PIXELHEIGHT', 0x3A); // [BA] -- Height of the encoded video frames in pixels.
 185+define('EBML_ID_CUEPOINT', 0x3B); // [BB] -- Contains all information relative to a seek point in the segment.
 186+define('EBML_ID_CRC32', 0x3F); // [BF] -- The CRC is computed on all the data of the Master element it's in, regardless of its position. It's recommended to put the CRC value at the beggining of the Master element for easier reading. All level 1 elements should include a CRC-32.
 187+define('EBML_ID_CLUSTERBLOCKADDITIONID', 0x4B); // [CB] -- The ID of the BlockAdditional element (0 is the main Block).
 188+define('EBML_ID_CLUSTERLACENUMBER', 0x4C); // [CC] -- The reverse number of the frame in the lace (0 is the last frame, 1 is the next to last, etc). While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback.
 189+define('EBML_ID_CLUSTERFRAMENUMBER', 0x4D); // [CD] -- The number of the frame to generate from this lace with this delay (allow you to generate many frames from the same Block/Frame).
 190+define('EBML_ID_CLUSTERDELAY', 0x4E); // [CE] -- The (scaled) delay to apply to the element.
 191+define('EBML_ID_CLUSTERDURATION', 0x4F); // [CF] -- The (scaled) duration to apply to the element.
 192+define('EBML_ID_TRACKNUMBER', 0x57); // [D7] -- The track number as used in the Block Header (using more than 127 tracks is not encouraged, though the design allows an unlimited number).
 193+define('EBML_ID_CUEREFERENCE', 0x5B); // [DB] -- The Clusters containing the required referenced Blocks.
 194+define('EBML_ID_VIDEO', 0x60); // [E0] -- Video settings.
 195+define('EBML_ID_AUDIO', 0x61); // [E1] -- Audio settings.
 196+define('EBML_ID_CLUSTERTIMESLICE', 0x68); // [E8] -- Contains extra time information about the data contained in the Block. While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback.
 197+define('EBML_ID_CUECODECSTATE', 0x6A); // [EA] -- The position of the Codec State corresponding to this Cue element. 0 means that the data is taken from the initial Track Entry.
 198+define('EBML_ID_CUEREFCODECSTATE', 0x6B); // [EB] -- The position of the Codec State corresponding to this referenced element. 0 means that the data is taken from the initial Track Entry.
 199+define('EBML_ID_VOID', 0x6C); // [EC] -- Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use.
 200+define('EBML_ID_CLUSTERTIMECODE', 0x67); // [E7] -- Absolute timecode of the cluster (based on TimecodeScale).
 201+define('EBML_ID_CLUSTERBLOCKADDID', 0x6E); // [EE] -- An ID to identify the BlockAdditional level.
 202+define('EBML_ID_CUECLUSTERPOSITION', 0x71); // [F1] -- The position of the Cluster containing the required Block.
 203+define('EBML_ID_CUETRACK', 0x77); // [F7] -- The track for which a position is given.
 204+define('EBML_ID_CLUSTERREFERENCEPRIORITY', 0x7A); // [FA] -- This frame is referenced and has the specified cache priority. In cache only a frame of the same or higher priority can replace this frame. A value of 0 means the frame is not referenced.
 205+define('EBML_ID_CLUSTERREFERENCEBLOCK', 0x7B); // [FB] -- Timecode of another frame used as a reference (ie: B or P frame). The timecode is relative to the block it's attached to.
 206+define('EBML_ID_CLUSTERREFERENCEVIRTUAL', 0x7D); // [FD] -- Relative position of the data that should be in position of the virtual block.
 207+
 208+
 209+class getid3_matroska
 210+{
 211+ var $read_buffer_size = 32768; // size of read buffer, 32kB is default
 212+ var $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
 213+ var $warnings = array();
 214+
 215+ function getid3_matroska(&$fd, &$ThisFileInfo) {
 216+
 217+ // http://www.matroska.org/technical/specs/index.html#EBMLBasics
 218+ $offset = $ThisFileInfo['avdataoffset'];
 219+ $EBMLdata = '';
 220+ $EBMLdata_offset = $offset;
 221+
 222+ if ($ThisFileInfo['avdataend'] > 2147483648) {
 223+ $this->warnings[] = 'This version of getID3() may or may not correctly handle Matroska files larger than 2GB';
 224+ }
 225+
 226+ while ($offset < $ThisFileInfo['avdataend']) {
 227+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 228+
 229+ $top_element_offset = $offset;
 230+ $top_element_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 231+ $top_element_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 232+ if ($top_element_length === false) {
 233+ $this->warnings[] = 'invalid chunk length at '.$top_element_offset;
 234+ $offset = pow(2, 63);
 235+ break;
 236+ }
 237+ $top_element_endoffset = $offset + $top_element_length;
 238+ switch ($top_element_id) {
 239+ case EBML_ID_EBML:
 240+ $ThisFileInfo['fileformat'] = 'matroska';
 241+ $ThisFileInfo['matroska']['header']['offset'] = $top_element_offset;
 242+ $ThisFileInfo['matroska']['header']['length'] = $top_element_length;
 243+
 244+ while ($offset < $top_element_endoffset) {
 245+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 246+ $element_data = array();
 247+ $element_data_offset = $offset;
 248+ $element_data['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 249+ $element_data['id_name'] = $this->EBMLidName($element_data['id']);
 250+ $element_data['length'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 251+ $end_offset = $offset + $element_data['length'];
 252+
 253+ switch ($element_data['id']) {
 254+ case EBML_ID_VOID: // padding, ignore
 255+ break;
 256+ case EBML_ID_EBMLVERSION:
 257+ case EBML_ID_EBMLREADVERSION:
 258+ case EBML_ID_EBMLMAXIDLENGTH:
 259+ case EBML_ID_EBMLMAXSIZELENGTH:
 260+ case EBML_ID_DOCTYPEVERSION:
 261+ case EBML_ID_DOCTYPEREADVERSION:
 262+ $element_data['data'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $element_data['length']));
 263+ break;
 264+ case EBML_ID_DOCTYPE:
 265+ $element_data['data'] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $element_data['length']), "\x00");
 266+ break;
 267+ default:
 268+ $this->warnings[] = 'Unhandled track.video element ['.basename(__FILE__).':'.__LINE__.'] ('.$element_data['id'].'::'.$element_data['id_name'].') at '.$element_data_offset;
 269+ break;
 270+ }
 271+ $offset = $end_offset;
 272+ $ThisFileInfo['matroska']['header']['elements'][] = $element_data;
 273+ }
 274+ break;
 275+
 276+
 277+ case EBML_ID_SEGMENT:
 278+ $ThisFileInfo['matroska']['segment'][0]['offset'] = $top_element_offset;
 279+ $ThisFileInfo['matroska']['segment'][0]['length'] = $top_element_length;
 280+
 281+ $segment_key = -1;
 282+ while ($offset < $ThisFileInfo['avdataend']) {
 283+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 284+
 285+ $element_data = array();
 286+ $element_data['offset'] = $offset;
 287+ $element_data['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 288+ $element_data['id_name'] = $this->EBMLidName($element_data['id']);
 289+ $element_data['length'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 290+ if ($element_data['length'] === false) {
 291+ $this->warnings[] = 'invalid chunk length at '.$element_data['offset'];
 292+ //$offset = pow(2, 63);
 293+ $offset = $ThisFileInfo['avdataend'];
 294+ break;
 295+ }
 296+ $element_end = $offset + $element_data['length'];
 297+ switch ($element_data['id']) {
 298+ //case EBML_ID_CLUSTER:
 299+ // // too many cluster entries, probably not useful
 300+ // break;
 301+ case false:
 302+ $this->warnings[] = 'invalid ID at '.$element_data['offset'];
 303+ $offset = $element_end;
 304+ continue 3;
 305+ default:
 306+ $ThisFileInfo['matroska']['segments'][] = $element_data;
 307+ break;
 308+ }
 309+ $segment_key++;
 310+
 311+ switch ($element_data['id']) {
 312+ case EBML_ID_SEEKHEAD: // Contains the position of other level 1 elements
 313+ while ($offset < $element_end) {
 314+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 315+ $seek_entry = array();
 316+ $seek_entry['offset'] = $offset;
 317+ $seek_entry['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 318+ $seek_entry['id_name'] = $this->EBMLidName($seek_entry['id']);
 319+ $seek_entry['length'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 320+ $seek_end_offset = $offset + $seek_entry['length'];
 321+ switch ($seek_entry['id']) {
 322+ case EBML_ID_SEEK: // Contains a single seek entry to an EBML element
 323+ while ($offset < $seek_end_offset) {
 324+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 325+ $id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 326+ $length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 327+ $value = substr($EBMLdata, $offset - $EBMLdata_offset, $length);
 328+ $offset += $length;
 329+ switch ($id) {
 330+ case EBML_ID_SEEKID:
 331+ $dummy = 0;
 332+ $seek_entry['target_id'] = $this->readEBMLint($value, $dummy);
 333+ $seek_entry['target_name'] = $this->EBMLidName($seek_entry['target_id']);
 334+ break;
 335+ case EBML_ID_SEEKPOSITION:
 336+ $seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($value);
 337+ break;
 338+ default:
 339+ $ThisFileInfo['error'][] = 'Unhandled segment ['.basename(__FILE__).':'.__LINE__.'] ('.$id.') at '.$offset;
 340+ break;
 341+ }
 342+ }
 343+ $ThisFileInfo['matroska']['seek'][] = $seek_entry;
 344+ //switch ($seek_entry['target_id']) {
 345+ // case EBML_ID_CLUSTER:
 346+ // // too many cluster seek points, probably not useful
 347+ // break;
 348+ // default:
 349+ // $ThisFileInfo['matroska']['seek'][] = $seek_entry;
 350+ // break;
 351+ //}
 352+ break;
 353+ default:
 354+ $this->warnings[] = 'Unhandled seekhead element ['.basename(__FILE__).':'.__LINE__.'] ('.$seek_entry['id'].') at '.$offset;
 355+ break;
 356+ }
 357+ $offset = $seek_end_offset;
 358+ }
 359+ break;
 360+
 361+ case EBML_ID_TRACKS: // information about all tracks in segment
 362+ $ThisFileInfo['matroska']['tracks'] = $element_data;
 363+ while ($offset < $element_end) {
 364+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 365+ $track_entry = array();
 366+ $track_entry['offset'] = $offset;
 367+ $track_entry['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 368+ $track_entry['id_name'] = $this->EBMLidName($track_entry['id']);
 369+ $track_entry['length'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 370+ $track_entry_endoffset = $offset + $track_entry['length'];
 371+ switch ($track_entry['id']) {
 372+ case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements.
 373+ while ($offset < $track_entry_endoffset) {
 374+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 375+ $subelement_offset = $offset;
 376+ $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 377+ $subelement_idname = $this->EBMLidName($subelement_id);
 378+ $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 379+ $subelement_end = $offset + $subelement_length;
 380+ switch ($subelement_id) {
 381+ case EBML_ID_TRACKNUMBER:
 382+ case EBML_ID_TRACKUID:
 383+ case EBML_ID_TRACKTYPE:
 384+ case EBML_ID_MINCACHE:
 385+ case EBML_ID_MAXCACHE:
 386+ case EBML_ID_MAXBLOCKADDITIONID:
 387+ case EBML_ID_DEFAULTDURATION: // nanoseconds per frame
 388+ $track_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
 389+ break;
 390+
 391+ case EBML_ID_TRACKTIMECODESCALE:
 392+ $track_entry[$subelement_idname] = getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
 393+ break;
 394+
 395+ case EBML_ID_CODECID:
 396+ case EBML_ID_LANGUAGE:
 397+ case EBML_ID_NAME:
 398+ case EBML_ID_CODECNAME:
 399+ case EBML_ID_CODECPRIVATE:
 400+ $track_entry[$subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00");
 401+ break;
 402+
 403+ case EBML_ID_FLAGENABLED:
 404+ case EBML_ID_FLAGDEFAULT:
 405+ case EBML_ID_FLAGFORCED:
 406+ case EBML_ID_FLAGLACING:
 407+ case EBML_ID_CODECDECODEALL:
 408+ $track_entry[$subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
 409+ break;
 410+
 411+ case EBML_ID_VIDEO:
 412+ while ($offset < $subelement_end) {
 413+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 414+ $sub_subelement_offset = $offset;
 415+ $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 416+ $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
 417+ $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 418+ $sub_subelement_end = $offset + $sub_subelement_length;
 419+ switch ($sub_subelement_id) {
 420+ case EBML_ID_PIXELWIDTH:
 421+ case EBML_ID_PIXELHEIGHT:
 422+ case EBML_ID_STEREOMODE:
 423+ case EBML_ID_PIXELCROPBOTTOM:
 424+ case EBML_ID_PIXELCROPTOP:
 425+ case EBML_ID_PIXELCROPLEFT:
 426+ case EBML_ID_PIXELCROPRIGHT:
 427+ case EBML_ID_DISPLAYWIDTH:
 428+ case EBML_ID_DISPLAYHEIGHT:
 429+ case EBML_ID_DISPLAYUNIT:
 430+ case EBML_ID_ASPECTRATIOTYPE:
 431+ $track_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 432+ break;
 433+ case EBML_ID_FLAGINTERLACED:
 434+ $track_entry[$sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 435+ break;
 436+ case EBML_ID_GAMMAVALUE:
 437+ $track_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 438+ break;
 439+ case EBML_ID_COLOURSPACE:
 440+ $track_entry[$sub_subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), "\x00");
 441+ break;
 442+ default:
 443+ $this->warnings[] = 'Unhandled track.video element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
 444+ break;
 445+ }
 446+ $offset = $sub_subelement_end;
 447+ }
 448+
 449+ if (isset($track_entry[$this->EBMLidName(EBML_ID_CODECID)]) && ($track_entry[$this->EBMLidName(EBML_ID_CODECID)] == 'V_MS/VFW/FOURCC') && isset($track_entry[$this->EBMLidName(EBML_ID_CODECPRIVATE)])) {
 450+ if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) {
 451+ $track_entry['codec_private_parsed'] = getid3_riff::ParseBITMAPINFOHEADER($track_entry[$this->EBMLidName(EBML_ID_CODECPRIVATE)]);
 452+ } else {
 453+ $this->warnings[] = 'Unable to parse codec private data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio-video.riff.php"';
 454+ }
 455+ }
 456+ break;
 457+
 458+ case EBML_ID_AUDIO:
 459+ while ($offset < $subelement_end) {
 460+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 461+ $sub_subelement_offset = $offset;
 462+ $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 463+ $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
 464+ $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 465+ $sub_subelement_end = $offset + $sub_subelement_length;
 466+ switch ($sub_subelement_id) {
 467+ case EBML_ID_CHANNELS:
 468+ case EBML_ID_BITDEPTH:
 469+ $track_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 470+ break;
 471+ case EBML_ID_SAMPLINGFREQUENCY:
 472+ case EBML_ID_OUTPUTSAMPLINGFREQUENCY:
 473+ $track_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 474+ break;
 475+ case EBML_ID_CHANNELPOSITIONS:
 476+ $track_entry[$sub_subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), "\x00");
 477+ break;
 478+ default:
 479+ $this->warnings[] = 'Unhandled track.video element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
 480+ break;
 481+ }
 482+ $offset = $sub_subelement_end;
 483+ }
 484+ break;
 485+
 486+ case EBML_ID_CONTENTENCODINGS:
 487+ while ($offset < $subelement_end) {
 488+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 489+ $sub_subelement_offset = $offset;
 490+ $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 491+ $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
 492+ $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 493+ $sub_subelement_end = $offset + $sub_subelement_length;
 494+ switch ($sub_subelement_id) {
 495+ case EBML_ID_CONTENTENCODING:
 496+ while ($offset < $sub_subelement_end) {
 497+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 498+ $sub_sub_subelement_offset = $offset;
 499+ $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 500+ $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
 501+ $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 502+ $sub_sub_subelement_end = $offset + $sub_sub_subelement_length;
 503+ switch ($sub_sub_subelement_id) {
 504+ case EBML_ID_CONTENTENCODINGORDER:
 505+ case EBML_ID_CONTENTENCODINGSCOPE:
 506+ case EBML_ID_CONTENTENCODINGTYPE:
 507+ $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
 508+ break;
 509+ case EBML_ID_CONTENTCOMPRESSION:
 510+ while ($offset < $sub_sub_subelement_end) {
 511+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 512+ $sub_sub_sub_subelement_offset = $offset;
 513+ $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 514+ $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
 515+ $sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 516+ $sub_sub_sub_subelement_end = $offset + $sub_sub_sub_subelement_length;
 517+ switch ($sub_sub_sub_subelement_id) {
 518+ case EBML_ID_CONTENTCOMPALGO:
 519+ $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length));
 520+ break;
 521+ case EBML_ID_CONTENTCOMPSETTINGS:
 522+ $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length);
 523+ break;
 524+ default:
 525+ $this->warnings[] = 'Unhandled track.contentencodings.contentencoding.contentcompression element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 526+ break;
 527+ }
 528+ $offset = $sub_sub_sub_subelement_end;
 529+ }
 530+ break;
 531+
 532+ case EBML_ID_CONTENTENCRYPTION:
 533+ while ($offset < $sub_sub_subelement_end) {
 534+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 535+ $sub_sub_sub_subelement_offset = $offset;
 536+ $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 537+ $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
 538+ $sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 539+ $sub_sub_sub_subelement_end = $offset + $sub_sub_sub_subelement_length;
 540+ switch ($sub_sub_sub_subelement_id) {
 541+ case EBML_ID_CONTENTENCALGO:
 542+ case EBML_ID_CONTENTSIGALGO:
 543+ case EBML_ID_CONTENTSIGHASHALGO:
 544+ $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length));
 545+ break;
 546+ case EBML_ID_CONTENTENCKEYID:
 547+ case EBML_ID_CONTENTSIGNATURE:
 548+ case EBML_ID_CONTENTSIGKEYID:
 549+ $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length);
 550+ break;
 551+ default:
 552+ $this->warnings[] = 'Unhandled track.contentencodings.contentencoding.contentcompression element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 553+ break;
 554+ }
 555+ $offset = $sub_sub_sub_subelement_end;
 556+ }
 557+ break;
 558+
 559+ default:
 560+ $this->warnings[] = 'Unhandled track.contentencodings.contentencoding element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 561+ break;
 562+ }
 563+ $offset = $sub_sub_subelement_end;
 564+ }
 565+ break;
 566+ default:
 567+ $this->warnings[] = 'Unhandled track.contentencodings element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 568+ break;
 569+ }
 570+ $offset = $sub_subelement_end;
 571+ }
 572+ break;
 573+
 574+ default:
 575+ $this->warnings[] = 'Unhandled track element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 576+ break;
 577+ }
 578+ $offset = $subelement_end;
 579+ }
 580+ break;
 581+ default:
 582+ $this->warnings[] = 'Unhandled track element ['.basename(__FILE__).':'.__LINE__.'] ('.$track_entry['id'].'::'.$track_entry['id_name'].') at '.$track_entry['offset'];
 583+ $offset = $track_entry_endoffset;
 584+ break;
 585+ }
 586+ $ThisFileInfo['matroska']['tracks']['tracks'][] = $track_entry;
 587+ }
 588+ break;
 589+
 590+ case EBML_ID_INFO: // Contains the position of other level 1 elements
 591+ $info_entry = array();
 592+ while ($offset < $element_end) {
 593+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 594+ $subelement_offset = $offset;
 595+ $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 596+ $subelement_idname = $this->EBMLidName($subelement_id);
 597+ $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 598+ $subelement_end = $offset + $subelement_length;
 599+ switch ($subelement_id) {
 600+ case EBML_ID_CHAPTERTRANSLATEEDITIONUID:
 601+ case EBML_ID_CHAPTERTRANSLATECODEC:
 602+ case EBML_ID_TIMECODESCALE:
 603+ $info_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
 604+ break;
 605+ case EBML_ID_DURATION:
 606+ $info_entry[$subelement_idname] = getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
 607+ break;
 608+ case EBML_ID_DATEUTC:
 609+ $info_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
 610+ $info_entry[$subelement_idname.'_unix'] = $this->EBMLdate2unix($info_entry[$subelement_idname]);
 611+ break;
 612+ case EBML_ID_SEGMENTUID:
 613+ case EBML_ID_PREVUID:
 614+ case EBML_ID_NEXTUID:
 615+ case EBML_ID_SEGMENTFAMILY:
 616+ case EBML_ID_CHAPTERTRANSLATEID:
 617+ $info_entry[$subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00");
 618+ break;
 619+ case EBML_ID_SEGMENTFILENAME:
 620+ case EBML_ID_PREVFILENAME:
 621+ case EBML_ID_NEXTFILENAME:
 622+ case EBML_ID_TITLE:
 623+ case EBML_ID_MUXINGAPP:
 624+ case EBML_ID_WRITINGAPP:
 625+ $info_entry[$subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00");
 626+ break;
 627+ default:
 628+ $this->warnings[] = 'Unhandled info element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 629+ break;
 630+ }
 631+ $offset = $subelement_end;
 632+ }
 633+ $ThisFileInfo['matroska']['info'][] = $info_entry;
 634+ break;
 635+
 636+ case EBML_ID_CUES:
 637+ $cues_entry = array();
 638+ while ($offset < $element_end) {
 639+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 640+ $subelement_offset = $offset;
 641+ $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 642+ $subelement_idname = $this->EBMLidName($subelement_id);
 643+ $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 644+ $subelement_end = $offset + $subelement_length;
 645+ switch ($subelement_id) {
 646+ case EBML_ID_CUEPOINT:
 647+ $cuepoint_entry = array();
 648+ while ($offset < $subelement_end) {
 649+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 650+ $sub_subelement_offset = $offset;
 651+ $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 652+ $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
 653+ $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 654+ $sub_subelement_end = $offset + $sub_subelement_length;
 655+ switch ($sub_subelement_id) {
 656+ case EBML_ID_CUETRACKPOSITIONS:
 657+ while ($offset < $sub_subelement_end) {
 658+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 659+ $sub_sub_subelement_offset = $offset;
 660+ $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 661+ $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
 662+ $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 663+ $sub_sub_subelement_end = $offset + $sub_sub_subelement_length;
 664+ switch ($sub_sub_subelement_id) {
 665+ case EBML_ID_CUETRACK:
 666+ $cuepoint_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
 667+ break;
 668+ default:
 669+ $this->warnings[] = 'Unhandled cues.cuepoint.cuetrackpositions element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset;
 670+ break;
 671+ }
 672+ $offset = $sub_subelement_end;
 673+ }
 674+ break;
 675+ case EBML_ID_CUETIME:
 676+ $cuepoint_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 677+ break;
 678+ default:
 679+ $this->warnings[] = 'Unhandled cues.cuepoint element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
 680+ break;
 681+ }
 682+ $offset = $sub_subelement_end;
 683+ }
 684+ $cues_entry[] = $cuepoint_entry;
 685+ $offset = $sub_subelement_end;
 686+ break;
 687+ default:
 688+ $this->warnings[] = 'Unhandled cues element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 689+ break;
 690+ }
 691+ $offset = $subelement_end;
 692+ }
 693+ $ThisFileInfo['matroska']['cues'] = $cues_entry;
 694+ break;
 695+
 696+ case EBML_ID_TAGS:
 697+ $tags_entry = array();
 698+ while ($offset < $element_end) {
 699+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 700+ $subelement_offset = $offset;
 701+ $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 702+ $subelement_idname = $this->EBMLidName($subelement_id);
 703+ $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 704+ $subelement_end = $offset + $subelement_length;
 705+ switch ($subelement_id) {
 706+ case EBML_ID_WRITINGAPP:
 707+ $tags_entry[$subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length);
 708+ break;
 709+ case EBML_ID_TAG:
 710+ $tag_entry = array();
 711+ while ($offset < $subelement_end) {
 712+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 713+ $sub_subelement_offset = $offset;
 714+ $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 715+ $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
 716+ $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 717+ $sub_subelement_end = $offset + $sub_subelement_length;
 718+ switch ($sub_subelement_id) {
 719+ case EBML_ID_TARGETS:
 720+ $targets_entry = array();
 721+ while ($offset < $sub_subelement_end) {
 722+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 723+ $sub_sub_subelement_offset = $offset;
 724+ $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 725+ $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
 726+ $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 727+ $sub_sub_subelement_end = $offset + $sub_sub_subelement_length;
 728+ switch ($sub_sub_subelement_id) {
 729+ case EBML_ID_TARGETTYPEVALUE:
 730+ case EBML_ID_EDITIONUID:
 731+ case EBML_ID_CHAPTERUID:
 732+ case EBML_ID_ATTACHMENTUID:
 733+ case EBML_ID_TAGTRACKUID:
 734+ case EBML_ID_TAGCHAPTERUID:
 735+ $targets_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
 736+ break;
 737+ default:
 738+ $this->warnings[] = 'Unhandled tag.targets element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset;
 739+ break;
 740+ }
 741+ $offset = $sub_sub_subelement_end;
 742+ }
 743+ $tag_entry[$sub_subelement_idname][] = $targets_entry;
 744+ break;
 745+ case EBML_ID_SIMPLETAG:
 746+ $simpletag_entry = array();
 747+ while ($offset < $sub_subelement_end) {
 748+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 749+ $sub_sub_subelement_offset = $offset;
 750+ $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 751+ $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
 752+ $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 753+ $sub_sub_subelement_end = $offset + $sub_sub_subelement_length;
 754+ switch ($sub_sub_subelement_id) {
 755+ case EBML_ID_TAGNAME:
 756+ case EBML_ID_TAGLANGUAGE:
 757+ case EBML_ID_TAGSTRING:
 758+ case EBML_ID_TAGBINARY:
 759+ $simpletag_entry[$sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length);
 760+ break;
 761+ case EBML_ID_TAGDEFAULT:
 762+ $simpletag_entry[$sub_sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
 763+ break;
 764+ default:
 765+ $this->warnings[] = 'Unhandled tag.simpletag element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset;
 766+ break;
 767+ }
 768+ $offset = $sub_sub_subelement_end;
 769+ }
 770+ $tag_entry[$sub_subelement_idname][] = $simpletag_entry;
 771+ break;
 772+ case EBML_ID_TARGETTYPE:
 773+ $tag_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length);
 774+ break;
 775+ case EBML_ID_TRACKUID:
 776+ $tag_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 777+ break;
 778+ default:
 779+ $this->warnings[] = 'Unhandled tags.tag element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
 780+ break;
 781+ }
 782+ $offset = $sub_subelement_end;
 783+ }
 784+ $tags_entry['tags'][] = $tag_entry;
 785+ $offset = $sub_subelement_end;
 786+ break;
 787+ default:
 788+ $this->warnings[] = 'Unhandled tags element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 789+ break;
 790+ }
 791+ $offset = $subelement_end;
 792+ }
 793+ $ThisFileInfo['matroska']['tags'] = $tags_entry;
 794+ break;
 795+
 796+
 797+ case EBML_ID_ATTACHMENTS:
 798+ while ($offset < $element_end) {
 799+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 800+ $subelement_offset = $offset;
 801+ $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 802+ $subelement_idname = $this->EBMLidName($subelement_id);
 803+ $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 804+ $subelement_end = $offset + $subelement_length;
 805+ switch ($subelement_id) {
 806+ case EBML_ID_ATTACHEDFILE:
 807+ $attachedfile_entry = array();
 808+ while ($offset < $subelement_end) {
 809+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 810+ $sub_subelement_offset = $offset;
 811+ $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 812+ $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
 813+ $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 814+ $sub_subelement_end = $offset + $sub_subelement_length;
 815+ switch ($sub_subelement_id) {
 816+ case EBML_ID_FILEDESCRIPTION:
 817+ case EBML_ID_FILENAME:
 818+ case EBML_ID_FILEMIMETYPE:
 819+ $attachedfile_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length);
 820+ break;
 821+
 822+ case EBML_ID_FILEDATA:
 823+ $attachedfile_entry['data_offset'] = $offset;
 824+ $attachedfile_entry['data_length'] = $sub_subelement_length;
 825+ if ($sub_subelement_length < 1024) {
 826+ $attachedfile_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length);
 827+ }
 828+ break;
 829+
 830+ case EBML_ID_FILEUID:
 831+ $attachedfile_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 832+ break;
 833+
 834+ default:
 835+ $this->warnings[] = 'Unhandled attachment.attachedfile element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
 836+ break;
 837+ }
 838+ $offset = $sub_subelement_end;
 839+ }
 840+ $ThisFileInfo['matroska']['attachments'][] = $attachedfile_entry;
 841+ $offset = $sub_subelement_end;
 842+ break;
 843+ default:
 844+ $this->warnings[] = 'Unhandled tags element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 845+ break;
 846+ }
 847+ $offset = $subelement_end;
 848+ }
 849+ break;
 850+
 851+
 852+ case EBML_ID_CHAPTERS: // not important to us, contains mostly actual audio/video data, ignore
 853+ while ($offset < $element_end) {
 854+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 855+ $subelement_offset = $offset;
 856+ $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 857+ $subelement_idname = $this->EBMLidName($subelement_id);
 858+ $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 859+ $subelement_end = $offset + $subelement_length;
 860+ switch ($subelement_id) {
 861+ case EBML_ID_EDITIONENTRY:
 862+ $editionentry_entry = array();
 863+ while ($offset < $subelement_end) {
 864+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 865+ $sub_subelement_offset = $offset;
 866+ $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 867+ $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
 868+ $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 869+ $sub_subelement_end = $offset + $sub_subelement_length;
 870+ switch ($sub_subelement_id) {
 871+ case EBML_ID_EDITIONUID:
 872+ $editionentry_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 873+ break;
 874+ case EBML_ID_EDITIONFLAGHIDDEN:
 875+ case EBML_ID_EDITIONFLAGDEFAULT:
 876+ case EBML_ID_EDITIONFLAGORDERED:
 877+ $editionentry_entry[$sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 878+ break;
 879+ case EBML_ID_CHAPTERATOM:
 880+ $chapteratom_entry = array();
 881+ while ($offset < $sub_subelement_end) {
 882+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 883+ $sub_sub_subelement_offset = $offset;
 884+ $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 885+ $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
 886+ $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 887+ $sub_sub_subelement_end = $offset + $sub_sub_subelement_length;
 888+ switch ($sub_sub_subelement_id) {
 889+ case EBML_ID_CHAPTERSEGMENTUID:
 890+ case EBML_ID_CHAPTERSEGMENTEDITIONUID:
 891+ $chapteratom_entry[$sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length);
 892+ break;
 893+ case EBML_ID_CHAPTERFLAGENABLED:
 894+ case EBML_ID_CHAPTERFLAGHIDDEN:
 895+ $chapteratom_entry[$sub_sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
 896+ break;
 897+ case EBML_ID_CHAPTERUID:
 898+ case EBML_ID_CHAPTERTIMESTART:
 899+ case EBML_ID_CHAPTERTIMEEND:
 900+ $chapteratom_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
 901+ break;
 902+ case EBML_ID_CHAPTERTRACK:
 903+ $chaptertrack_entry = array();
 904+ while ($offset < $sub_sub_subelement_end) {
 905+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 906+ $sub_sub_sub_subelement_offset = $offset;
 907+ $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 908+ $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
 909+ $sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 910+ $sub_sub_sub_subelement_end = $offset + $sub_sub_sub_subelement_length;
 911+ switch ($sub_sub_sub_subelement_id) {
 912+ case EBML_ID_CHAPTERTRACKNUMBER:
 913+ $chaptertrack_entry[$sub_sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length));
 914+ break;
 915+ default:
 916+ $this->warnings[] = 'Unhandled chapters.editionentry.chapteratom.chaptertrack element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_sub_subelement_id.'::'.$sub_sub_sub_subelement_idname.') at '.$sub_sub_sub_subelement_offset;
 917+ break;
 918+ }
 919+ $offset = $sub_sub_sub_subelement_end;
 920+ }
 921+ $chapteratom_entry[$sub_sub_subelement_idname][] = $chaptertrack_entry;
 922+ break;
 923+ case EBML_ID_CHAPTERDISPLAY:
 924+ $chapterdisplay_entry = array();
 925+ while ($offset < $sub_sub_subelement_end) {
 926+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 927+ $sub_sub_sub_subelement_offset = $offset;
 928+ $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 929+ $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_sub_subelement_id);
 930+ $sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 931+ $sub_sub_sub_subelement_end = $offset + $sub_sub_sub_subelement_length;
 932+ switch ($sub_sub_sub_subelement_id) {
 933+ case EBML_ID_CHAPSTRING:
 934+ case EBML_ID_CHAPLANGUAGE:
 935+ case EBML_ID_CHAPCOUNTRY:
 936+ $chapterdisplay_entry[$sub_sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length);
 937+ break;
 938+ default:
 939+ $this->warnings[] = 'Unhandled chapters.editionentry.chapteratom.chapterdisplay element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_sub_subelement_id.'::'.$sub_sub_sub_subelement_idname.') at '.$sub_sub_sub_subelement_offset;
 940+ break;
 941+ }
 942+ $offset = $sub_sub_sub_subelement_end;
 943+ }
 944+ $chapteratom_entry[$sub_sub_subelement_idname][] = $chapterdisplay_entry;
 945+ break;
 946+ default:
 947+ $this->warnings[] = 'Unhandled chapters.editionentry.chapteratom element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset;
 948+ break;
 949+ }
 950+ $offset = $sub_sub_subelement_end;
 951+ }
 952+ $editionentry_entry[$sub_subelement_idname][] = $chapteratom_entry;
 953+ break;
 954+ default:
 955+ $this->warnings[] = 'Unhandled chapters.editionentry element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
 956+ break;
 957+ }
 958+ $offset = $sub_subelement_end;
 959+ }
 960+ $ThisFileInfo['matroska']['chapters'][] = $editionentry_entry;
 961+ $offset = $sub_subelement_end;
 962+ break;
 963+ default:
 964+ $this->warnings[] = 'Unhandled chapters element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 965+ break;
 966+ }
 967+ $offset = $subelement_end;
 968+ }
 969+ break;
 970+
 971+
 972+ case EBML_ID_VOID: // padding, ignore
 973+ $void_entry = array();
 974+ $void_entry['offset'] = $offset;
 975+ $ThisFileInfo['matroska']['void'][] = $void_entry;
 976+ break;
 977+
 978+ case EBML_ID_CLUSTER: // not important to us, contains mostly actual audio/video data, ignore
 979+ $cluster_entry = array();
 980+ while ($offset < $element_end) {
 981+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 982+ $subelement_offset = $offset;
 983+ $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 984+ $subelement_idname = $this->EBMLidName($subelement_id);
 985+ $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 986+ $subelement_end = $offset + $subelement_length;
 987+ switch ($subelement_id) {
 988+ case EBML_ID_CLUSTERTIMECODE:
 989+ case EBML_ID_CLUSTERPOSITION:
 990+ case EBML_ID_CLUSTERPREVSIZE:
 991+ $cluster_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
 992+ break;
 993+
 994+ case EBML_ID_CLUSTERSILENTTRACKS:
 995+ $cluster_silent_tracks = array();
 996+ while ($offset < $subelement_end) {
 997+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 998+ $sub_subelement_offset = $offset;
 999+ $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 1000+ $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
 1001+ $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 1002+ $sub_subelement_end = $offset + $sub_subelement_length;
 1003+ switch ($sub_subelement_id) {
 1004+ case EBML_ID_CLUSTERSILENTTRACKNUMBER:
 1005+ $cluster_silent_tracks[] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 1006+ break;
 1007+ default:
 1008+ $this->warnings[] = 'Unhandled clusters.silenttracks element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
 1009+ break;
 1010+ }
 1011+ $offset = $sub_subelement_end;
 1012+ }
 1013+ $cluster_entry[$subelement_idname][] = $cluster_silent_tracks;
 1014+ $offset = $sub_subelement_end;
 1015+ break;
 1016+
 1017+ case EBML_ID_CLUSTERBLOCKGROUP:
 1018+ $cluster_block_group = array('offset'=>$offset);
 1019+ while ($offset < $subelement_end) {
 1020+ $this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
 1021+ $sub_subelement_offset = $offset;
 1022+ $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 1023+ $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
 1024+ $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 1025+ $sub_subelement_end = $offset + $sub_subelement_length;
 1026+ switch ($sub_subelement_id) {
 1027+ case EBML_ID_CLUSTERBLOCK:
 1028+ $cluster_block_data = array();
 1029+ $cluster_block_data['tracknumber'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 1030+ $cluster_block_data['timecode'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 2));
 1031+ $offset += 2;
 1032+ // unsure whether this is 1 octect or 2 octets? (http://matroska.org/technical/specs/index.html#block_structure)
 1033+ $cluster_block_data['flags_raw'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
 1034+ $offset += 1;
 1035+ //$cluster_block_data['flags']['reserved1'] = (($cluster_block_data['flags_raw'] & 0xF0) >> 4);
 1036+ $cluster_block_data['flags']['invisible'] = (bool) (($cluster_block_data['flags_raw'] & 0x08) >> 3);
 1037+ $cluster_block_data['flags']['lacing'] = (($cluster_block_data['flags_raw'] & 0x06) >> 1);
 1038+ //$cluster_block_data['flags']['reserved2'] = (($cluster_block_data['flags_raw'] & 0x01) >> 0);
 1039+ $cluster_block_data['flags']['lacing_type'] = $this->MatroskaBlockLacingType($cluster_block_data['flags']['lacing']);
 1040+ if ($cluster_block_data['flags']['lacing'] != 0) {
 1041+ $cluster_block_data['lace_frames'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1)); // Number of frames in the lace-1 (uint8)
 1042+ $offset += 1;
 1043+ if ($cluster_block_data['flags']['lacing'] != 2) {
 1044+ $cluster_block_data['lace_frames'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1)); // 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).
 1045+ $offset += 1;
 1046+ }
 1047+ }
 1048+ if (!isset($ThisFileInfo['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']])) {
 1049+ $ThisFileInfo['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']] = $offset;
 1050+ }
 1051+ $cluster_block_group[$sub_subelement_idname] = $cluster_block_data;
 1052+ break;
 1053+
 1054+ case EBML_ID_CLUSTERREFERENCEPRIORITY: // unsigned-int
 1055+ case EBML_ID_CLUSTERBLOCKDURATION: // unsigned-int
 1056+ $cluster_block_group[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
 1057+ break;
 1058+
 1059+ case EBML_ID_CLUSTERREFERENCEBLOCK: // signed-int
 1060+ $cluster_block_group[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), false, true);
 1061+ break;
 1062+
 1063+ default:
 1064+ $this->warnings[] = 'Unhandled clusters.blockgroup element ['.basename(__FILE__).':'.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
 1065+ break;
 1066+ }
 1067+ $offset = $sub_subelement_end;
 1068+ }
 1069+ $cluster_entry[$subelement_idname][] = $cluster_block_group;
 1070+ $offset = $sub_subelement_end;
 1071+ break;
 1072+
 1073+ case EBML_ID_CLUSTERSIMPLEBLOCK:
 1074+ // http://www.matroska.org/technical/specs/index.html#simpleblock_structure
 1075+ $cluster_block_data = array();
 1076+ $cluster_block_data['tracknumber'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
 1077+ $cluster_block_data['timecode'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 2));
 1078+ $offset += 2;
 1079+ $cluster_block_data['flags_raw'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
 1080+ $offset += 1;
 1081+ $cluster_block_data['flags']['keyframe'] = (($cluster_block_data['flags_raw'] & 0x80) >> 7);
 1082+ $cluster_block_data['flags']['reserved1'] = (($cluster_block_data['flags_raw'] & 0x70) >> 4);
 1083+ $cluster_block_data['flags']['invisible'] = (($cluster_block_data['flags_raw'] & 0x08) >> 3);
 1084+ $cluster_block_data['flags']['lacing'] = (($cluster_block_data['flags_raw'] & 0x06) >> 1); // 00=no lacing; 01=Xiph lacing; 11=EBML lacing; 10=fixed-size lacing
 1085+ $cluster_block_data['flags']['discardable'] = (($cluster_block_data['flags_raw'] & 0x01));
 1086+
 1087+ if ($cluster_block_data['flags']['lacing'] > 0) {
 1088+ $cluster_block_data['lace_frames'] = 1 + getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
 1089+ $offset += 1;
 1090+ if ($cluster_block_data['flags']['lacing'] != 0x02) {
 1091+ // *This is not used with Fixed-size lacing as it is calculated automatically from (total size of lace) / (number of frames in lace).
 1092+ $cluster_block_data['lace_frame_size'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
 1093+ $offset += 1;
 1094+ }
 1095+ }
 1096+
 1097+ if (!isset($ThisFileInfo['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']])) {
 1098+ $ThisFileInfo['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']] = $offset;
 1099+ }
 1100+ $cluster_block_group[$sub_subelement_idname] = $cluster_block_data;
 1101+ break;
 1102+
 1103+ default:
 1104+ $this->warnings[] = 'Unhandled cluster element ['.basename(__FILE__).':'.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
 1105+ break;
 1106+ }
 1107+ $offset = $subelement_end;
 1108+ }
 1109+ $ThisFileInfo['matroska']['cluster'][] = $cluster_entry;
 1110+
 1111+ // check to see if all the data we need exists already, if so, break out of the loop
 1112+ if (isset($ThisFileInfo['matroska']['info']) && is_array($ThisFileInfo['matroska']['info'])) {
 1113+ if (isset($ThisFileInfo['matroska']['tracks']['tracks']) && is_array($ThisFileInfo['matroska']['tracks']['tracks'])) {
 1114+ break 2;
 1115+ }
 1116+ }
 1117+ break;
 1118+
 1119+ default:
 1120+ if ($element_data['id_name'] == dechex($element_data['id'])) {
 1121+ $ThisFileInfo['error'][] = 'Unhandled segment ['.basename(__FILE__).':'.__LINE__.'] ('.$element_data['id'].') at '.$element_data_offset;
 1122+ } else {
 1123+ $this->warnings[] = 'Unhandled segment ['.basename(__FILE__).':'.__LINE__.'] ('.$element_data['id'].'::'.$element_data['id_name'].') at '.$element_data['offset'];
 1124+ }
 1125+ break;
 1126+ }
 1127+ $offset = $element_end;
 1128+ }
 1129+ break;
 1130+
 1131+
 1132+ default:
 1133+ $ThisFileInfo['error'][] = 'Unhandled chunk ['.basename(__FILE__).':'.__LINE__.'] ('.$top_element_id.') at '.$offset;
 1134+ break;
 1135+ }
 1136+ $offset = $top_element_endoffset;
 1137+ }
 1138+
 1139+
 1140+
 1141+ if (isset($ThisFileInfo['matroska']['info']) && is_array($ThisFileInfo['matroska']['info'])) {
 1142+ foreach ($ThisFileInfo['matroska']['info'] as $key => $infoarray) {
 1143+ if (isset($infoarray['Duration'])) {
 1144+ // TimecodeScale is how many nanoseconds each Duration unit is
 1145+ $ThisFileInfo['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000);
 1146+ break;
 1147+ }
 1148+ }
 1149+ }
 1150+ if (isset($ThisFileInfo['matroska']['tracks']['tracks']) && is_array($ThisFileInfo['matroska']['tracks']['tracks'])) {
 1151+ foreach ($ThisFileInfo['matroska']['tracks']['tracks'] as $key => $trackarray) {
 1152+ $track_info = array();
 1153+ switch (isset($trackarray['TrackType']) ? $trackarray['TrackType'] : '') {
 1154+ case 1: // Video
 1155+ if (!empty($trackarray['PixelWidth'])) { $track_info['resolution_x'] = $trackarray['PixelWidth']; }
 1156+ if (!empty($trackarray['PixelHeight'])) { $track_info['resolution_y'] = $trackarray['PixelHeight']; }
 1157+ if (!empty($trackarray['DisplayWidth'])) { $track_info['display_x'] = $trackarray['DisplayWidth']; }
 1158+ if (!empty($trackarray['DisplayHeight'])) { $track_info['display_y'] = $trackarray['DisplayHeight']; }
 1159+ if (!empty($trackarray['DefaultDuration'])) { $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3); }
 1160+ if (!empty($trackarray['CodecID'])) { $track_info['dataformat'] = $this->MatroskaCodecIDtoCommonName($trackarray['CodecID']); }
 1161+ if (!empty($trackarray['codec_private_parsed']['fourcc'])) { $track_info['fourcc'] = $trackarray['codec_private_parsed']['fourcc']; }
 1162+ $ThisFileInfo['video']['streams'][] = $track_info;
 1163+ if (isset($track_info['resolution_x']) && empty($ThisFileInfo['video']['resolution_x'])) {
 1164+ foreach ($track_info as $key => $value) {
 1165+ $ThisFileInfo['video'][$key] = $value;
 1166+ }
 1167+ }
 1168+ break;
 1169+ case 2: // Audio
 1170+ if (!empty($trackarray['CodecID'])) { $track_info['dataformat'] = $this->MatroskaCodecIDtoCommonName($trackarray['CodecID']); }
 1171+ if (!empty($trackarray['SamplingFrequency'])) { $track_info['sample_rate'] = $trackarray['SamplingFrequency']; }
 1172+ if (!empty($trackarray['Channels'])) { $track_info['channels'] = $trackarray['Channels']; }
 1173+ if (!empty($trackarray['BitDepth'])) { $track_info['bits_per_sample'] = $trackarray['BitDepth']; }
 1174+ switch (isset($trackarray[$this->EBMLidName(EBML_ID_CODECID)]) ? $trackarray[$this->EBMLidName(EBML_ID_CODECID)] : '') {
 1175+ case 'A_PCM/INT/LIT':
 1176+ case 'A_PCM/INT/BIG':
 1177+ $track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth'];
 1178+ break;
 1179+
 1180+ case 'A_AC3':
 1181+ if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) {
 1182+ if (isset($ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) {
 1183+ $ac3_thisfileinfo = array('avdataoffset'=>$ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']]);
 1184+ $getid3_ac3 = new getid3_ac3($fd, $ac3_thisfileinfo);
 1185+ $ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $ac3_thisfileinfo;
 1186+ if (!empty($ac3_thisfileinfo['error'])) {
 1187+ foreach ($ac3_thisfileinfo['error'] as $newerror) {
 1188+ $this->warnings[] = 'getid3_ac3() says: ['.$newerror.']';
 1189+ }
 1190+ }
 1191+ if (!empty($ac3_thisfileinfo['warning'])) {
 1192+ foreach ($ac3_thisfileinfo['warning'] as $newerror) {
 1193+ $this->warnings[] = 'getid3_ac3() says: ['.$newerror.']';
 1194+ }
 1195+ }
 1196+ if (isset($ac3_thisfileinfo['audio']) && is_array($ac3_thisfileinfo['audio'])) {
 1197+ foreach ($ac3_thisfileinfo['audio'] as $key => $value) {
 1198+ $track_info[$key] = $value;
 1199+ }
 1200+ }
 1201+ unset($ac3_thisfileinfo);
 1202+ unset($getid3_ac3);
 1203+ } else {
 1204+ $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $ThisFileInfo[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set';
 1205+ }
 1206+ } else {
 1207+ $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.ac3.php"';
 1208+ }
 1209+ break;
 1210+
 1211+ case 'A_DTS':
 1212+ $dts_offset = $ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']];
 1213+ // this is a NASTY hack, but sometimes audio data is off by a byte or two and not sure why, email info@getid3.org if you can explain better
 1214+ fseek($fd, $dts_offset, SEEK_SET);
 1215+ $magic_test = fread($fd, 8);
 1216+ for ($i = 0; $i < 4; $i++) {
 1217+ // look to see if DTS "magic" is here, if so adjust offset by that many bytes
 1218+ if (substr($magic_test, $i, 4) == "\x7F\xFE\x80\x01") {
 1219+ $dts_offset += $i;
 1220+ break;
 1221+ }
 1222+ }
 1223+ if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, false)) {
 1224+ $dts_thisfileinfo = array('avdataoffset'=>$dts_offset);
 1225+ $getid3_dts = new getid3_dts($fd, $dts_thisfileinfo);
 1226+ $ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $dts_thisfileinfo;
 1227+ if (!empty($dts_thisfileinfo['error'])) {
 1228+ foreach ($dts_thisfileinfo['error'] as $newerror) {
 1229+ $this->warnings[] = 'getid3_dts() says: ['.$newerror.']';
 1230+ }
 1231+ }
 1232+ if (!empty($dts_thisfileinfo['warning'])) {
 1233+ foreach ($dts_thisfileinfo['warning'] as $newerror) {
 1234+ $this->warnings[] = 'getid3_dts() says: ['.$newerror.']';
 1235+ }
 1236+ }
 1237+ if (isset($dts_thisfileinfo['audio']) && is_array($dts_thisfileinfo['audio'])) {
 1238+ foreach ($dts_thisfileinfo['audio'] as $key => $value) {
 1239+ $track_info[$key] = $value;
 1240+ }
 1241+ }
 1242+ unset($dts_thisfileinfo);
 1243+ unset($getid3_dts);
 1244+ } else {
 1245+ $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.dts.php"';
 1246+ }
 1247+ break;
 1248+
 1249+ case 'A_AAC':
 1250+$this->warnings[] = 'This version of getID3() [v'.GETID3_VERSION.'] has problems parsing AAC audio in Matroska containers ['.basename(__FILE__).':'.__LINE__.']';
 1251+ if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.aac.php', __FILE__, false)) {
 1252+ // $aac_thisfileinfo = array('avdataoffset'=>$ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']]);
 1253+ // $getid3_aac = new getid3_aac($fd, $aac_thisfileinfo);
 1254+ // $ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $aac_thisfileinfo;
 1255+ // if (isset($aac_thisfileinfo['audio']) && is_array($aac_thisfileinfo['audio'])) {
 1256+ // foreach ($aac_thisfileinfo['audio'] as $key => $value) {
 1257+ // $track_info[$key] = $value;
 1258+ // }
 1259+ // }
 1260+ // unset($aac_thisfileinfo);
 1261+ // unset($getid3_aac);
 1262+ } else {
 1263+ $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.aac.php"';
 1264+ }
 1265+ break;
 1266+
 1267+ case 'A_MPEG/L3':
 1268+ if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, false)) {
 1269+ $mp3_thisfileinfo = array(
 1270+ 'avdataoffset' => $ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']],
 1271+ 'avdataend' => $ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']] + 1024,
 1272+ );
 1273+ $getid3_mp3 = new getid3_mp3($fd, $mp3_thisfileinfo);
 1274+ $getid3_mp3->allow_bruteforce = true;
 1275+ //getid3_mp3::getOnlyMPEGaudioInfo($fd, $mp3_thisfileinfo, $offset, false);
 1276+ $ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $mp3_thisfileinfo;
 1277+ if (!empty($mp3_thisfileinfo['error'])) {
 1278+ foreach ($mp3_thisfileinfo['error'] as $newerror) {
 1279+ $this->warnings[] = 'getid3_mp3() says: ['.$newerror.']';
 1280+ }
 1281+ }
 1282+ if (!empty($mp3_thisfileinfo['warning'])) {
 1283+ foreach ($mp3_thisfileinfo['warning'] as $newerror) {
 1284+ $this->warnings[] = 'getid3_mp3() says: ['.$newerror.']';
 1285+ }
 1286+ }
 1287+ if (isset($mp3_thisfileinfo['audio']) && is_array($mp3_thisfileinfo['audio'])) {
 1288+ foreach ($mp3_thisfileinfo['audio'] as $key => $value) {
 1289+ $track_info[$key] = $value;
 1290+ }
 1291+ }
 1292+ unset($mp3_thisfileinfo);
 1293+ unset($getid3_mp3);
 1294+ } else {
 1295+ $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.mp3.php"';
 1296+ }
 1297+ break;
 1298+
 1299+ case 'A_VORBIS':
 1300+ if (isset($trackarray['CodecPrivate'])) {
 1301+ // this is a NASTY hack, email info@getid3.org if you have a better idea how to get this info out
 1302+ $found_vorbis = false;
 1303+ for ($vorbis_offset = 1; $vorbis_offset < 16; $vorbis_offset++) {
 1304+ if (substr($trackarray['CodecPrivate'], $vorbis_offset, 6) == 'vorbis') {
 1305+ $vorbis_offset--;
 1306+ $found_vorbis = true;
 1307+ break;
 1308+ }
 1309+ }
 1310+ if ($found_vorbis) {
 1311+ if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, false)) {
 1312+ $vorbis_fileinfo = array();
 1313+ $oggpageinfo['page_seqno'] = 0;
 1314+ getid3_ogg::ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $vorbis_fileinfo, $oggpageinfo);
 1315+ $ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $vorbis_fileinfo;
 1316+ if (!empty($vorbis_fileinfo['error'])) {
 1317+ foreach ($vorbis_fileinfo['error'] as $newerror) {
 1318+ $this->warnings[] = 'getid3_ogg() says: ['.$newerror.']';
 1319+ }
 1320+ }
 1321+ if (!empty($vorbis_fileinfo['warning'])) {
 1322+ foreach ($vorbis_fileinfo['warning'] as $newerror) {
 1323+ $this->warnings[] = 'getid3_ogg() says: ['.$newerror.']';
 1324+ }
 1325+ }
 1326+ if (isset($vorbis_fileinfo['audio']) && is_array($vorbis_fileinfo['audio'])) {
 1327+ foreach ($vorbis_fileinfo['audio'] as $key => $value) {
 1328+ $track_info[$key] = $value;
 1329+ }
 1330+ }
 1331+ if (!empty($vorbis_fileinfo['ogg']['bitrate_average'])) {
 1332+ $track_info['bitrate'] = $vorbis_fileinfo['ogg']['bitrate_average'];
 1333+ } elseif (!empty($vorbis_fileinfo['ogg']['bitrate_nominal'])) {
 1334+ $track_info['bitrate'] = $vorbis_fileinfo['ogg']['bitrate_nominal'];
 1335+ }
 1336+ unset($vorbis_fileinfo);
 1337+ unset($oggpageinfo);
 1338+ } else {
 1339+ $this->warnings[] = 'Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.ogg.php"';
 1340+ }
 1341+ } else {
 1342+ }
 1343+ } else {
 1344+ }
 1345+ break;
 1346+
 1347+ default:
 1348+ $this->warnings[] = 'Unhandled audio type "'.(isset($trackarray[$this->EBMLidName(EBML_ID_CODECID)]) ? $trackarray[$this->EBMLidName(EBML_ID_CODECID)] : '').'"';
 1349+ break;
 1350+ }
 1351+
 1352+
 1353+ $ThisFileInfo['audio']['streams'][] = $track_info;
 1354+ if (isset($track_info['dataformat']) && empty($ThisFileInfo['audio']['dataformat'])) {
 1355+ foreach ($track_info as $key => $value) {
 1356+ $ThisFileInfo['audio'][$key] = $value;
 1357+ }
 1358+ }
 1359+ break;
 1360+ default:
 1361+ // ignore, do nothing
 1362+ break;
 1363+ }
 1364+ }
 1365+ }
 1366+
 1367+ if ($this->hide_clusters) {
 1368+ // too much data returned that is usually not useful
 1369+ if (isset($ThisFileInfo['matroska']['segments']) && is_array($ThisFileInfo['matroska']['segments'])) {
 1370+ foreach ($ThisFileInfo['matroska']['segments'] as $key => $segmentsarray) {
 1371+ if ($segmentsarray['id'] == EBML_ID_CLUSTER) {
 1372+ unset($ThisFileInfo['matroska']['segments'][$key]);
 1373+ }
 1374+ }
 1375+ }
 1376+ if (isset($ThisFileInfo['matroska']['seek']) && is_array($ThisFileInfo['matroska']['seek'])) {
 1377+ foreach ($ThisFileInfo['matroska']['seek'] as $key => $seekarray) {
 1378+ if ($seekarray['target_id'] == EBML_ID_CLUSTER) {
 1379+ unset($ThisFileInfo['matroska']['seek'][$key]);
 1380+ }
 1381+ }
 1382+ }
 1383+ //unset($ThisFileInfo['matroska']['cluster']);
 1384+ //unset($ThisFileInfo['matroska']['track_data_offsets']);
 1385+ }
 1386+
 1387+ if (!empty($ThisFileInfo['video']['streams'])) {
 1388+ $ThisFileInfo['mime_type'] = 'video/x-matroska';
 1389+ } elseif (!empty($ThisFileInfo['video']['streams'])) {
 1390+ $ThisFileInfo['mime_type'] = 'audio/x-matroska';
 1391+ } elseif (isset($ThisFileInfo['mime_type'])) {
 1392+ unset($ThisFileInfo['mime_type']);
 1393+ }
 1394+
 1395+ foreach ($this->warnings as $key => $value) {
 1396+ $ThisFileInfo['warning'][] = $value;
 1397+ }
 1398+
 1399+ return true;
 1400+ }
 1401+
 1402+
 1403+///////////////////////////////////////
 1404+
 1405+
 1406+ function EnsureBufferHasEnoughData(&$fd, &$EBMLdata, &$offset, &$EBMLdata_offset) {
 1407+ $min_data = 1024;
 1408+ if ($offset > 2147450880) { // 2^31 - 2^15 (2G-32k)
 1409+ $offset = pow(2,63);
 1410+ return false;
 1411+ } elseif (($offset - $EBMLdata_offset) >= (strlen($EBMLdata) - $min_data)) {
 1412+ fseek($fd, $offset, SEEK_SET);
 1413+ $EBMLdata_offset = ftell($fd);
 1414+ $EBMLdata = fread($fd, $this->read_buffer_size);
 1415+ }
 1416+ return true;
 1417+ }
 1418+
 1419+ function readEBMLint(&$string, &$offset, $dataoffset=0) {
 1420+ $actual_offset = $offset - $dataoffset;
 1421+ if ($offset > 2147450880) { // 2^31 - 2^15 (2G-32k)
 1422+ $this->warnings[] = 'aborting readEBMLint() because $offset larger than 2GB';
 1423+ return false;
 1424+ } elseif ($actual_offset >= strlen($string)) {
 1425+ $this->warnings[] = '$actual_offset > $string in readEBMLint($string['.strlen($string).'], '.$offset.', '.$dataoffset.')';
 1426+ return false;
 1427+ } elseif ($actual_offset < 0) {
 1428+ $this->warnings[] = '$actual_offset < 0 in readEBMLint($string['.strlen($string).'], '.$offset.', '.$dataoffset.')';
 1429+ return false;
 1430+ }
 1431+ $first_byte_int = ord($string{$actual_offset});
 1432+ if (0x80 & $first_byte_int) {
 1433+ $length = 1;
 1434+ } elseif (0x40 & $first_byte_int) {
 1435+ $length = 2;
 1436+ } elseif (0x20 & $first_byte_int) {
 1437+ $length = 3;
 1438+ } elseif (0x10 & $first_byte_int) {
 1439+ $length = 4;
 1440+ } elseif (0x08 & $first_byte_int) {
 1441+ $length = 5;
 1442+ } elseif (0x04 & $first_byte_int) {
 1443+ $length = 6;
 1444+ } elseif (0x02 & $first_byte_int) {
 1445+ $length = 7;
 1446+ } elseif (0x01 & $first_byte_int) {
 1447+ $length = 8;
 1448+ } else {
 1449+ $offset = pow(2,63); // abort processing, skip to end of file
 1450+ $this->warnings[] = 'invalid EBML integer (leading 0x00) at '.$offset;
 1451+ return false;
 1452+ }
 1453+ $int_value = $this->EBML2Int(substr($string, $actual_offset, $length));
 1454+ $offset += $length;
 1455+ return $int_value;
 1456+ }
 1457+
 1458+ function EBML2Int($EBMLstring) {
 1459+ // http://matroska.org/specs/
 1460+
 1461+ // Element ID coded with an UTF-8 like system:
 1462+ // 1xxx xxxx - Class A IDs (2^7 -2 possible values) (base 0x8X)
 1463+ // 01xx xxxx xxxx xxxx - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX)
 1464+ // 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX)
 1465+ // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX)
 1466+ // Values with all x at 0 and 1 are reserved (hence the -2).
 1467+
 1468+ // Data size, in octets, is also coded with an UTF-8 like system :
 1469+ // 1xxx xxxx - value 0 to 2^7-2
 1470+ // 01xx xxxx xxxx xxxx - value 0 to 2^14-2
 1471+ // 001x xxxx xxxx xxxx xxxx xxxx - value 0 to 2^21-2
 1472+ // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^28-2
 1473+ // 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^35-2
 1474+ // 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^42-2
 1475+ // 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^49-2
 1476+ // 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^56-2
 1477+
 1478+ $first_byte_int = ord($EBMLstring{0});
 1479+ if (0x80 & $first_byte_int) {
 1480+ $EBMLstring{0} = chr($first_byte_int & 0x7F);
 1481+ } elseif (0x40 & $first_byte_int) {
 1482+ $EBMLstring{0} = chr($first_byte_int & 0x3F);
 1483+ } elseif (0x20 & $first_byte_int) {
 1484+ $EBMLstring{0} = chr($first_byte_int & 0x1F);
 1485+ } elseif (0x10 & $first_byte_int) {
 1486+ $EBMLstring{0} = chr($first_byte_int & 0x0F);
 1487+ } elseif (0x08 & $first_byte_int) {
 1488+ $EBMLstring{0} = chr($first_byte_int & 0x07);
 1489+ } elseif (0x04 & $first_byte_int) {
 1490+ $EBMLstring{0} = chr($first_byte_int & 0x03);
 1491+ } elseif (0x02 & $first_byte_int) {
 1492+ $EBMLstring{0} = chr($first_byte_int & 0x01);
 1493+ } elseif (0x01 & $first_byte_int) {
 1494+ $EBMLstring{0} = chr($first_byte_int & 0x00);
 1495+ } else {
 1496+ return false;
 1497+ }
 1498+ return getid3_lib::BigEndian2Int($EBMLstring);
 1499+ }
 1500+
 1501+
 1502+ function EBMLdate2unix($EBMLdatestamp) {
 1503+ // Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC)
 1504+ // 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC
 1505+ return round(($EBMLdatestamp / 1000000000) + 978307200);
 1506+ }
 1507+
 1508+
 1509+ function MatroskaBlockLacingType($lacingtype) {
 1510+ // http://matroska.org/technical/specs/index.html#block_structure
 1511+ static $MatroskaBlockLacingType = array();
 1512+ if (empty($MatroskaBlockLacingType)) {
 1513+ $MatroskaBlockLacingType[0x00] = 'no lacing';
 1514+ $MatroskaBlockLacingType[0x01] = 'Xiph lacing';
 1515+ $MatroskaBlockLacingType[0x02] = 'fixed-size lacing';
 1516+ $MatroskaBlockLacingType[0x03] = 'EBML lacing';
 1517+ }
 1518+ return (isset($MatroskaBlockLacingType[$lacingtype]) ? $MatroskaBlockLacingType[$lacingtype] : $lacingtype);
 1519+ }
 1520+
 1521+ function MatroskaCodecIDtoCommonName($codecid) {
 1522+ // http://www.matroska.org/technical/specs/codecid/index.html
 1523+ static $MatroskaCodecIDlist = array();
 1524+ if (empty($MatroskaCodecIDlist)) {
 1525+ $MatroskaCodecIDlist['A_AAC'] = 'aac';
 1526+ $MatroskaCodecIDlist['A_AAC/MPEG2/LC'] = 'aac';
 1527+ $MatroskaCodecIDlist['A_AC3'] = 'ac3';
 1528+ $MatroskaCodecIDlist['A_DTS'] = 'dts';
 1529+ $MatroskaCodecIDlist['A_FLAC'] = 'flac';
 1530+ $MatroskaCodecIDlist['A_MPEG/L1'] = 'mp1';
 1531+ $MatroskaCodecIDlist['A_MPEG/L2'] = 'mp2';
 1532+ $MatroskaCodecIDlist['A_MPEG/L3'] = 'mp3';
 1533+ $MatroskaCodecIDlist['A_PCM/INT/LIT'] = 'pcm'; // PCM Integer Little Endian
 1534+ $MatroskaCodecIDlist['A_PCM/INT/BIG'] = 'pcm'; // PCM Integer Big Endian
 1535+ $MatroskaCodecIDlist['A_QUICKTIME/QDMC'] = 'quicktime'; // Quicktime: QDesign Music
 1536+ $MatroskaCodecIDlist['A_QUICKTIME/QDM2'] = 'quicktime'; // Quicktime: QDesign Music v2
 1537+ $MatroskaCodecIDlist['A_VORBIS'] = 'vorbis';
 1538+ $MatroskaCodecIDlist['V_MPEG1'] = 'mpeg';
 1539+ $MatroskaCodecIDlist['V_THEORA'] = 'theora';
 1540+ $MatroskaCodecIDlist['V_REAL/RV40'] = 'real';
 1541+ $MatroskaCodecIDlist['V_REAL/RV10'] = 'real';
 1542+ $MatroskaCodecIDlist['V_REAL/RV20'] = 'real';
 1543+ $MatroskaCodecIDlist['V_REAL/RV30'] = 'real';
 1544+ $MatroskaCodecIDlist['V_QUICKTIME'] = 'quicktime'; // Quicktime
 1545+ $MatroskaCodecIDlist['V_MPEG4/ISO/AP'] = 'mpeg4';
 1546+ $MatroskaCodecIDlist['V_MPEG4/ISO/ASP'] = 'mpeg4';
 1547+ $MatroskaCodecIDlist['V_MPEG4/ISO/AVC'] = 'h264';
 1548+ $MatroskaCodecIDlist['V_MPEG4/ISO/SP'] = 'mpeg4';
 1549+ }
 1550+ return (isset($MatroskaCodecIDlist[$codecid]) ? $MatroskaCodecIDlist[$codecid] : $codecid);
 1551+ }
 1552+
 1553+ function EBMLidName($value) {
 1554+ static $EBMLidList = array();
 1555+ if (empty($EBMLidList)) {
 1556+ $EBMLidList[EBML_ID_ASPECTRATIOTYPE] = 'AspectRatioType';
 1557+ $EBMLidList[EBML_ID_ATTACHEDFILE] = 'AttachedFile';
 1558+ $EBMLidList[EBML_ID_ATTACHMENTLINK] = 'AttachmentLink';
 1559+ $EBMLidList[EBML_ID_ATTACHMENTS] = 'Attachments';
 1560+ $EBMLidList[EBML_ID_ATTACHMENTUID] = 'AttachmentUID';
 1561+ $EBMLidList[EBML_ID_AUDIO] = 'Audio';
 1562+ $EBMLidList[EBML_ID_BITDEPTH] = 'BitDepth';
 1563+ $EBMLidList[EBML_ID_CHANNELPOSITIONS] = 'ChannelPositions';
 1564+ $EBMLidList[EBML_ID_CHANNELS] = 'Channels';
 1565+ $EBMLidList[EBML_ID_CHAPCOUNTRY] = 'ChapCountry';
 1566+ $EBMLidList[EBML_ID_CHAPLANGUAGE] = 'ChapLanguage';
 1567+ $EBMLidList[EBML_ID_CHAPPROCESS] = 'ChapProcess';
 1568+ $EBMLidList[EBML_ID_CHAPPROCESSCODECID] = 'ChapProcessCodecID';
 1569+ $EBMLidList[EBML_ID_CHAPPROCESSCOMMAND] = 'ChapProcessCommand';
 1570+ $EBMLidList[EBML_ID_CHAPPROCESSDATA] = 'ChapProcessData';
 1571+ $EBMLidList[EBML_ID_CHAPPROCESSPRIVATE] = 'ChapProcessPrivate';
 1572+ $EBMLidList[EBML_ID_CHAPPROCESSTIME] = 'ChapProcessTime';
 1573+ $EBMLidList[EBML_ID_CHAPSTRING] = 'ChapString';
 1574+ $EBMLidList[EBML_ID_CHAPTERATOM] = 'ChapterAtom';
 1575+ $EBMLidList[EBML_ID_CHAPTERDISPLAY] = 'ChapterDisplay';
 1576+ $EBMLidList[EBML_ID_CHAPTERFLAGENABLED] = 'ChapterFlagEnabled';
 1577+ $EBMLidList[EBML_ID_CHAPTERFLAGHIDDEN] = 'ChapterFlagHidden';
 1578+ $EBMLidList[EBML_ID_CHAPTERPHYSICALEQUIV] = 'ChapterPhysicalEquiv';
 1579+ $EBMLidList[EBML_ID_CHAPTERS] = 'Chapters';
 1580+ $EBMLidList[EBML_ID_CHAPTERSEGMENTEDITIONUID] = 'ChapterSegmentEditionUID';
 1581+ $EBMLidList[EBML_ID_CHAPTERSEGMENTUID] = 'ChapterSegmentUID';
 1582+ $EBMLidList[EBML_ID_CHAPTERTIMEEND] = 'ChapterTimeEnd';
 1583+ $EBMLidList[EBML_ID_CHAPTERTIMESTART] = 'ChapterTimeStart';
 1584+ $EBMLidList[EBML_ID_CHAPTERTRACK] = 'ChapterTrack';
 1585+ $EBMLidList[EBML_ID_CHAPTERTRACKNUMBER] = 'ChapterTrackNumber';
 1586+ $EBMLidList[EBML_ID_CHAPTERTRANSLATE] = 'ChapterTranslate';
 1587+ $EBMLidList[EBML_ID_CHAPTERTRANSLATECODEC] = 'ChapterTranslateCodec';
 1588+ $EBMLidList[EBML_ID_CHAPTERTRANSLATEEDITIONUID] = 'ChapterTranslateEditionUID';
 1589+ $EBMLidList[EBML_ID_CHAPTERTRANSLATEID] = 'ChapterTranslateID';
 1590+ $EBMLidList[EBML_ID_CHAPTERUID] = 'ChapterUID';
 1591+ $EBMLidList[EBML_ID_CLUSTER] = 'Cluster';
 1592+ $EBMLidList[EBML_ID_CLUSTERBLOCK] = 'ClusterBlock';
 1593+ $EBMLidList[EBML_ID_CLUSTERBLOCKADDID] = 'ClusterBlockAddID';
 1594+ $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONAL] = 'ClusterBlockAdditional';
 1595+ $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONID] = 'ClusterBlockAdditionID';
 1596+ $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONS] = 'ClusterBlockAdditions';
 1597+ $EBMLidList[EBML_ID_CLUSTERBLOCKDURATION] = 'ClusterBlockDuration';
 1598+ $EBMLidList[EBML_ID_CLUSTERBLOCKGROUP] = 'ClusterBlockGroup';
 1599+ $EBMLidList[EBML_ID_CLUSTERBLOCKMORE] = 'ClusterBlockMore';
 1600+ $EBMLidList[EBML_ID_CLUSTERBLOCKVIRTUAL] = 'ClusterBlockVirtual';
 1601+ $EBMLidList[EBML_ID_CLUSTERCODECSTATE] = 'ClusterCodecState';
 1602+ $EBMLidList[EBML_ID_CLUSTERDELAY] = 'ClusterDelay';
 1603+ $EBMLidList[EBML_ID_CLUSTERDURATION] = 'ClusterDuration';
 1604+ $EBMLidList[EBML_ID_CLUSTERENCRYPTEDBLOCK] = 'ClusterEncryptedBlock';
 1605+ $EBMLidList[EBML_ID_CLUSTERFRAMENUMBER] = 'ClusterFrameNumber';
 1606+ $EBMLidList[EBML_ID_CLUSTERLACENUMBER] = 'ClusterLaceNumber';
 1607+ $EBMLidList[EBML_ID_CLUSTERPOSITION] = 'ClusterPosition';
 1608+ $EBMLidList[EBML_ID_CLUSTERPREVSIZE] = 'ClusterPrevSize';
 1609+ $EBMLidList[EBML_ID_CLUSTERREFERENCEBLOCK] = 'ClusterReferenceBlock';
 1610+ $EBMLidList[EBML_ID_CLUSTERREFERENCEPRIORITY] = 'ClusterReferencePriority';
 1611+ $EBMLidList[EBML_ID_CLUSTERREFERENCEVIRTUAL] = 'ClusterReferenceVirtual';
 1612+ $EBMLidList[EBML_ID_CLUSTERSILENTTRACKNUMBER] = 'ClusterSilentTrackNumber';
 1613+ $EBMLidList[EBML_ID_CLUSTERSILENTTRACKS] = 'ClusterSilentTracks';
 1614+ $EBMLidList[EBML_ID_CLUSTERSIMPLEBLOCK] = 'ClusterSimpleBlock';
 1615+ $EBMLidList[EBML_ID_CLUSTERTIMECODE] = 'ClusterTimecode';
 1616+ $EBMLidList[EBML_ID_CLUSTERTIMESLICE] = 'ClusterTimeSlice';
 1617+ $EBMLidList[EBML_ID_CODECDECODEALL] = 'CodecDecodeAll';
 1618+ $EBMLidList[EBML_ID_CODECDOWNLOADURL] = 'CodecDownloadURL';
 1619+ $EBMLidList[EBML_ID_CODECID] = 'CodecID';
 1620+ $EBMLidList[EBML_ID_CODECINFOURL] = 'CodecInfoURL';
 1621+ $EBMLidList[EBML_ID_CODECNAME] = 'CodecName';
 1622+ $EBMLidList[EBML_ID_CODECPRIVATE] = 'CodecPrivate';
 1623+ $EBMLidList[EBML_ID_CODECSETTINGS] = 'CodecSettings';
 1624+ $EBMLidList[EBML_ID_COLOURSPACE] = 'ColourSpace';
 1625+ $EBMLidList[EBML_ID_CONTENTCOMPALGO] = 'ContentCompAlgo';
 1626+ $EBMLidList[EBML_ID_CONTENTCOMPRESSION] = 'ContentCompression';
 1627+ $EBMLidList[EBML_ID_CONTENTCOMPSETTINGS] = 'ContentCompSettings';
 1628+ $EBMLidList[EBML_ID_CONTENTENCALGO] = 'ContentEncAlgo';
 1629+ $EBMLidList[EBML_ID_CONTENTENCKEYID] = 'ContentEncKeyID';
 1630+ $EBMLidList[EBML_ID_CONTENTENCODING] = 'ContentEncoding';
 1631+ $EBMLidList[EBML_ID_CONTENTENCODINGORDER] = 'ContentEncodingOrder';
 1632+ $EBMLidList[EBML_ID_CONTENTENCODINGS] = 'ContentEncodings';
 1633+ $EBMLidList[EBML_ID_CONTENTENCODINGSCOPE] = 'ContentEncodingScope';
 1634+ $EBMLidList[EBML_ID_CONTENTENCODINGTYPE] = 'ContentEncodingType';
 1635+ $EBMLidList[EBML_ID_CONTENTENCRYPTION] = 'ContentEncryption';
 1636+ $EBMLidList[EBML_ID_CONTENTSIGALGO] = 'ContentSigAlgo';
 1637+ $EBMLidList[EBML_ID_CONTENTSIGHASHALGO] = 'ContentSigHashAlgo';
 1638+ $EBMLidList[EBML_ID_CONTENTSIGKEYID] = 'ContentSigKeyID';
 1639+ $EBMLidList[EBML_ID_CONTENTSIGNATURE] = 'ContentSignature';
 1640+ $EBMLidList[EBML_ID_CRC32] = 'CRC32';
 1641+ $EBMLidList[EBML_ID_CUEBLOCKNUMBER] = 'CueBlockNumber';
 1642+ $EBMLidList[EBML_ID_CUECLUSTERPOSITION] = 'CueClusterPosition';
 1643+ $EBMLidList[EBML_ID_CUECODECSTATE] = 'CueCodecState';
 1644+ $EBMLidList[EBML_ID_CUEPOINT] = 'CuePoint';
 1645+ $EBMLidList[EBML_ID_CUEREFCLUSTER] = 'CueRefCluster';
 1646+ $EBMLidList[EBML_ID_CUEREFCODECSTATE] = 'CueRefCodecState';
 1647+ $EBMLidList[EBML_ID_CUEREFERENCE] = 'CueReference';
 1648+ $EBMLidList[EBML_ID_CUEREFNUMBER] = 'CueRefNumber';
 1649+ $EBMLidList[EBML_ID_CUEREFTIME] = 'CueRefTime';
 1650+ $EBMLidList[EBML_ID_CUES] = 'Cues';
 1651+ $EBMLidList[EBML_ID_CUETIME] = 'CueTime';
 1652+ $EBMLidList[EBML_ID_CUETRACK] = 'CueTrack';
 1653+ $EBMLidList[EBML_ID_CUETRACKPOSITIONS] = 'CueTrackPositions';
 1654+ $EBMLidList[EBML_ID_DATEUTC] = 'DateUTC';
 1655+ $EBMLidList[EBML_ID_DEFAULTDURATION] = 'DefaultDuration';
 1656+ $EBMLidList[EBML_ID_DISPLAYHEIGHT] = 'DisplayHeight';
 1657+ $EBMLidList[EBML_ID_DISPLAYUNIT] = 'DisplayUnit';
 1658+ $EBMLidList[EBML_ID_DISPLAYWIDTH] = 'DisplayWidth';
 1659+ $EBMLidList[EBML_ID_DOCTYPE] = 'DocType';
 1660+ $EBMLidList[EBML_ID_DOCTYPEREADVERSION] = 'DocTypeReadVersion';
 1661+ $EBMLidList[EBML_ID_DOCTYPEVERSION] = 'DocTypeVersion';
 1662+ $EBMLidList[EBML_ID_DURATION] = 'Duration';
 1663+ $EBMLidList[EBML_ID_EBMLMAXIDLENGTH] = 'EBMLMaxIDLength';
 1664+ $EBMLidList[EBML_ID_EBMLMAXSIZELENGTH] = 'EBMLMaxSizeLength';
 1665+ $EBMLidList[EBML_ID_EBMLREADVERSION] = 'EBMLReadVersion';
 1666+ $EBMLidList[EBML_ID_EBMLVERSION] = 'EBMLVersion';
 1667+ $EBMLidList[EBML_ID_EDITIONENTRY] = 'EditionEntry';
 1668+ $EBMLidList[EBML_ID_EDITIONFLAGDEFAULT] = 'EditionFlagDefault';
 1669+ $EBMLidList[EBML_ID_EDITIONFLAGHIDDEN] = 'EditionFlagHidden';
 1670+ $EBMLidList[EBML_ID_EDITIONFLAGORDERED] = 'EditionFlagOrdered';
 1671+ $EBMLidList[EBML_ID_EDITIONUID] = 'EditionUID';
 1672+ $EBMLidList[EBML_ID_FILEDATA] = 'FileData';
 1673+ $EBMLidList[EBML_ID_FILEDESCRIPTION] = 'FileDescription';
 1674+ $EBMLidList[EBML_ID_FILEMIMETYPE] = 'FileMimeType';
 1675+ $EBMLidList[EBML_ID_FILENAME] = 'FileName';
 1676+ $EBMLidList[EBML_ID_FILEREFERRAL] = 'FileReferral';
 1677+ $EBMLidList[EBML_ID_FILEUID] = 'FileUID';
 1678+ $EBMLidList[EBML_ID_FLAGDEFAULT] = 'FlagDefault';
 1679+ $EBMLidList[EBML_ID_FLAGENABLED] = 'FlagEnabled';
 1680+ $EBMLidList[EBML_ID_FLAGFORCED] = 'FlagForced';
 1681+ $EBMLidList[EBML_ID_FLAGINTERLACED] = 'FlagInterlaced';
 1682+ $EBMLidList[EBML_ID_FLAGLACING] = 'FlagLacing';
 1683+ $EBMLidList[EBML_ID_GAMMAVALUE] = 'GammaValue';
 1684+ $EBMLidList[EBML_ID_INFO] = 'Info';
 1685+ $EBMLidList[EBML_ID_LANGUAGE] = 'Language';
 1686+ $EBMLidList[EBML_ID_MAXBLOCKADDITIONID] = 'MaxBlockAdditionID';
 1687+ $EBMLidList[EBML_ID_MAXCACHE] = 'MaxCache';
 1688+ $EBMLidList[EBML_ID_MINCACHE] = 'MinCache';
 1689+ $EBMLidList[EBML_ID_MUXINGAPP] = 'MuxingApp';
 1690+ $EBMLidList[EBML_ID_NAME] = 'Name';
 1691+ $EBMLidList[EBML_ID_NEXTFILENAME] = 'NextFilename';
 1692+ $EBMLidList[EBML_ID_NEXTUID] = 'NextUID';
 1693+ $EBMLidList[EBML_ID_OUTPUTSAMPLINGFREQUENCY] = 'OutputSamplingFrequency';
 1694+ $EBMLidList[EBML_ID_PIXELCROPBOTTOM] = 'PixelCropBottom';
 1695+ $EBMLidList[EBML_ID_PIXELCROPLEFT] = 'PixelCropLeft';
 1696+ $EBMLidList[EBML_ID_PIXELCROPRIGHT] = 'PixelCropRight';
 1697+ $EBMLidList[EBML_ID_PIXELCROPTOP] = 'PixelCropTop';
 1698+ $EBMLidList[EBML_ID_PIXELHEIGHT] = 'PixelHeight';
 1699+ $EBMLidList[EBML_ID_PIXELWIDTH] = 'PixelWidth';
 1700+ $EBMLidList[EBML_ID_PREVFILENAME] = 'PrevFilename';
 1701+ $EBMLidList[EBML_ID_PREVUID] = 'PrevUID';
 1702+ $EBMLidList[EBML_ID_SAMPLINGFREQUENCY] = 'SamplingFrequency';
 1703+ $EBMLidList[EBML_ID_SEEK] = 'Seek';
 1704+ $EBMLidList[EBML_ID_SEEKHEAD] = 'SeekHead';
 1705+ $EBMLidList[EBML_ID_SEEKID] = 'SeekID';
 1706+ $EBMLidList[EBML_ID_SEEKPOSITION] = 'SeekPosition';
 1707+ $EBMLidList[EBML_ID_SEGMENTFAMILY] = 'SegmentFamily';
 1708+ $EBMLidList[EBML_ID_SEGMENTFILENAME] = 'SegmentFilename';
 1709+ $EBMLidList[EBML_ID_SEGMENTUID] = 'SegmentUID';
 1710+ $EBMLidList[EBML_ID_SIMPLETAG] = 'SimpleTag';
 1711+ $EBMLidList[EBML_ID_CLUSTERSLICES] = 'ClusterSlices';
 1712+ $EBMLidList[EBML_ID_STEREOMODE] = 'StereoMode';
 1713+ $EBMLidList[EBML_ID_TAG] = 'Tag';
 1714+ $EBMLidList[EBML_ID_TAGBINARY] = 'TagBinary';
 1715+ $EBMLidList[EBML_ID_TAGCHAPTERUID] = 'TagChapterUID';
 1716+ $EBMLidList[EBML_ID_TAGDEFAULT] = 'TagDefault';
 1717+ $EBMLidList[EBML_ID_TAGEDITIONUID] = 'TagEditionUID';
 1718+ $EBMLidList[EBML_ID_TAGLANGUAGE] = 'TagLanguage';
 1719+ $EBMLidList[EBML_ID_TAGNAME] = 'TagName';
 1720+ $EBMLidList[EBML_ID_TAGTRACKUID] = 'TagTrackUID';
 1721+ $EBMLidList[EBML_ID_TAGS] = 'Tags';
 1722+ $EBMLidList[EBML_ID_TAGSTRING] = 'TagString';
 1723+ $EBMLidList[EBML_ID_TARGETS] = 'Targets';
 1724+ $EBMLidList[EBML_ID_TARGETTYPE] = 'TargetType';
 1725+ $EBMLidList[EBML_ID_TARGETTYPEVALUE] = 'TargetTypeValue';
 1726+ $EBMLidList[EBML_ID_TIMECODESCALE] = 'TimecodeScale';
 1727+ $EBMLidList[EBML_ID_TITLE] = 'Title';
 1728+ $EBMLidList[EBML_ID_TRACKENTRY] = 'TrackEntry';
 1729+ $EBMLidList[EBML_ID_TRACKNUMBER] = 'TrackNumber';
 1730+ $EBMLidList[EBML_ID_TRACKOFFSET] = 'TrackOffset';
 1731+ $EBMLidList[EBML_ID_TRACKOVERLAY] = 'TrackOverlay';
 1732+ $EBMLidList[EBML_ID_TRACKS] = 'Tracks';
 1733+ $EBMLidList[EBML_ID_TRACKTIMECODESCALE] = 'TrackTimecodeScale';
 1734+ $EBMLidList[EBML_ID_TRACKTRANSLATE] = 'TrackTranslate';
 1735+ $EBMLidList[EBML_ID_TRACKTRANSLATECODEC] = 'TrackTranslateCodec';
 1736+ $EBMLidList[EBML_ID_TRACKTRANSLATEEDITIONUID] = 'TrackTranslateEditionUID';
 1737+ $EBMLidList[EBML_ID_TRACKTRANSLATETRACKID] = 'TrackTranslateTrackID';
 1738+ $EBMLidList[EBML_ID_TRACKTYPE] = 'TrackType';
 1739+ $EBMLidList[EBML_ID_TRACKUID] = 'TrackUID';
 1740+ $EBMLidList[EBML_ID_VIDEO] = 'Video';
 1741+ $EBMLidList[EBML_ID_VOID] = 'Void';
 1742+ $EBMLidList[EBML_ID_WRITINGAPP] = 'WritingApp';
 1743+ }
 1744+ return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
 1745+ }
 1746+
 1747+}
 1748+
 1749+?>
\ No newline at end of file
Index: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/getid3/module.audio.flac.php
@@ -0,0 +1,403 @@
 2+<?php
 3+/////////////////////////////////////////////////////////////////
 4+/// getID3() by James Heinrich <info@getid3.org> //
 5+// available at http://getid3.sourceforge.net //
 6+// or http://www.getid3.org //
 7+/////////////////////////////////////////////////////////////////
 8+// See readme.txt for more details //
 9+/////////////////////////////////////////////////////////////////
 10+// //
 11+// module.audio.flac.php //
 12+// module for analyzing FLAC and OggFLAC audio files //
 13+// dependencies: module.audio.ogg.php //
 14+// ///
 15+/////////////////////////////////////////////////////////////////
 16+
 17+
 18+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
 19+
 20+class getid3_flac
 21+{
 22+
 23+ function getid3_flac(&$fd, &$ThisFileInfo) {
 24+ // http://flac.sourceforge.net/format.html
 25+
 26+ fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
 27+ $StreamMarker = fread($fd, 4);
 28+ if ($StreamMarker != 'fLaC') {
 29+ $ThisFileInfo['error'][] = 'Expecting "fLaC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"';
 30+ return false;
 31+ }
 32+ $ThisFileInfo['fileformat'] = 'flac';
 33+ $ThisFileInfo['audio']['dataformat'] = 'flac';
 34+ $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
 35+ $ThisFileInfo['audio']['lossless'] = true;
 36+
 37+ return getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo);
 38+ }
 39+
 40+
 41+ static function FLACparseMETAdata(&$fd, &$ThisFileInfo) {
 42+
 43+ do {
 44+ $METAdataBlockOffset = ftell($fd);
 45+ $METAdataBlockHeader = fread($fd, 4);
 46+ $METAdataLastBlockFlag = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x80);
 47+ $METAdataBlockType = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x7F;
 48+ $METAdataBlockLength = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 1, 3));
 49+ $METAdataBlockTypeText = getid3_flac::FLACmetaBlockTypeLookup($METAdataBlockType);
 50+
 51+ if ($METAdataBlockLength < 0) {
 52+ $ThisFileInfo['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
 53+ break;
 54+ }
 55+
 56+ $ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'] = array();
 57+ $ThisFileInfo_flac_METAdataBlockTypeText_raw = &$ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'];
 58+
 59+ $ThisFileInfo_flac_METAdataBlockTypeText_raw['offset'] = $METAdataBlockOffset;
 60+ $ThisFileInfo_flac_METAdataBlockTypeText_raw['last_meta_block'] = $METAdataLastBlockFlag;
 61+ $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type'] = $METAdataBlockType;
 62+ $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type_text'] = $METAdataBlockTypeText;
 63+ $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_length'] = $METAdataBlockLength;
 64+ ob_start();
 65+ $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'] = fread($fd, $METAdataBlockLength);
 66+ $errormessage = ob_get_contents();
 67+ ob_end_clean();
 68+ $ThisFileInfo['avdataoffset'] = ftell($fd);
 69+
 70+ switch ($METAdataBlockTypeText) {
 71+ case 'STREAMINFO': // 0x00
 72+ if (!getid3_flac::FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
 73+ return false;
 74+ }
 75+ break;
 76+
 77+ case 'PADDING': // 0x01
 78+ // ignore
 79+ break;
 80+
 81+ case 'APPLICATION': // 0x02
 82+ if (!getid3_flac::FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
 83+ return false;
 84+ }
 85+ break;
 86+
 87+ case 'SEEKTABLE': // 0x03
 88+ if (!getid3_flac::FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
 89+ return false;
 90+ }
 91+ break;
 92+
 93+ case 'VORBIS_COMMENT': // 0x04
 94+ $OldOffset = ftell($fd);
 95+ fseek($fd, 0 - $METAdataBlockLength, SEEK_CUR);
 96+ getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
 97+ fseek($fd, $OldOffset, SEEK_SET);
 98+ break;
 99+
 100+ case 'CUESHEET': // 0x05
 101+ if (!getid3_flac::FLACparseCUESHEET($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
 102+ return false;
 103+ }
 104+ break;
 105+
 106+ case 'PICTURE': // 0x06
 107+ if (!getid3_flac::FLACparsePICTURE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
 108+ return false;
 109+ }
 110+ break;
 111+
 112+ default:
 113+ $ThisFileInfo['warning'][] = 'Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
 114+ break;
 115+ }
 116+
 117+ } while ($METAdataLastBlockFlag === false);
 118+
 119+
 120+ if (isset($ThisFileInfo['flac']['STREAMINFO'])) {
 121+ $ThisFileInfo['flac']['compressed_audio_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
 122+ $ThisFileInfo['flac']['uncompressed_audio_bytes'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] * $ThisFileInfo['flac']['STREAMINFO']['channels'] * ($ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] / 8);
 123+ if ($ThisFileInfo['flac']['uncompressed_audio_bytes'] == 0) {
 124+ $ThisFileInfo['error'][] = 'Corrupt FLAC file: uncompressed_audio_bytes == zero';
 125+ return false;
 126+ }
 127+ $ThisFileInfo['flac']['compression_ratio'] = $ThisFileInfo['flac']['compressed_audio_bytes'] / $ThisFileInfo['flac']['uncompressed_audio_bytes'];
 128+ }
 129+
 130+ // set md5_data_source - built into flac 0.5+
 131+ if (isset($ThisFileInfo['flac']['STREAMINFO']['audio_signature'])) {
 132+
 133+ if ($ThisFileInfo['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
 134+
 135+ $ThisFileInfo['warning'][] = 'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)';
 136+
 137+ } else {
 138+
 139+ $ThisFileInfo['md5_data_source'] = '';
 140+ $md5 = $ThisFileInfo['flac']['STREAMINFO']['audio_signature'];
 141+ for ($i = 0; $i < strlen($md5); $i++) {
 142+ $ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
 143+ }
 144+ if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) {
 145+ unset($ThisFileInfo['md5_data_source']);
 146+ }
 147+
 148+ }
 149+
 150+ }
 151+
 152+ $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
 153+ if ($ThisFileInfo['audio']['bits_per_sample'] == 8) {
 154+ // special case
 155+ // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
 156+ // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
 157+ $ThisFileInfo['warning'][] = 'FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file';
 158+ }
 159+ if (!empty($ThisFileInfo['ogg']['vendor'])) {
 160+ $ThisFileInfo['audio']['encoder'] = $ThisFileInfo['ogg']['vendor'];
 161+ }
 162+
 163+ return true;
 164+ }
 165+
 166+ static function FLACmetaBlockTypeLookup($blocktype) {
 167+ static $FLACmetaBlockTypeLookup = array();
 168+ if (empty($FLACmetaBlockTypeLookup)) {
 169+ $FLACmetaBlockTypeLookup[0] = 'STREAMINFO';
 170+ $FLACmetaBlockTypeLookup[1] = 'PADDING';
 171+ $FLACmetaBlockTypeLookup[2] = 'APPLICATION';
 172+ $FLACmetaBlockTypeLookup[3] = 'SEEKTABLE';
 173+ $FLACmetaBlockTypeLookup[4] = 'VORBIS_COMMENT';
 174+ $FLACmetaBlockTypeLookup[5] = 'CUESHEET';
 175+ $FLACmetaBlockTypeLookup[6] = 'PICTURE';
 176+ }
 177+ return (isset($FLACmetaBlockTypeLookup[$blocktype]) ? $FLACmetaBlockTypeLookup[$blocktype] : 'reserved');
 178+ }
 179+
 180+ static function FLACapplicationIDLookup($applicationid) {
 181+ static $FLACapplicationIDLookup = array();
 182+ if (empty($FLACapplicationIDLookup)) {
 183+ // http://flac.sourceforge.net/id.html
 184+ $FLACapplicationIDLookup[0x46746F6C] = 'flac-tools'; // 'Ftol'
 185+ $FLACapplicationIDLookup[0x46746F6C] = 'Sound Font FLAC'; // 'SFFL'
 186+ }
 187+ return (isset($FLACapplicationIDLookup[$applicationid]) ? $FLACapplicationIDLookup[$applicationid] : 'reserved');
 188+ }
 189+
 190+ static function FLACpictureTypeLookup($type_id) {
 191+ static $lookup = array (
 192+ 0 => 'Other',
 193+ 1 => '32x32 pixels \'file icon\' (PNG only)',
 194+ 2 => 'Other file icon',
 195+ 3 => 'Cover (front)',
 196+ 4 => 'Cover (back)',
 197+ 5 => 'Leaflet page',
 198+ 6 => 'Media (e.g. label side of CD)',
 199+ 7 => 'Lead artist/lead performer/soloist',
 200+ 8 => 'Artist/performer',
 201+ 9 => 'Conductor',
 202+ 10 => 'Band/Orchestra',
 203+ 11 => 'Composer',
 204+ 12 => 'Lyricist/text writer',
 205+ 13 => 'Recording Location',
 206+ 14 => 'During recording',
 207+ 15 => 'During performance',
 208+ 16 => 'Movie/video screen capture',
 209+ 17 => 'A bright coloured fish',
 210+ 18 => 'Illustration',
 211+ 19 => 'Band/artist logotype',
 212+ 20 => 'Publisher/Studio logotype',
 213+ );
 214+ return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
 215+ }
 216+
 217+ static function FLACparseSTREAMINFO($METAdataBlockData, &$ThisFileInfo) {
 218+ $offset = 0;
 219+ $ThisFileInfo['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
 220+ $offset += 2;
 221+ $ThisFileInfo['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
 222+ $offset += 2;
 223+ $ThisFileInfo['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
 224+ $offset += 3;
 225+ $ThisFileInfo['flac']['STREAMINFO']['max_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
 226+ $offset += 3;
 227+
 228+ $SampleRateChannelsSampleBitsStreamSamples = getid3_lib::BigEndian2Bin(substr($METAdataBlockData, $offset, 8));
 229+ $ThisFileInfo['flac']['STREAMINFO']['sample_rate'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20));
 230+ $ThisFileInfo['flac']['STREAMINFO']['channels'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1;
 231+ $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1;
 232+ $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36));
 233+ $offset += 8;
 234+
 235+ $ThisFileInfo['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16);
 236+ $offset += 16;
 237+
 238+ if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) {
 239+
 240+ $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
 241+ $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
 242+ $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flac']['STREAMINFO']['channels'];
 243+ $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
 244+ $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
 245+ if ($ThisFileInfo['playtime_seconds'] > 0) {
 246+ $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
 247+ }
 248+
 249+ } else {
 250+
 251+ $ThisFileInfo['error'][] = 'Corrupt METAdata block: STREAMINFO';
 252+ return false;
 253+
 254+ }
 255+
 256+ unset($ThisFileInfo['flac']['STREAMINFO']['raw']);
 257+
 258+ return true;
 259+ }
 260+
 261+
 262+ static function FLACparseAPPLICATION($METAdataBlockData, &$ThisFileInfo) {
 263+ $offset = 0;
 264+ $ApplicationID = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 4));
 265+ $offset += 4;
 266+ $ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['name'] = getid3_flac::FLACapplicationIDLookup($ApplicationID);
 267+ $ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['data'] = substr($METAdataBlockData, $offset);
 268+ $offset = $METAdataBlockLength;
 269+
 270+ unset($ThisFileInfo['flac']['APPLICATION']['raw']);
 271+
 272+ return true;
 273+ }
 274+
 275+
 276+ static function FLACparseSEEKTABLE($METAdataBlockData, &$ThisFileInfo) {
 277+ $offset = 0;
 278+ $METAdataBlockLength = strlen($METAdataBlockData);
 279+ $placeholderpattern = str_repeat("\xFF", 8);
 280+ while ($offset < $METAdataBlockLength) {
 281+ $SampleNumberString = substr($METAdataBlockData, $offset, 8);
 282+ $offset += 8;
 283+ if ($SampleNumberString == $placeholderpattern) {
 284+
 285+ // placeholder point
 286+ getid3_lib::safe_inc($ThisFileInfo['flac']['SEEKTABLE']['placeholders'], 1);
 287+ $offset += 10;
 288+
 289+ } else {
 290+
 291+ $SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
 292+ $ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
 293+ $offset += 8;
 294+ $ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
 295+ $offset += 2;
 296+
 297+ }
 298+ }
 299+
 300+ unset($ThisFileInfo['flac']['SEEKTABLE']['raw']);
 301+
 302+ return true;
 303+ }
 304+
 305+ static function FLACparseCUESHEET($METAdataBlockData, &$ThisFileInfo) {
 306+ $offset = 0;
 307+ $ThisFileInfo['flac']['CUESHEET']['media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128), "\0");
 308+ $offset += 128;
 309+ $ThisFileInfo['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
 310+ $offset += 8;
 311+ $ThisFileInfo['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80);
 312+ $offset += 1;
 313+
 314+ $offset += 258; // reserved
 315+
 316+ $ThisFileInfo['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
 317+ $offset += 1;
 318+
 319+ for ($track = 0; $track < $ThisFileInfo['flac']['CUESHEET']['number_tracks']; $track++) {
 320+ $TrackSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
 321+ $offset += 8;
 322+ $TrackNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
 323+ $offset += 1;
 324+
 325+ $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
 326+
 327+ $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($METAdataBlockData, $offset, 12);
 328+ $offset += 12;
 329+
 330+ $TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
 331+ $offset += 1;
 332+ $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
 333+ $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
 334+
 335+ $offset += 13; // reserved
 336+
 337+ $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
 338+ $offset += 1;
 339+
 340+ for ($index = 0; $index < $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
 341+ $IndexSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
 342+ $offset += 8;
 343+ $IndexNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
 344+ $offset += 1;
 345+
 346+ $offset += 3; // reserved
 347+
 348+ $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
 349+ }
 350+ }
 351+
 352+ unset($ThisFileInfo['flac']['CUESHEET']['raw']);
 353+
 354+ return true;
 355+ }
 356+
 357+
 358+ static function FLACparsePICTURE($meta_data_block_data, &$ThisFileInfo) {
 359+ $picture = &$ThisFileInfo['flac']['PICTURE'][sizeof($ThisFileInfo['flac']['PICTURE']) - 1];
 360+
 361+ $offset = 0;
 362+
 363+ $picture['typeid'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
 364+ $picture['type'] = getid3_flac::FLACpictureTypeLookup($picture['typeid']);
 365+ $offset += 4;
 366+
 367+ $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
 368+ $offset += 4;
 369+
 370+ $picture['mime_type'] = substr($meta_data_block_data, $offset, $length);
 371+ $offset += $length;
 372+
 373+ $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
 374+ $offset += 4;
 375+
 376+ $picture['description'] = substr($meta_data_block_data, $offset, $length);
 377+ $offset += $length;
 378+
 379+ $picture['width'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
 380+ $offset += 4;
 381+
 382+ $picture['height'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
 383+ $offset += 4;
 384+
 385+ $picture['color_depth'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
 386+ $offset += 4;
 387+
 388+ $picture['colors_indexed'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
 389+ $offset += 4;
 390+
 391+ $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
 392+ $offset += 4;
 393+
 394+ $picture['image_data'] = substr($meta_data_block_data, $offset, $length);
 395+ $offset += $length;
 396+ $picture['data_length'] = strlen($picture['image_data']);
 397+
 398+ unset($ThisFileInfo['flac']['PICTURE']['raw']);
 399+
 400+ return true;
 401+ }
 402+}
 403+
 404+?>
\ No newline at end of file
Index: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/getid3/license.txt
@@ -0,0 +1,340 @@
 2+ GNU GENERAL PUBLIC LICENSE
 3+ Version 2, June 1991
 4+
 5+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
 6+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 7+ Everyone is permitted to copy and distribute verbatim copies
 8+ of this license document, but changing it is not allowed.
 9+
 10+ Preamble
 11+
 12+ The licenses for most software are designed to take away your
 13+freedom to share and change it. By contrast, the GNU General Public
 14+License is intended to guarantee your freedom to share and change free
 15+software--to make sure the software is free for all its users. This
 16+General Public License applies to most of the Free Software
 17+Foundation's software and to any other program whose authors commit to
 18+using it. (Some other Free Software Foundation software is covered by
 19+the GNU Library General Public License instead.) You can apply it to
 20+your programs, too.
 21+
 22+ When we speak of free software, we are referring to freedom, not
 23+price. Our General Public Licenses are designed to make sure that you
 24+have the freedom to distribute copies of free software (and charge for
 25+this service if you wish), that you receive source code or can get it
 26+if you want it, that you can change the software or use pieces of it
 27+in new free programs; and that you know you can do these things.
 28+
 29+ To protect your rights, we need to make restrictions that forbid
 30+anyone to deny you these rights or to ask you to surrender the rights.
 31+These restrictions translate to certain responsibilities for you if you
 32+distribute copies of the software, or if you modify it.
 33+
 34+ For example, if you distribute copies of such a program, whether
 35+gratis or for a fee, you must give the recipients all the rights that
 36+you have. You must make sure that they, too, receive or can get the
 37+source code. And you must show them these terms so they know their
 38+rights.
 39+
 40+ We protect your rights with two steps: (1) copyright the software, and
 41+(2) offer you this license which gives you legal permission to copy,
 42+distribute and/or modify the software.
 43+
 44+ Also, for each author's protection and ours, we want to make certain
 45+that everyone understands that there is no warranty for this free
 46+software. If the software is modified by someone else and passed on, we
 47+want its recipients to know that what they have is not the original, so
 48+that any problems introduced by others will not reflect on the original
 49+authors' reputations.
 50+
 51+ Finally, any free program is threatened constantly by software
 52+patents. We wish to avoid the danger that redistributors of a free
 53+program will individually obtain patent licenses, in effect making the
 54+program proprietary. To prevent this, we have made it clear that any
 55+patent must be licensed for everyone's free use or not licensed at all.
 56+
 57+ The precise terms and conditions for copying, distribution and
 58+modification follow.
 59+
 60+ GNU GENERAL PUBLIC LICENSE
 61+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 62+
 63+ 0. This License applies to any program or other work which contains
 64+a notice placed by the copyright holder saying it may be distributed
 65+under the terms of this General Public License. The "Program", below,
 66+refers to any such program or work, and a "work based on the Program"
 67+means either the Program or any derivative work under copyright law:
 68+that is to say, a work containing the Program or a portion of it,
 69+either verbatim or with modifications and/or translated into another
 70+language. (Hereinafter, translation is included without limitation in
 71+the term "modification".) Each licensee is addressed as "you".
 72+
 73+Activities other than copying, distribution and modification are not
 74+covered by this License; they are outside its scope. The act of
 75+running the Program is not restricted, and the output from the Program
 76+is covered only if its contents constitute a work based on the
 77+Program (independent of having been made by running the Program).
 78+Whether that is true depends on what the Program does.
 79+
 80+ 1. You may copy and distribute verbatim copies of the Program's
 81+source code as you receive it, in any medium, provided that you
 82+conspicuously and appropriately publish on each copy an appropriate
 83+copyright notice and disclaimer of warranty; keep intact all the
 84+notices that refer to this License and to the absence of any warranty;
 85+and give any other recipients of the Program a copy of this License
 86+along with the Program.
 87+
 88+You may charge a fee for the physical act of transferring a copy, and
 89+you may at your option offer warranty protection in exchange for a fee.
 90+
 91+ 2. You may modify your copy or copies of the Program or any portion
 92+of it, thus forming a work based on the Program, and copy and
 93+distribute such modifications or work under the terms of Section 1
 94+above, provided that you also meet all of these conditions:
 95+
 96+ a) You must cause the modified files to carry prominent notices
 97+ stating that you changed the files and the date of any change.
 98+
 99+ b) You must cause any work that you distribute or publish, that in
 100+ whole or in part contains or is derived from the Program or any
 101+ part thereof, to be licensed as a whole at no charge to all third
 102+ parties under the terms of this License.
 103+
 104+ c) If the modified program normally reads commands interactively
 105+ when run, you must cause it, when started running for such
 106+ interactive use in the most ordinary way, to print or display an
 107+ announcement including an appropriate copyright notice and a
 108+ notice that there is no warranty (or else, saying that you provide
 109+ a warranty) and that users may redistribute the program under
 110+ these conditions, and telling the user how to view a copy of this
 111+ License. (Exception: if the Program itself is interactive but
 112+ does not normally print such an announcement, your work based on
 113+ the Program is not required to print an announcement.)
 114+
 115+These requirements apply to the modified work as a whole. If
 116+identifiable sections of that work are not derived from the Program,
 117+and can be reasonably considered independent and separate works in
 118+themselves, then this License, and its terms, do not apply to those
 119+sections when you distribute them as separate works. But when you
 120+distribute the same sections as part of a whole which is a work based
 121+on the Program, the distribution of the whole must be on the terms of
 122+this License, whose permissions for other licensees extend to the
 123+entire whole, and thus to each and every part regardless of who wrote it.
 124+
 125+Thus, it is not the intent of this section to claim rights or contest
 126+your rights to work written entirely by you; rather, the intent is to
 127+exercise the right to control the distribution of derivative or
 128+collective works based on the Program.
 129+
 130+In addition, mere aggregation of another work not based on the Program
 131+with the Program (or with a work based on the Program) on a volume of
 132+a storage or distribution medium does not bring the other work under
 133+the scope of this License.
 134+
 135+ 3. You may copy and distribute the Program (or a work based on it,
 136+under Section 2) in object code or executable form under the terms of
 137+Sections 1 and 2 above provided that you also do one of the following:
 138+
 139+ a) Accompany it with the complete corresponding machine-readable
 140+ source code, which must be distributed under the terms of Sections
 141+ 1 and 2 above on a medium customarily used for software interchange; or,
 142+
 143+ b) Accompany it with a written offer, valid for at least three
 144+ years, to give any third party, for a charge no more than your
 145+ cost of physically performing source distribution, a complete
 146+ machine-readable copy of the corresponding source code, to be
 147+ distributed under the terms of Sections 1 and 2 above on a medium
 148+ customarily used for software interchange; or,
 149+
 150+ c) Accompany it with the information you received as to the offer
 151+ to distribute corresponding source code. (This alternative is
 152+ allowed only for noncommercial distribution and only if you
 153+ received the program in object code or executable form with such
 154+ an offer, in accord with Subsection b above.)
 155+
 156+The source code for a work means the preferred form of the work for
 157+making modifications to it. For an executable work, complete source
 158+code means all the source code for all modules it contains, plus any
 159+associated interface definition files, plus the scripts used to
 160+control compilation and installation of the executable. However, as a
 161+special exception, the source code distributed need not include
 162+anything that is normally distributed (in either source or binary
 163+form) with the major components (compiler, kernel, and so on) of the
 164+operating system on which the executable runs, unless that component
 165+itself accompanies the executable.
 166+
 167+If distribution of executable or object code is made by offering
 168+access to copy from a designated place, then offering equivalent
 169+access to copy the source code from the same place counts as
 170+distribution of the source code, even though third parties are not
 171+compelled to copy the source along with the object code.
 172+
 173+ 4. You may not copy, modify, sublicense, or distribute the Program
 174+except as expressly provided under this License. Any attempt
 175+otherwise to copy, modify, sublicense or distribute the Program is
 176+void, and will automatically terminate your rights under this License.
 177+However, parties who have received copies, or rights, from you under
 178+this License will not have their licenses terminated so long as such
 179+parties remain in full compliance.
 180+
 181+ 5. You are not required to accept this License, since you have not
 182+signed it. However, nothing else grants you permission to modify or
 183+distribute the Program or its derivative works. These actions are
 184+prohibited by law if you do not accept this License. Therefore, by
 185+modifying or distributing the Program (or any work based on the
 186+Program), you indicate your acceptance of this License to do so, and
 187+all its terms and conditions for copying, distributing or modifying
 188+the Program or works based on it.
 189+
 190+ 6. Each time you redistribute the Program (or any work based on the
 191+Program), the recipient automatically receives a license from the
 192+original licensor to copy, distribute or modify the Program subject to
 193+these terms and conditions. You may not impose any further
 194+restrictions on the recipients' exercise of the rights granted herein.
 195+You are not responsible for enforcing compliance by third parties to
 196+this License.
 197+
 198+ 7. If, as a consequence of a court judgment or allegation of patent
 199+infringement or for any other reason (not limited to patent issues),
 200+conditions are imposed on you (whether by court order, agreement or
 201+otherwise) that contradict the conditions of this License, they do not
 202+excuse you from the conditions of this License. If you cannot
 203+distribute so as to satisfy simultaneously your obligations under this
 204+License and any other pertinent obligations, then as a consequence you
 205+may not distribute the Program at all. For example, if a patent
 206+license would not permit royalty-free redistribution of the Program by
 207+all those who receive copies directly or indirectly through you, then
 208+the only way you could satisfy both it and this License would be to
 209+refrain entirely from distribution of the Program.
 210+
 211+If any portion of this section is held invalid or unenforceable under
 212+any particular circumstance, the balance of the section is intended to
 213+apply and the section as a whole is intended to apply in other
 214+circumstances.
 215+
 216+It is not the purpose of this section to induce you to infringe any
 217+patents or other property right claims or to contest validity of any
 218+such claims; this section has the sole purpose of protecting the
 219+integrity of the free software distribution system, which is
 220+implemented by public license practices. Many people have made
 221+generous contributions to the wide range of software distributed
 222+through that system in reliance on consistent application of that
 223+system; it is up to the author/donor to decide if he or she is willing
 224+to distribute software through any other system and a licensee cannot
 225+impose that choice.
 226+
 227+This section is intended to make thoroughly clear what is believed to
 228+be a consequence of the rest of this License.
 229+
 230+ 8. If the distribution and/or use of the Program is restricted in
 231+certain countries either by patents or by copyrighted interfaces, the
 232+original copyright holder who places the Program under this License
 233+may add an explicit geographical distribution limitation excluding
 234+those countries, so that distribution is permitted only in or among
 235+countries not thus excluded. In such case, this License incorporates
 236+the limitation as if written in the body of this License.
 237+
 238+ 9. The Free Software Foundation may publish revised and/or new versions
 239+of the General Public License from time to time. Such new versions will
 240+be similar in spirit to the present version, but may differ in detail to
 241+address new problems or concerns.
 242+
 243+Each version is given a distinguishing version number. If the Program
 244+specifies a version number of this License which applies to it and "any
 245+later version", you have the option of following the terms and conditions
 246+either of that version or of any later version published by the Free
 247+Software Foundation. If the Program does not specify a version number of
 248+this License, you may choose any version ever published by the Free Software
 249+Foundation.
 250+
 251+ 10. If you wish to incorporate parts of the Program into other free
 252+programs whose distribution conditions are different, write to the author
 253+to ask for permission. For software which is copyrighted by the Free
 254+Software Foundation, write to the Free Software Foundation; we sometimes
 255+make exceptions for this. Our decision will be guided by the two goals
 256+of preserving the free status of all derivatives of our free software and
 257+of promoting the sharing and reuse of software generally.
 258+
 259+ NO WARRANTY
 260+
 261+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 262+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
 263+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
 264+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
 265+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 266+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
 267+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
 268+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 269+REPAIR OR CORRECTION.
 270+
 271+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 272+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
 273+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
 274+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
 275+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
 276+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
 277+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
 278+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 279+POSSIBILITY OF SUCH DAMAGES.
 280+
 281+ END OF TERMS AND CONDITIONS
 282+
 283+ How to Apply These Terms to Your New Programs
 284+
 285+ If you develop a new program, and you want it to be of the greatest
 286+possible use to the public, the best way to achieve this is to make it
 287+free software which everyone can redistribute and change under these terms.
 288+
 289+ To do so, attach the following notices to the program. It is safest
 290+to attach them to the start of each source file to most effectively
 291+convey the exclusion of warranty; and each file should have at least
 292+the "copyright" line and a pointer to where the full notice is found.
 293+
 294+ <one line to give the program's name and a brief idea of what it does.>
 295+ Copyright (C) <year> <name of author>
 296+
 297+ This program is free software; you can redistribute it and/or modify
 298+ it under the terms of the GNU General Public License as published by
 299+ the Free Software Foundation; either version 2 of the License, or
 300+ (at your option) any later version.
 301+
 302+ This program is distributed in the hope that it will be useful,
 303+ but WITHOUT ANY WARRANTY; without even the implied warranty of
 304+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 305+ GNU General Public License for more details.
 306+
 307+ You should have received a copy of the GNU General Public License
 308+ along with this program; if not, write to the Free Software
 309+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 310+
 311+
 312+Also add information on how to contact you by electronic and paper mail.
 313+
 314+If the program is interactive, make it output a short notice like this
 315+when it starts in an interactive mode:
 316+
 317+ Gnomovision version 69, Copyright (C) year name of author
 318+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 319+ This is free software, and you are welcome to redistribute it
 320+ under certain conditions; type `show c' for details.
 321+
 322+The hypothetical commands `show w' and `show c' should show the appropriate
 323+parts of the General Public License. Of course, the commands you use may
 324+be called something other than `show w' and `show c'; they could even be
 325+mouse-clicks or menu items--whatever suits your program.
 326+
 327+You should also get your employer (if you work as a programmer) or your
 328+school, if any, to sign a "copyright disclaimer" for the program, if
 329+necessary. Here is a sample; alter the names:
 330+
 331+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
 332+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
 333+
 334+ <signature of Ty Coon>, 1 April 1989
 335+ Ty Coon, President of Vice
 336+
 337+This General Public License does not permit incorporating your program into
 338+proprietary programs. If your program is a subroutine library, you may
 339+consider it more useful to permit linking proprietary applications with the
 340+library. If this is what you want to do, use the GNU Library General
 341+Public License instead of this License.
Property changes on: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/getid3/license.txt
___________________________________________________________________
Added: svn:mime-type
1342 + text/plain
Index: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/getid3/getid3.php
@@ -0,0 +1,1501 @@
 2+<?php
 3+/////////////////////////////////////////////////////////////////
 4+/// getID3() by James Heinrich <info@getid3.org> //
 5+// available at http://getid3.sourceforge.net //
 6+// or http://www.getid3.org //
 7+/////////////////////////////////////////////////////////////////
 8+// //
 9+// Please see readme.txt for more information //
 10+// ///
 11+/////////////////////////////////////////////////////////////////
 12+
 13+// Defines
 14+define('GETID3_VERSION', '1.8.4-20110203');
 15+define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes
 16+
 17+
 18+// attempt to define temp dir as something flexible but reliable
 19+$temp_dir = ini_get('upload_tmp_dir');
 20+if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
 21+ $temp_dir = '';
 22+}
 23+if (!$temp_dir && function_exists('sys_get_temp_dir')) {
 24+ // PHP v5.2.1+
 25+ // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
 26+ $temp_dir = sys_get_temp_dir();
 27+}
 28+$temp_dir = realpath($temp_dir);
 29+$open_basedir = ini_get('open_basedir');
 30+if ($open_basedir) {
 31+ // e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/"
 32+ $temp_dir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $temp_dir);
 33+ $open_basedir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $open_basedir);
 34+ if (substr($temp_dir, -1, 1) != DIRECTORY_SEPARATOR) {
 35+ $temp_dir .= DIRECTORY_SEPARATOR;
 36+ }
 37+ $found_valid_tempdir = false;
 38+ $open_basedirs = explode(':', $open_basedir);
 39+ foreach ($open_basedirs as $basedir) {
 40+ if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
 41+ $basedir .= DIRECTORY_SEPARATOR;
 42+ }
 43+ if (preg_match('#^'.preg_quote($basedir).'#', $temp_dir)) {
 44+ $found_valid_tempdir = true;
 45+ break;
 46+ }
 47+ }
 48+ if (!$found_valid_tempdir) {
 49+ $temp_dir = '';
 50+ }
 51+ unset($open_basedirs, $found_valid_tempdir, $basedir);
 52+}
 53+if (!$temp_dir) {
 54+ $temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir
 55+}
 56+// $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system
 57+define('GETID3_TEMP_DIR', $temp_dir);
 58+unset($open_basedir, $temp_dir);
 59+
 60+
 61+
 62+class getID3
 63+{
 64+ // public: Settings
 65+ var $encoding = 'ISO-8859-1'; // CASE SENSITIVE! - i.e. (must be supported by iconv())
 66+ // Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE
 67+
 68+ var $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN'
 69+
 70+ var $tempdir = GETID3_TEMP_DIR;
 71+
 72+ // public: Optional tag checks - disable for speed.
 73+ var $option_tag_id3v1 = true; // Read and process ID3v1 tags
 74+ var $option_tag_id3v2 = true; // Read and process ID3v2 tags
 75+ var $option_tag_lyrics3 = true; // Read and process Lyrics3 tags
 76+ var $option_tag_apetag = true; // Read and process APE tags
 77+ var $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding
 78+ var $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
 79+
 80+ // public: Optional tag/comment calucations
 81+ var $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc
 82+
 83+ // public: Optional calculations
 84+ var $option_md5_data = false; // Get MD5 sum of data part - slow
 85+ var $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
 86+ var $option_sha1_data = false; // Get SHA1 sum of data part - slow
 87+ var $option_max_2gb_check = true; // Check whether file is larger than 2 Gb and thus not supported by PHP
 88+
 89+ // private
 90+ var $filename;
 91+
 92+
 93+ // public: constructor
 94+ function getID3()
 95+ {
 96+
 97+ $this->startup_error = '';
 98+ $this->startup_warning = '';
 99+
 100+ // Check for PHP version >= 4.2.0
 101+ if (PHP_VERSION < '4.2.0') { // version_compare not available before PHP v4.1.0, do not use for initial version check
 102+ $this->startup_error .= 'getID3() requires PHP v4.2.0 or higher - you are running v'.PHP_VERSION;
 103+ }
 104+ if (version_compare(PHP_VERSION, '5.0.0', '<')) {
 105+ $this->startup_warning .= 'getID3() v1.8+ recommends PHP v5.0.0 or higher - you are running v'.PHP_VERSION;
 106+ }
 107+
 108+ // Check memory
 109+ $memory_limit = ini_get('memory_limit');
 110+ if (preg_match('#([0-9]+)M#i', $memory_limit, $matches)) {
 111+ // could be stored as "16M" rather than 16777216 for example
 112+ $memory_limit = $matches[1] * 1048576;
 113+ } elseif (preg_match('#([0-9]+)G#i', $memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
 114+ // could be stored as "2G" rather than 2147483648 for example
 115+ $memory_limit = $matches[1] * 1073741824;
 116+ }
 117+ if ($memory_limit <= 0) {
 118+ // memory limits probably disabled
 119+ } elseif ($memory_limit <= 3145728) {
 120+ $this->startup_error .= 'PHP has less than 3MB available memory and will very likely run out. Increase memory_limit in php.ini';
 121+ } elseif ($memory_limit <= 12582912) {
 122+ $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini';
 123+ }
 124+
 125+ // Check safe_mode off
 126+ if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
 127+ $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
 128+ }
 129+
 130+ if (intval(ini_get('mbstring.func_overload')) > 0) {
 131+ $this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.');
 132+ }
 133+
 134+ // Check timezone config setting
 135+ if (!ini_get('date.timezone')) {
 136+ if (function_exists('date_default_timezone_set')) {
 137+ $this->warning('php.ini should have "date.timezone" set, but it does not. Setting timezone to "America/New_York"');
 138+ date_default_timezone_set('America/New_York');
 139+ } else {
 140+ $this->warning('php.ini should have "date.timezone" set, but it does not.');
 141+ }
 142+ }
 143+
 144+ // Check for magic_quotes_runtime
 145+ if (function_exists('get_magic_quotes_runtime')) {
 146+ if (get_magic_quotes_runtime()) {
 147+ return $this->startup_error('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).');
 148+ }
 149+ }
 150+
 151+ // Check for magic_quotes_gpc
 152+ if (function_exists('magic_quotes_gpc')) {
 153+ if (get_magic_quotes_gpc()) {
 154+ return $this->startup_error('magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).');
 155+ }
 156+ }
 157+
 158+ // define a constant rather than looking up every time it is needed
 159+ if (!defined('GETID3_OS_ISWINDOWS')) {
 160+ if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
 161+ define('GETID3_OS_ISWINDOWS', true);
 162+ } else {
 163+ define('GETID3_OS_ISWINDOWS', false);
 164+ }
 165+ }
 166+
 167+ // Get base path of getID3() - ONCE
 168+ if (!defined('GETID3_INCLUDEPATH')) {
 169+ foreach (get_included_files() as $key => $val) {
 170+ if (basename($val) == 'getid3.php') {
 171+ define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR);
 172+ break;
 173+ }
 174+ }
 175+ }
 176+
 177+ // Load support library
 178+ if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
 179+ $this->startup_error .= 'getid3.lib.php is missing or corrupt';
 180+ }
 181+
 182+
 183+ // Needed for Windows only:
 184+ // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
 185+ // as well as other helper functions such as head, tail, md5sum, etc
 186+ // This path cannot contain spaces, but the below code will attempt to get the
 187+ // 8.3-equivalent path automatically
 188+ // IMPORTANT: This path must include the trailing slash
 189+ if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
 190+
 191+ $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
 192+
 193+ if (!is_dir($helperappsdir)) {
 194+ $this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
 195+ } elseif (strpos(realpath($helperappsdir), ' ') !== false) {
 196+ $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
 197+ $path_so_far = array();
 198+ foreach ($DirPieces as $key => $value) {
 199+ if (strpos($value, ' ') !== false) {
 200+ if (!empty($path_so_far)) {
 201+ $commandline = 'dir /x '.escapeshellarg(implode(DIRECTORY_SEPARATOR, $path_so_far));
 202+ $dir_listing = `$commandline`;
 203+ $lines = explode("\n", $dir_listing);
 204+ foreach ($lines as $line) {
 205+ $line = trim($line);
 206+ if (preg_match('#^([0-9/]{10}) +([0-9:]{4,5}( [AP]M)?) +(<DIR>|[0-9,]+) +([^ ]{0,11}) +(.+)$#', $line, $matches)) {
 207+ list($dummy, $date, $time, $ampm, $filesize, $shortname, $filename) = $matches;
 208+ if ((strtoupper($filesize) == '<DIR>') && (strtolower($filename) == strtolower($value))) {
 209+ $value = $shortname;
 210+ }
 211+ }
 212+ }
 213+ } else {
 214+ $this->startup_error .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.';
 215+ }
 216+ }
 217+ $path_so_far[] = $value;
 218+ }
 219+ $helperappsdir = implode(DIRECTORY_SEPARATOR, $path_so_far);
 220+ }
 221+ define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
 222+ }
 223+
 224+ }
 225+
 226+
 227+ // public: setOption
 228+ function setOption($optArray) {
 229+ if (!is_array($optArray) || empty($optArray)) {
 230+ return false;
 231+ }
 232+ foreach ($optArray as $opt => $val) {
 233+ //if (isset($this, $opt) === false) {
 234+ if (isset($this->$opt) === false) {
 235+ continue;
 236+ }
 237+ $this->$opt = $val;
 238+ }
 239+ return true;
 240+ }
 241+
 242+
 243+ // public: analyze file - replaces GetAllFileInfo() and GetTagOnly()
 244+ function analyze($filename) {
 245+ if (!empty($this->startup_error)) {
 246+ return $this->error($this->startup_error);
 247+ }
 248+ if (!empty($this->startup_warning)) {
 249+ $this->warning($this->startup_warning);
 250+ }
 251+
 252+ // init result array and set parameters
 253+ $this->info = array();
 254+ $this->info['GETID3_VERSION'] = GETID3_VERSION;
 255+
 256+ // Check encoding/iconv support
 257+ if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
 258+ $errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
 259+ if (GETID3_OS_ISWINDOWS) {
 260+ $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
 261+ } else {
 262+ $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
 263+ }
 264+ return $this->error($errormessage);
 265+ }
 266+
 267+ // remote files not supported
 268+ if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
 269+ return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first');
 270+ }
 271+
 272+ $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
 273+ $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
 274+
 275+ // open local file
 276+ if (file_exists($filename)) {
 277+ ob_start();
 278+ if ($fp = fopen($filename, 'rb')) {
 279+ // great
 280+ ob_end_clean();
 281+ } else {
 282+ $fopen_error = ob_get_contents();
 283+ ob_end_clean();
 284+ return $this->error('Could not open file "'.$filename.'" (fopen says: '.$fopen_error.')');
 285+ }
 286+ } else {
 287+ return $this->error('Could not open file "'.$filename.'" (does not exist)');
 288+ }
 289+
 290+ // set parameters
 291+ $this->info['filesize'] = filesize($filename);
 292+
 293+ // option_max_2gb_check
 294+ if ($this->option_max_2gb_check) {
 295+ // PHP doesn't support integers larger than 31-bit (~2GB)
 296+ // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
 297+ // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
 298+ fseek($fp, 0, SEEK_END);
 299+ if ((($this->info['filesize'] != 0) && (ftell($fp) == 0)) ||
 300+ ($this->info['filesize'] < 0) ||
 301+ (ftell($fp) < 0)) {
 302+ $real_filesize = false;
 303+ if (GETID3_OS_ISWINDOWS) {
 304+ $commandline = 'dir /-C "'.str_replace('/', DIRECTORY_SEPARATOR, $filename).'"';
 305+ $dir_output = `$commandline`;
 306+ if (preg_match('#1 File\(s\)[ ]+([0-9]+) bytes#i', $dir_output, $matches)) {
 307+ $real_filesize = (float) $matches[1];
 308+ }
 309+ } else {
 310+ $commandline = 'ls -o -g -G --time-style=long-iso '.escapeshellarg($filename);
 311+ $dir_output = `$commandline`;
 312+ if (preg_match('#([0-9]+) ([0-9]{4}-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}) '.str_replace('#', '\\#', preg_quote($filename)).'$#', $dir_output, $matches)) {
 313+ $real_filesize = (float) $matches[1];
 314+ }
 315+ }
 316+ if ($real_filesize === false) {
 317+ unset($this->info['filesize']);
 318+ fclose($fp);
 319+ return $this->error('File is most likely larger than 2GB and is not supported by PHP');
 320+ } elseif ($real_filesize < pow(2, 31)) {
 321+ unset($this->info['filesize']);
 322+ fclose($fp);
 323+ return $this->error('PHP seems to think the file is larger than 2GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org');
 324+ }
 325+ $this->info['filesize'] = $real_filesize;
 326+ $this->error('File is larger than 2GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.');
 327+ }
 328+ }
 329+
 330+ // set more parameters
 331+ $this->info['avdataoffset'] = 0;
 332+ $this->info['avdataend'] = $this->info['filesize'];
 333+ $this->info['fileformat'] = ''; // filled in later
 334+ $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used
 335+ $this->info['video']['dataformat'] = ''; // filled in later, unset if not used
 336+ $this->info['tags'] = array(); // filled in later, unset if not used
 337+ $this->info['error'] = array(); // filled in later, unset if not used
 338+ $this->info['warning'] = array(); // filled in later, unset if not used
 339+ $this->info['comments'] = array(); // filled in later, unset if not used
 340+ $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired
 341+
 342+ // set redundant parameters - might be needed in some include file
 343+ $this->info['filename'] = basename($filename);
 344+ $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
 345+ $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
 346+
 347+
 348+ // handle ID3v2 tag - done first - already at beginning of file
 349+ // ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect
 350+ if ($this->option_tag_id3v2) {
 351+
 352+ $GETID3_ERRORARRAY = &$this->info['warning'];
 353+ if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, false)) {
 354+ $tag = new getid3_id3v2($fp, $this->info);
 355+ unset($tag);
 356+ }
 357+
 358+ } else {
 359+
 360+ fseek($fp, 0, SEEK_SET);
 361+ $header = fread($fp, 10);
 362+ if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
 363+ $this->info['id3v2']['header'] = true;
 364+ $this->info['id3v2']['majorversion'] = ord($header{3});
 365+ $this->info['id3v2']['minorversion'] = ord($header{4});
 366+ $this->info['id3v2']['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
 367+
 368+ $this->info['id3v2']['tag_offset_start'] = 0;
 369+ $this->info['id3v2']['tag_offset_end'] = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength'];
 370+ $this->info['avdataoffset'] = $this->info['id3v2']['tag_offset_end'];
 371+ }
 372+
 373+ }
 374+
 375+
 376+ // handle ID3v1 tag
 377+ if ($this->option_tag_id3v1) {
 378+ if (!file_exists(GETID3_INCLUDEPATH.'module.tag.id3v1.php') || !include_once(GETID3_INCLUDEPATH.'module.tag.id3v1.php')) {
 379+ return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.');
 380+ }
 381+ $tag = new getid3_id3v1($fp, $this->info);
 382+ unset($tag);
 383+ }
 384+
 385+ // handle APE tag
 386+ if ($this->option_tag_apetag) {
 387+ if (!file_exists(GETID3_INCLUDEPATH.'module.tag.apetag.php') || !include_once(GETID3_INCLUDEPATH.'module.tag.apetag.php')) {
 388+ return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.');
 389+ }
 390+ $tag = new getid3_apetag($fp, $this->info);
 391+ unset($tag);
 392+ }
 393+
 394+ // handle lyrics3 tag
 395+ if ($this->option_tag_lyrics3) {
 396+ if (!file_exists(GETID3_INCLUDEPATH.'module.tag.lyrics3.php') || !include_once(GETID3_INCLUDEPATH.'module.tag.lyrics3.php')) {
 397+ return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.');
 398+ }
 399+ $tag = new getid3_lyrics3($fp, $this->info);
 400+ unset($tag);
 401+ }
 402+
 403+ // read 32 kb file data
 404+ fseek($fp, $this->info['avdataoffset'], SEEK_SET);
 405+ $formattest = fread($fp, 32774);
 406+
 407+ // determine format
 408+ $determined_format = $this->GetFileFormat($formattest, $filename);
 409+
 410+ // unable to determine file format
 411+ if (!$determined_format) {
 412+ fclose($fp);
 413+ return $this->error('unable to determine file format');
 414+ }
 415+
 416+ // check for illegal ID3 tags
 417+ if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
 418+ if ($determined_format['fail_id3'] === 'ERROR') {
 419+ fclose($fp);
 420+ return $this->error('ID3 tags not allowed on this file type.');
 421+ } elseif ($determined_format['fail_id3'] === 'WARNING') {
 422+ $this->info['warning'][] = 'ID3 tags not allowed on this file type.';
 423+ }
 424+ }
 425+
 426+ // check for illegal APE tags
 427+ if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
 428+ if ($determined_format['fail_ape'] === 'ERROR') {
 429+ fclose($fp);
 430+ return $this->error('APE tags not allowed on this file type.');
 431+ } elseif ($determined_format['fail_ape'] === 'WARNING') {
 432+ $this->info['warning'][] = 'APE tags not allowed on this file type.';
 433+ }
 434+ }
 435+
 436+ // set mime type
 437+ $this->info['mime_type'] = $determined_format['mime_type'];
 438+
 439+ // supported format signature pattern detected, but module deleted
 440+ if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
 441+ fclose($fp);
 442+ return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
 443+ }
 444+
 445+ // module requires iconv support
 446+ if (!function_exists('iconv') && !empty($determined_format['iconv_req'])) {
 447+ return $this->error('iconv support is required for this module ('.$determined_format['include'].').');
 448+ }
 449+
 450+ // include module
 451+ include_once(GETID3_INCLUDEPATH.$determined_format['include']);
 452+
 453+ // instantiate module class
 454+ $class_name = 'getid3_'.$determined_format['module'];
 455+ if (!class_exists($class_name)) {
 456+ return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
 457+ }
 458+ if (isset($determined_format['option'])) {
 459+ $class = new $class_name($fp, $this->info, $determined_format['option']);
 460+ } else {
 461+ $class = new $class_name($fp, $this->info);
 462+ }
 463+ unset($class);
 464+
 465+ // close file
 466+ fclose($fp);
 467+
 468+ // process all tags - copy to 'tags' and convert charsets
 469+ if ($this->option_tags_process) {
 470+ $this->HandleAllTags();
 471+ }
 472+
 473+ // perform more calculations
 474+ if ($this->option_extra_info) {
 475+ $this->ChannelsBitratePlaytimeCalculations();
 476+ $this->CalculateCompressionRatioVideo();
 477+ $this->CalculateCompressionRatioAudio();
 478+ $this->CalculateReplayGain();
 479+ $this->ProcessAudioStreams();
 480+ }
 481+
 482+ // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
 483+ if ($this->option_md5_data) {
 484+ // do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
 485+ if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
 486+ $this->getHashdata('md5');
 487+ }
 488+ }
 489+
 490+ // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
 491+ if ($this->option_sha1_data) {
 492+ $this->getHashdata('sha1');
 493+ }
 494+
 495+ // remove undesired keys
 496+ $this->CleanUp();
 497+
 498+ // return info array
 499+ return $this->info;
 500+ }
 501+
 502+
 503+ // private: error handling
 504+ function error($message) {
 505+
 506+ $this->CleanUp();
 507+
 508+ $this->info['error'][] = $message;
 509+ return $this->info;
 510+ }
 511+
 512+
 513+ // private: warning handling
 514+ function warning($message) {
 515+ $this->info['warning'][] = $message;
 516+ return true;
 517+ }
 518+
 519+
 520+ // private: CleanUp
 521+ function CleanUp() {
 522+
 523+ // remove possible empty keys
 524+ $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
 525+ foreach ($AVpossibleEmptyKeys as $dummy => $key) {
 526+ if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
 527+ unset($this->info['audio'][$key]);
 528+ }
 529+ if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
 530+ unset($this->info['video'][$key]);
 531+ }
 532+ }
 533+
 534+ // remove empty root keys
 535+ if (!empty($this->info)) {
 536+ foreach ($this->info as $key => $value) {
 537+ if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
 538+ unset($this->info[$key]);
 539+ }
 540+ }
 541+ }
 542+
 543+ // remove meaningless entries from unknown-format files
 544+ if (empty($this->info['fileformat'])) {
 545+ if (isset($this->info['avdataoffset'])) {
 546+ unset($this->info['avdataoffset']);
 547+ }
 548+ if (isset($this->info['avdataend'])) {
 549+ unset($this->info['avdataend']);
 550+ }
 551+ }
 552+ }
 553+
 554+
 555+ // return array containing information about all supported formats
 556+ function GetFileFormatArray() {
 557+ static $format_info = array();
 558+ if (empty($format_info)) {
 559+ $format_info = array(
 560+
 561+ // Audio formats
 562+
 563+ // AC-3 - audio - Dolby AC-3 / Dolby Digital
 564+ 'ac3' => array(
 565+ 'pattern' => '^\x0B\x77',
 566+ 'group' => 'audio',
 567+ 'module' => 'ac3',
 568+ 'mime_type' => 'audio/ac3',
 569+ ),
 570+
 571+ // AAC - audio - Advanced Audio Coding (AAC) - ADIF format
 572+ 'adif' => array(
 573+ 'pattern' => '^ADIF',
 574+ 'group' => 'audio',
 575+ 'module' => 'aac',
 576+ 'option' => 'adif',
 577+ 'mime_type' => 'application/octet-stream',
 578+ 'fail_ape' => 'WARNING',
 579+ ),
 580+
 581+
 582+ // AA - audio - Audible Audiobook
 583+ 'adts' => array(
 584+ 'pattern' => '^.{4}\x57\x90\x75\x36',
 585+ 'group' => 'audio',
 586+ 'module' => 'aa',
 587+ 'mime_type' => 'audio/audible ',
 588+ ),
 589+
 590+ // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
 591+ 'adts' => array(
 592+ 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]',
 593+ 'group' => 'audio',
 594+ 'module' => 'aac',
 595+ 'option' => 'adts',
 596+ 'mime_type' => 'application/octet-stream',
 597+ 'fail_ape' => 'WARNING',
 598+ ),
 599+
 600+
 601+ // AU - audio - NeXT/Sun AUdio (AU)
 602+ 'au' => array(
 603+ 'pattern' => '^\.snd',
 604+ 'group' => 'audio',
 605+ 'module' => 'au',
 606+ 'mime_type' => 'audio/basic',
 607+ ),
 608+
 609+ // AVR - audio - Audio Visual Research
 610+ 'avr' => array(
 611+ 'pattern' => '^2BIT',
 612+ 'group' => 'audio',
 613+ 'module' => 'avr',
 614+ 'mime_type' => 'application/octet-stream',
 615+ ),
 616+
 617+ // BONK - audio - Bonk v0.9+
 618+ 'bonk' => array(
 619+ 'pattern' => '^\x00(BONK|INFO|META| ID3)',
 620+ 'group' => 'audio',
 621+ 'module' => 'bonk',
 622+ 'mime_type' => 'audio/xmms-bonk',
 623+ ),
 624+
 625+ // DSS - audio - Digital Speech Standard
 626+ 'dss' => array(
 627+ 'pattern' => '^[\x02]dss',
 628+ 'group' => 'audio',
 629+ 'module' => 'dss',
 630+ 'mime_type' => 'application/octet-stream',
 631+ ),
 632+
 633+ // DTS - audio - Dolby Theatre System
 634+ 'dts' => array(
 635+ 'pattern' => '^\x7F\xFE\x80\x01',
 636+ 'group' => 'audio',
 637+ 'module' => 'dts',
 638+ 'mime_type' => 'audio/dts',
 639+ ),
 640+
 641+ // FLAC - audio - Free Lossless Audio Codec
 642+ 'flac' => array(
 643+ 'pattern' => '^fLaC',
 644+ 'group' => 'audio',
 645+ 'module' => 'flac',
 646+ 'mime_type' => 'audio/x-flac',
 647+ ),
 648+
 649+ // LA - audio - Lossless Audio (LA)
 650+ 'la' => array(
 651+ 'pattern' => '^LA0[2-4]',
 652+ 'group' => 'audio',
 653+ 'module' => 'la',
 654+ 'mime_type' => 'application/octet-stream',
 655+ ),
 656+
 657+ // LPAC - audio - Lossless Predictive Audio Compression (LPAC)
 658+ 'lpac' => array(
 659+ 'pattern' => '^LPAC',
 660+ 'group' => 'audio',
 661+ 'module' => 'lpac',
 662+ 'mime_type' => 'application/octet-stream',
 663+ ),
 664+
 665+ // MIDI - audio - MIDI (Musical Instrument Digital Interface)
 666+ 'midi' => array(
 667+ 'pattern' => '^MThd',
 668+ 'group' => 'audio',
 669+ 'module' => 'midi',
 670+ 'mime_type' => 'audio/midi',
 671+ ),
 672+
 673+ // MAC - audio - Monkey's Audio Compressor
 674+ 'mac' => array(
 675+ 'pattern' => '^MAC ',
 676+ 'group' => 'audio',
 677+ 'module' => 'monkey',
 678+ 'mime_type' => 'application/octet-stream',
 679+ ),
 680+
 681+// has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available
 682+// // MOD - audio - MODule (assorted sub-formats)
 683+// 'mod' => array(
 684+// 'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)',
 685+// 'group' => 'audio',
 686+// 'module' => 'mod',
 687+// 'option' => 'mod',
 688+// 'mime_type' => 'audio/mod',
 689+// ),
 690+
 691+ // MOD - audio - MODule (Impulse Tracker)
 692+ 'it' => array(
 693+ 'pattern' => '^IMPM',
 694+ 'group' => 'audio',
 695+ 'module' => 'mod',
 696+ 'option' => 'it',
 697+ 'mime_type' => 'audio/it',
 698+ ),
 699+
 700+ // MOD - audio - MODule (eXtended Module, various sub-formats)
 701+ 'xm' => array(
 702+ 'pattern' => '^Extended Module',
 703+ 'group' => 'audio',
 704+ 'module' => 'mod',
 705+ 'option' => 'xm',
 706+ 'mime_type' => 'audio/xm',
 707+ ),
 708+
 709+ // MOD - audio - MODule (ScreamTracker)
 710+ 's3m' => array(
 711+ 'pattern' => '^.{44}SCRM',
 712+ 'group' => 'audio',
 713+ 'module' => 'mod',
 714+ 'option' => 's3m',
 715+ 'mime_type' => 'audio/s3m',
 716+ ),
 717+
 718+ // MPC - audio - Musepack / MPEGplus
 719+ 'mpc' => array(
 720+ 'pattern' => '^(MPCK|MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
 721+ 'group' => 'audio',
 722+ 'module' => 'mpc',
 723+ 'mime_type' => 'audio/x-musepack',
 724+ ),
 725+
 726+ // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS)
 727+ 'mp3' => array(
 728+ 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\x0B\x10-\x1B\x20-\x2B\x30-\x3B\x40-\x4B\x50-\x5B\x60-\x6B\x70-\x7B\x80-\x8B\x90-\x9B\xA0-\xAB\xB0-\xBB\xC0-\xCB\xD0-\xDB\xE0-\xEB\xF0-\xFB]',
 729+ 'group' => 'audio',
 730+ 'module' => 'mp3',
 731+ 'mime_type' => 'audio/mpeg',
 732+ ),
 733+
 734+ // OFR - audio - OptimFROG
 735+ 'ofr' => array(
 736+ 'pattern' => '^(\*RIFF|OFR)',
 737+ 'group' => 'audio',
 738+ 'module' => 'optimfrog',
 739+ 'mime_type' => 'application/octet-stream',
 740+ ),
 741+
 742+ // RKAU - audio - RKive AUdio compressor
 743+ 'rkau' => array(
 744+ 'pattern' => '^RKA',
 745+ 'group' => 'audio',
 746+ 'module' => 'rkau',
 747+ 'mime_type' => 'application/octet-stream',
 748+ ),
 749+
 750+ // SHN - audio - Shorten
 751+ 'shn' => array(
 752+ 'pattern' => '^ajkg',
 753+ 'group' => 'audio',
 754+ 'module' => 'shorten',
 755+ 'mime_type' => 'audio/xmms-shn',
 756+ 'fail_id3' => 'ERROR',
 757+ 'fail_ape' => 'ERROR',
 758+ ),
 759+
 760+ // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
 761+ 'tta' => array(
 762+ 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)'
 763+ 'group' => 'audio',
 764+ 'module' => 'tta',
 765+ 'mime_type' => 'application/octet-stream',
 766+ ),
 767+
 768+ // VOC - audio - Creative Voice (VOC)
 769+ 'voc' => array(
 770+ 'pattern' => '^Creative Voice File',
 771+ 'group' => 'audio',
 772+ 'module' => 'voc',
 773+ 'mime_type' => 'audio/voc',
 774+ ),
 775+
 776+ // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF)
 777+ 'vqf' => array(
 778+ 'pattern' => '^TWIN',
 779+ 'group' => 'audio',
 780+ 'module' => 'vqf',
 781+ 'mime_type' => 'application/octet-stream',
 782+ ),
 783+
 784+ // WV - audio - WavPack (v4.0+)
 785+ 'wv' => array(
 786+ 'pattern' => '^wvpk',
 787+ 'group' => 'audio',
 788+ 'module' => 'wavpack',
 789+ 'mime_type' => 'application/octet-stream',
 790+ ),
 791+
 792+
 793+ // Audio-Video formats
 794+
 795+ // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
 796+ 'asf' => array(
 797+ 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
 798+ 'group' => 'audio-video',
 799+ 'module' => 'asf',
 800+ 'mime_type' => 'video/x-ms-asf',
 801+ 'iconv_req' => false,
 802+ ),
 803+
 804+ // BINK - audio/video - Bink / Smacker
 805+ 'bink' => array(
 806+ 'pattern' => '^(BIK|SMK)',
 807+ 'group' => 'audio-video',
 808+ 'module' => 'bink',
 809+ 'mime_type' => 'application/octet-stream',
 810+ ),
 811+
 812+ // FLV - audio/video - FLash Video
 813+ 'flv' => array(
 814+ 'pattern' => '^FLV\x01',
 815+ 'group' => 'audio-video',
 816+ 'module' => 'flv',
 817+ 'mime_type' => 'video/x-flv',
 818+ ),
 819+
 820+ // MKAV - audio/video - Mastroka
 821+ 'matroska' => array(
 822+ 'pattern' => '^\x1A\x45\xDF\xA3',
 823+ 'group' => 'audio-video',
 824+ 'module' => 'matroska',
 825+ 'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
 826+ ),
 827+
 828+ // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
 829+ 'mpeg' => array(
 830+ 'pattern' => '^\x00\x00\x01(\xBA|\xB3)',
 831+ 'group' => 'audio-video',
 832+ 'module' => 'mpeg',
 833+ 'mime_type' => 'video/mpeg',
 834+ ),
 835+
 836+ // NSV - audio/video - Nullsoft Streaming Video (NSV)
 837+ 'nsv' => array(
 838+ 'pattern' => '^NSV[sf]',
 839+ 'group' => 'audio-video',
 840+ 'module' => 'nsv',
 841+ 'mime_type' => 'application/octet-stream',
 842+ ),
 843+
 844+ // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
 845+ 'ogg' => array(
 846+ 'pattern' => '^OggS',
 847+ 'group' => 'audio',
 848+ 'module' => 'ogg',
 849+ 'mime_type' => 'application/ogg',
 850+ 'fail_id3' => 'WARNING',
 851+ 'fail_ape' => 'WARNING',
 852+ ),
 853+
 854+ // QT - audio/video - Quicktime
 855+ 'quicktime' => array(
 856+ 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
 857+ 'group' => 'audio-video',
 858+ 'module' => 'quicktime',
 859+ 'mime_type' => 'video/quicktime',
 860+ ),
 861+
 862+ // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
 863+ 'riff' => array(
 864+ 'pattern' => '^(RIFF|SDSS|FORM)',
 865+ 'group' => 'audio-video',
 866+ 'module' => 'riff',
 867+ 'mime_type' => 'audio/x-wave',
 868+ 'fail_ape' => 'WARNING',
 869+ ),
 870+
 871+ // Real - audio/video - RealAudio, RealVideo
 872+ 'real' => array(
 873+ 'pattern' => '^(\\.RMF|\\.ra)',
 874+ 'group' => 'audio-video',
 875+ 'module' => 'real',
 876+ 'mime_type' => 'audio/x-realaudio',
 877+ ),
 878+
 879+ // SWF - audio/video - ShockWave Flash
 880+ 'swf' => array(
 881+ 'pattern' => '^(F|C)WS',
 882+ 'group' => 'audio-video',
 883+ 'module' => 'swf',
 884+ 'mime_type' => 'application/x-shockwave-flash',
 885+ ),
 886+
 887+
 888+ // Still-Image formats
 889+
 890+ // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
 891+ 'bmp' => array(
 892+ 'pattern' => '^BM',
 893+ 'group' => 'graphic',
 894+ 'module' => 'bmp',
 895+ 'mime_type' => 'image/bmp',
 896+ 'fail_id3' => 'ERROR',
 897+ 'fail_ape' => 'ERROR',
 898+ ),
 899+
 900+ // GIF - still image - Graphics Interchange Format
 901+ 'gif' => array(
 902+ 'pattern' => '^GIF',
 903+ 'group' => 'graphic',
 904+ 'module' => 'gif',
 905+ 'mime_type' => 'image/gif',
 906+ 'fail_id3' => 'ERROR',
 907+ 'fail_ape' => 'ERROR',
 908+ ),
 909+
 910+ // JPEG - still image - Joint Photographic Experts Group (JPEG)
 911+ 'jpg' => array(
 912+ 'pattern' => '^\xFF\xD8\xFF',
 913+ 'group' => 'graphic',
 914+ 'module' => 'jpg',
 915+ 'mime_type' => 'image/jpeg',
 916+ 'fail_id3' => 'ERROR',
 917+ 'fail_ape' => 'ERROR',
 918+ ),
 919+
 920+ // PCD - still image - Kodak Photo CD
 921+ 'pcd' => array(
 922+ 'pattern' => '^.{2048}PCD_IPI\x00',
 923+ 'group' => 'graphic',
 924+ 'module' => 'pcd',
 925+ 'mime_type' => 'image/x-photo-cd',
 926+ 'fail_id3' => 'ERROR',
 927+ 'fail_ape' => 'ERROR',
 928+ ),
 929+
 930+
 931+ // PNG - still image - Portable Network Graphics (PNG)
 932+ 'png' => array(
 933+ 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
 934+ 'group' => 'graphic',
 935+ 'module' => 'png',
 936+ 'mime_type' => 'image/png',
 937+ 'fail_id3' => 'ERROR',
 938+ 'fail_ape' => 'ERROR',
 939+ ),
 940+
 941+
 942+ // SVG - still image - Scalable Vector Graphics (SVG)
 943+ 'svg' => array(
 944+ 'pattern' => '(<!DOCTYPE svg PUBLIC |xmlns="http:\/\/www\.w3\.org\/2000\/svg")',
 945+ 'group' => 'graphic',
 946+ 'module' => 'svg',
 947+ 'mime_type' => 'image/svg+xml',
 948+ 'fail_id3' => 'ERROR',
 949+ 'fail_ape' => 'ERROR',
 950+ ),
 951+
 952+
 953+ // TIFF - still image - Tagged Information File Format (TIFF)
 954+ 'tiff' => array(
 955+ 'pattern' => '^(II\x2A\x00|MM\x00\x2A)',
 956+ 'group' => 'graphic',
 957+ 'module' => 'tiff',
 958+ 'mime_type' => 'image/tiff',
 959+ 'fail_id3' => 'ERROR',
 960+ 'fail_ape' => 'ERROR',
 961+ ),
 962+
 963+
 964+ // Data formats
 965+
 966+ // ISO - data - International Standards Organization (ISO) CD-ROM Image
 967+ 'iso' => array(
 968+ 'pattern' => '^.{32769}CD001',
 969+ 'group' => 'misc',
 970+ 'module' => 'iso',
 971+ 'mime_type' => 'application/octet-stream',
 972+ 'fail_id3' => 'ERROR',
 973+ 'fail_ape' => 'ERROR',
 974+ 'iconv_req' => false,
 975+ ),
 976+
 977+ // RAR - data - RAR compressed data
 978+ 'rar' => array(
 979+ 'pattern' => '^Rar\!',
 980+ 'group' => 'archive',
 981+ 'module' => 'rar',
 982+ 'mime_type' => 'application/octet-stream',
 983+ 'fail_id3' => 'ERROR',
 984+ 'fail_ape' => 'ERROR',
 985+ ),
 986+
 987+ // SZIP - audio/data - SZIP compressed data
 988+ 'szip' => array(
 989+ 'pattern' => '^SZ\x0A\x04',
 990+ 'group' => 'archive',
 991+ 'module' => 'szip',
 992+ 'mime_type' => 'application/octet-stream',
 993+ 'fail_id3' => 'ERROR',
 994+ 'fail_ape' => 'ERROR',
 995+ ),
 996+
 997+ // TAR - data - TAR compressed data
 998+ 'tar' => array(
 999+ 'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
 1000+ 'group' => 'archive',
 1001+ 'module' => 'tar',
 1002+ 'mime_type' => 'application/x-tar',
 1003+ 'fail_id3' => 'ERROR',
 1004+ 'fail_ape' => 'ERROR',
 1005+ ),
 1006+
 1007+ // GZIP - data - GZIP compressed data
 1008+ 'gz' => array(
 1009+ 'pattern' => '^\x1F\x8B\x08',
 1010+ 'group' => 'archive',
 1011+ 'module' => 'gzip',
 1012+ 'mime_type' => 'application/x-gzip',
 1013+ 'fail_id3' => 'ERROR',
 1014+ 'fail_ape' => 'ERROR',
 1015+ ),
 1016+
 1017+ // ZIP - data - ZIP compressed data
 1018+ 'zip' => array(
 1019+ 'pattern' => '^PK\x03\x04',
 1020+ 'group' => 'archive',
 1021+ 'module' => 'zip',
 1022+ 'mime_type' => 'application/zip',
 1023+ 'fail_id3' => 'ERROR',
 1024+ 'fail_ape' => 'ERROR',
 1025+ ),
 1026+
 1027+
 1028+ // Misc other formats
 1029+
 1030+ // PAR2 - data - Parity Volume Set Specification 2.0
 1031+ 'par2' => array (
 1032+ 'pattern' => '^PAR2\x00PKT',
 1033+ 'group' => 'misc',
 1034+ 'module' => 'par2',
 1035+ 'mime_type' => 'application/octet-stream',
 1036+ 'fail_id3' => 'ERROR',
 1037+ 'fail_ape' => 'ERROR',
 1038+ ),
 1039+
 1040+ // PDF - data - Portable Document Format
 1041+ 'pdf' => array(
 1042+ 'pattern' => '^\x25PDF',
 1043+ 'group' => 'misc',
 1044+ 'module' => 'pdf',
 1045+ 'mime_type' => 'application/pdf',
 1046+ 'fail_id3' => 'ERROR',
 1047+ 'fail_ape' => 'ERROR',
 1048+ ),
 1049+
 1050+ // MSOFFICE - data - ZIP compressed data
 1051+ 'msoffice' => array(
 1052+ 'pattern' => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document
 1053+ 'group' => 'misc',
 1054+ 'module' => 'msoffice',
 1055+ 'mime_type' => 'application/octet-stream',
 1056+ 'fail_id3' => 'ERROR',
 1057+ 'fail_ape' => 'ERROR',
 1058+ ),
 1059+
 1060+ // CUE - data - CUEsheet (index to single-file disc images)
 1061+ 'cue' => array(
 1062+ 'pattern' => '', // empty pattern means cannot be automatically detected, will fall through all other formats and match based on filename and very basic file contents
 1063+ 'group' => 'misc',
 1064+ 'module' => 'cue',
 1065+ 'mime_type' => 'application/octet-stream',
 1066+ ),
 1067+
 1068+ );
 1069+ }
 1070+
 1071+ return $format_info;
 1072+ }
 1073+
 1074+
 1075+
 1076+ function GetFileFormat(&$filedata, $filename='') {
 1077+ // this function will determine the format of a file based on usually
 1078+ // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
 1079+ // and in the case of ISO CD image, 6 bytes offset 32kb from the start
 1080+ // of the file).
 1081+
 1082+ // Identify file format - loop through $format_info and detect with reg expr
 1083+ foreach ($this->GetFileFormatArray() as $format_name => $info) {
 1084+ // The /s switch on preg_match() forces preg_match() NOT to treat
 1085+ // newline (0x0A) characters as special chars but do a binary match
 1086+ if (!empty($info['pattern']) && preg_match('#'.$info['pattern'].'#s', $filedata)) {
 1087+ $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
 1088+ return $info;
 1089+ }
 1090+ }
 1091+
 1092+
 1093+ if (preg_match('#\.mp[123a]$#i', $filename)) {
 1094+ // Too many mp3 encoders on the market put gabage in front of mpeg files
 1095+ // use assume format on these if format detection failed
 1096+ $GetFileFormatArray = $this->GetFileFormatArray();
 1097+ $info = $GetFileFormatArray['mp3'];
 1098+ $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
 1099+ return $info;
 1100+ } elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
 1101+ // there's not really a useful consistent "magic" at the beginning of .cue files to identify them
 1102+ // so until I think of something better, just go by filename if all other format checks fail
 1103+ // and verify there's at least one instance of "TRACK xx AUDIO" in the file
 1104+ $GetFileFormatArray = $this->GetFileFormatArray();
 1105+ $info = $GetFileFormatArray['cue'];
 1106+ $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
 1107+ return $info;
 1108+ }
 1109+
 1110+ return false;
 1111+ }
 1112+
 1113+
 1114+ // converts array to $encoding charset from $this->encoding
 1115+ function CharConvert(&$array, $encoding) {
 1116+
 1117+ // identical encoding - end here
 1118+ if ($encoding == $this->encoding) {
 1119+ return;
 1120+ }
 1121+
 1122+ // loop thru array
 1123+ foreach ($array as $key => $value) {
 1124+
 1125+ // go recursive
 1126+ if (is_array($value)) {
 1127+ $this->CharConvert($array[$key], $encoding);
 1128+ }
 1129+
 1130+ // convert string
 1131+ elseif (is_string($value)) {
 1132+ $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
 1133+ }
 1134+ }
 1135+ }
 1136+
 1137+
 1138+ function HandleAllTags() {
 1139+
 1140+ // key name => array (tag name, character encoding)
 1141+ static $tags;
 1142+ if (empty($tags)) {
 1143+ $tags = array(
 1144+ 'asf' => array('asf' , 'UTF-16LE'),
 1145+ 'midi' => array('midi' , 'ISO-8859-1'),
 1146+ 'nsv' => array('nsv' , 'ISO-8859-1'),
 1147+ 'ogg' => array('vorbiscomment' , 'UTF-8'),
 1148+ 'png' => array('png' , 'UTF-8'),
 1149+ 'tiff' => array('tiff' , 'ISO-8859-1'),
 1150+ 'quicktime' => array('quicktime' , 'UTF-8'),
 1151+ 'real' => array('real' , 'ISO-8859-1'),
 1152+ 'vqf' => array('vqf' , 'ISO-8859-1'),
 1153+ 'zip' => array('zip' , 'ISO-8859-1'),
 1154+ 'riff' => array('riff' , 'ISO-8859-1'),
 1155+ 'lyrics3' => array('lyrics3' , 'ISO-8859-1'),
 1156+ 'id3v1' => array('id3v1' , $this->encoding_id3v1),
 1157+ 'id3v2' => array('id3v2' , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8
 1158+ 'ape' => array('ape' , 'UTF-8'),
 1159+ 'cue' => array('cue' , 'ISO-8859-1'),
 1160+ );
 1161+ }
 1162+
 1163+ // loop thru comments array
 1164+ foreach ($tags as $comment_name => $tagname_encoding_array) {
 1165+ list($tag_name, $encoding) = $tagname_encoding_array;
 1166+
 1167+ // fill in default encoding type if not already present
 1168+ if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
 1169+ $this->info[$comment_name]['encoding'] = $encoding;
 1170+ }
 1171+
 1172+ // copy comments if key name set
 1173+ if (!empty($this->info[$comment_name]['comments'])) {
 1174+
 1175+ foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
 1176+ foreach ($valuearray as $key => $value) {
 1177+ if (strlen(trim($value)) > 0) {
 1178+ $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed!
 1179+ }
 1180+ }
 1181+ }
 1182+
 1183+ if (!isset($this->info['tags'][$tag_name])) {
 1184+ // comments are set but contain nothing but empty strings, so skip
 1185+ continue;
 1186+ }
 1187+
 1188+ if ($this->option_tags_html) {
 1189+ foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
 1190+ foreach ($valuearray as $key => $value) {
 1191+ if (is_string($value)) {
 1192+ //$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding);
 1193+ $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $encoding));
 1194+ } else {
 1195+ $this->info['tags_html'][$tag_name][$tag_key][$key] = $value;
 1196+ }
 1197+ }
 1198+ }
 1199+ }
 1200+
 1201+ $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted!
 1202+ }
 1203+
 1204+ }
 1205+ return true;
 1206+ }
 1207+
 1208+
 1209+ function getHashdata($algorithm) {
 1210+ switch ($algorithm) {
 1211+ case 'md5':
 1212+ case 'sha1':
 1213+ break;
 1214+
 1215+ default:
 1216+ return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
 1217+ break;
 1218+ }
 1219+
 1220+ if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) {
 1221+
 1222+ // We cannot get an identical md5_data value for Ogg files where the comments
 1223+ // span more than 1 Ogg page (compared to the same audio data with smaller
 1224+ // comments) using the normal getID3() method of MD5'ing the data between the
 1225+ // end of the comments and the end of the file (minus any trailing tags),
 1226+ // because the page sequence numbers of the pages that the audio data is on
 1227+ // do not match. Under normal circumstances, where comments are smaller than
 1228+ // the nominal 4-8kB page size, then this is not a problem, but if there are
 1229+ // very large comments, the only way around it is to strip off the comment
 1230+ // tags with vorbiscomment and MD5 that file.
 1231+ // This procedure must be applied to ALL Ogg files, not just the ones with
 1232+ // comments larger than 1 page, because the below method simply MD5's the
 1233+ // whole file with the comments stripped, not just the portion after the
 1234+ // comments block (which is the standard getID3() method.
 1235+
 1236+ // The above-mentioned problem of comments spanning multiple pages and changing
 1237+ // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
 1238+ // currently vorbiscomment only works on OggVorbis files.
 1239+
 1240+ if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
 1241+
 1242+ $this->info['warning'][] = 'Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)';
 1243+ $this->info[$algorithm.'_data'] = false;
 1244+
 1245+ } else {
 1246+
 1247+ // Prevent user from aborting script
 1248+ $old_abort = ignore_user_abort(true);
 1249+
 1250+ // Create empty file
 1251+ $empty = tempnam(GETID3_TEMP_DIR, 'getID3');
 1252+ touch($empty);
 1253+
 1254+ // Use vorbiscomment to make temp file without comments
 1255+ $temp = tempnam(GETID3_TEMP_DIR, 'getID3');
 1256+ $file = $this->info['filenamepath'];
 1257+
 1258+ if (GETID3_OS_ISWINDOWS) {
 1259+
 1260+ if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
 1261+
 1262+ $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
 1263+ $VorbisCommentError = `$commandline`;
 1264+
 1265+ } else {
 1266+
 1267+ $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
 1268+
 1269+ }
 1270+
 1271+ } else {
 1272+
 1273+ $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
 1274+ $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
 1275+ $VorbisCommentError = `$commandline`;
 1276+
 1277+ }
 1278+
 1279+ if (!empty($VorbisCommentError)) {
 1280+
 1281+ $this->info['warning'][] = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError;
 1282+ $this->info[$algorithm.'_data'] = false;
 1283+
 1284+ } else {
 1285+
 1286+ // Get hash of newly created file
 1287+ switch ($algorithm) {
 1288+ case 'md5':
 1289+ $this->info[$algorithm.'_data'] = getid3_lib::md5_file($temp);
 1290+ break;
 1291+
 1292+ case 'sha1':
 1293+ $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($temp);
 1294+ break;
 1295+ }
 1296+ }
 1297+
 1298+ // Clean up
 1299+ unlink($empty);
 1300+ unlink($temp);
 1301+
 1302+ // Reset abort setting
 1303+ ignore_user_abort($old_abort);
 1304+
 1305+ }
 1306+
 1307+ } else {
 1308+
 1309+ if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
 1310+
 1311+ // get hash from part of file
 1312+ $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
 1313+
 1314+ } else {
 1315+
 1316+ // get hash from whole file
 1317+ switch ($algorithm) {
 1318+ case 'md5':
 1319+ $this->info[$algorithm.'_data'] = getid3_lib::md5_file($this->info['filenamepath']);
 1320+ break;
 1321+
 1322+ case 'sha1':
 1323+ $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($this->info['filenamepath']);
 1324+ break;
 1325+ }
 1326+ }
 1327+
 1328+ }
 1329+ return true;
 1330+ }
 1331+
 1332+
 1333+ function ChannelsBitratePlaytimeCalculations() {
 1334+
 1335+ // set channelmode on audio
 1336+ if (!isset($this->info['audio']['channels'])) {
 1337+ // ignore
 1338+ } elseif ($this->info['audio']['channels'] == 1) {
 1339+ $this->info['audio']['channelmode'] = 'mono';
 1340+ } elseif ($this->info['audio']['channels'] == 2) {
 1341+ $this->info['audio']['channelmode'] = 'stereo';
 1342+ }
 1343+
 1344+ // Calculate combined bitrate - audio + video
 1345+ $CombinedBitrate = 0;
 1346+ $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
 1347+ $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
 1348+ if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
 1349+ $this->info['bitrate'] = $CombinedBitrate;
 1350+ }
 1351+ //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
 1352+ // // for example, VBR MPEG video files cannot determine video bitrate:
 1353+ // // should not set overall bitrate and playtime from audio bitrate only
 1354+ // unset($this->info['bitrate']);
 1355+ //}
 1356+
 1357+ // video bitrate undetermined, but calculable
 1358+ if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
 1359+ // if video bitrate not set
 1360+ if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
 1361+ // AND if audio bitrate is set to same as overall bitrate
 1362+ if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
 1363+ // AND if playtime is set
 1364+ if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
 1365+ // AND if AV data offset start/end is known
 1366+ // THEN we can calculate the video bitrate
 1367+ $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
 1368+ $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
 1369+ }
 1370+ }
 1371+ }
 1372+ }
 1373+
 1374+ if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) {
 1375+ $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
 1376+ }
 1377+
 1378+ if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) {
 1379+ $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds'];
 1380+ }
 1381+ if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) {
 1382+ if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) {
 1383+ // audio only
 1384+ $this->info['audio']['bitrate'] = $this->info['bitrate'];
 1385+ } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) {
 1386+ // video only
 1387+ $this->info['video']['bitrate'] = $this->info['bitrate'];
 1388+ }
 1389+ }
 1390+
 1391+ // Set playtime string
 1392+ if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
 1393+ $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
 1394+ }
 1395+ }
 1396+
 1397+
 1398+ function CalculateCompressionRatioVideo() {
 1399+ if (empty($this->info['video'])) {
 1400+ return false;
 1401+ }
 1402+ if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
 1403+ return false;
 1404+ }
 1405+ if (empty($this->info['video']['bits_per_sample'])) {
 1406+ return false;
 1407+ }
 1408+
 1409+ switch ($this->info['video']['dataformat']) {
 1410+ case 'bmp':
 1411+ case 'gif':
 1412+ case 'jpeg':
 1413+ case 'jpg':
 1414+ case 'png':
 1415+ case 'tiff':
 1416+ $FrameRate = 1;
 1417+ $PlaytimeSeconds = 1;
 1418+ $BitrateCompressed = $this->info['filesize'] * 8;
 1419+ break;
 1420+
 1421+ default:
 1422+ if (!empty($this->info['video']['frame_rate'])) {
 1423+ $FrameRate = $this->info['video']['frame_rate'];
 1424+ } else {
 1425+ return false;
 1426+ }
 1427+ if (!empty($this->info['playtime_seconds'])) {
 1428+ $PlaytimeSeconds = $this->info['playtime_seconds'];
 1429+ } else {
 1430+ return false;
 1431+ }
 1432+ if (!empty($this->info['video']['bitrate'])) {
 1433+ $BitrateCompressed = $this->info['video']['bitrate'];
 1434+ } else {
 1435+ return false;
 1436+ }
 1437+ break;
 1438+ }
 1439+ $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
 1440+
 1441+ $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
 1442+ return true;
 1443+ }
 1444+
 1445+
 1446+ function CalculateCompressionRatioAudio() {
 1447+ if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) {
 1448+ return false;
 1449+ }
 1450+ $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16));
 1451+
 1452+ if (!empty($this->info['audio']['streams'])) {
 1453+ foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
 1454+ if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
 1455+ $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
 1456+ }
 1457+ }
 1458+ }
 1459+ return true;
 1460+ }
 1461+
 1462+
 1463+ function CalculateReplayGain() {
 1464+ if (isset($this->info['replay_gain'])) {
 1465+ $this->info['replay_gain']['reference_volume'] = 89;
 1466+ if (isset($this->info['replay_gain']['track']['adjustment'])) {
 1467+ $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
 1468+ }
 1469+ if (isset($this->info['replay_gain']['album']['adjustment'])) {
 1470+ $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
 1471+ }
 1472+
 1473+ if (isset($this->info['replay_gain']['track']['peak'])) {
 1474+ $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
 1475+ }
 1476+ if (isset($this->info['replay_gain']['album']['peak'])) {
 1477+ $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
 1478+ }
 1479+ }
 1480+ return true;
 1481+ }
 1482+
 1483+ function ProcessAudioStreams() {
 1484+ if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
 1485+ if (!isset($this->info['audio']['streams'])) {
 1486+ foreach ($this->info['audio'] as $key => $value) {
 1487+ if ($key != 'streams') {
 1488+ $this->info['audio']['streams'][0][$key] = $value;
 1489+ }
 1490+ }
 1491+ }
 1492+ }
 1493+ return true;
 1494+ }
 1495+
 1496+ function getid3_tempnam() {
 1497+ return tempnam($this->tempdir, 'gI3');
 1498+ }
 1499+
 1500+}
 1501+
 1502+?>
\ No newline at end of file
Index: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/getid3/module.audio.ogg.php
@@ -0,0 +1,565 @@
 2+<?php
 3+/////////////////////////////////////////////////////////////////
 4+/// getID3() by James Heinrich <info@getid3.org> //
 5+// available at http://getid3.sourceforge.net //
 6+// or http://www.getid3.org //
 7+/////////////////////////////////////////////////////////////////
 8+// See readme.txt for more details //
 9+/////////////////////////////////////////////////////////////////
 10+// //
 11+// module.audio.ogg.php //
 12+// module for analyzing Ogg Vorbis, OggFLAC and Speex files //
 13+// dependencies: module.audio.flac.php //
 14+// ///
 15+/////////////////////////////////////////////////////////////////
 16+
 17+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
 18+
 19+class getid3_ogg
 20+{
 21+
 22+ function getid3_ogg(&$fd, &$ThisFileInfo) {
 23+
 24+ $ThisFileInfo['fileformat'] = 'ogg';
 25+
 26+ // Warn about illegal tags - only vorbiscomments are allowed
 27+ if (isset($ThisFileInfo['id3v2'])) {
 28+ $ThisFileInfo['warning'][] = 'Illegal ID3v2 tag present.';
 29+ }
 30+ if (isset($ThisFileInfo['id3v1'])) {
 31+ $ThisFileInfo['warning'][] = 'Illegal ID3v1 tag present.';
 32+ }
 33+ if (isset($ThisFileInfo['ape'])) {
 34+ $ThisFileInfo['warning'][] = 'Illegal APE tag present.';
 35+ }
 36+
 37+
 38+ // Page 1 - Stream Header
 39+
 40+ fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
 41+
 42+ $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
 43+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 44+
 45+ if (ftell($fd) >= GETID3_FREAD_BUFFER_SIZE) {
 46+ $ThisFileInfo['error'][] = 'Could not find start of Ogg page in the first '.GETID3_FREAD_BUFFER_SIZE.' bytes (this might not be an Ogg-Vorbis file?)';
 47+ unset($ThisFileInfo['fileformat']);
 48+ unset($ThisFileInfo['ogg']);
 49+ return false;
 50+ }
 51+
 52+ $filedata = fread($fd, $oggpageinfo['page_length']);
 53+ $filedataoffset = 0;
 54+
 55+ if (substr($filedata, 0, 4) == 'fLaC') {
 56+
 57+ $ThisFileInfo['audio']['dataformat'] = 'flac';
 58+ $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
 59+ $ThisFileInfo['audio']['lossless'] = true;
 60+
 61+ } elseif (substr($filedata, 1, 6) == 'vorbis') {
 62+
 63+ $this->ParseVorbisPageHeader($filedata, $filedataoffset, $ThisFileInfo, $oggpageinfo);
 64+
 65+ } elseif (substr($filedata, 0, 8) == 'Speex ') {
 66+
 67+ // http://www.speex.org/manual/node10.html
 68+
 69+ $ThisFileInfo['audio']['dataformat'] = 'speex';
 70+ $ThisFileInfo['mime_type'] = 'audio/speex';
 71+ $ThisFileInfo['audio']['bitrate_mode'] = 'abr';
 72+ $ThisFileInfo['audio']['lossless'] = false;
 73+
 74+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex '
 75+ $filedataoffset += 8;
 76+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
 77+ $filedataoffset += 20;
 78+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 79+ $filedataoffset += 4;
 80+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 81+ $filedataoffset += 4;
 82+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 83+ $filedataoffset += 4;
 84+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 85+ $filedataoffset += 4;
 86+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 87+ $filedataoffset += 4;
 88+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 89+ $filedataoffset += 4;
 90+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 91+ $filedataoffset += 4;
 92+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 93+ $filedataoffset += 4;
 94+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 95+ $filedataoffset += 4;
 96+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 97+ $filedataoffset += 4;
 98+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 99+ $filedataoffset += 4;
 100+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 101+ $filedataoffset += 4;
 102+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 103+ $filedataoffset += 4;
 104+
 105+ $ThisFileInfo['speex']['speex_version'] = trim($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
 106+ $ThisFileInfo['speex']['sample_rate'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
 107+ $ThisFileInfo['speex']['channels'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
 108+ $ThisFileInfo['speex']['vbr'] = (bool) $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
 109+ $ThisFileInfo['speex']['band_type'] = getid3_ogg::SpeexBandModeLookup($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
 110+
 111+ $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['speex']['sample_rate'];
 112+ $ThisFileInfo['audio']['channels'] = $ThisFileInfo['speex']['channels'];
 113+ if ($ThisFileInfo['speex']['vbr']) {
 114+ $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
 115+ }
 116+
 117+ } else {
 118+
 119+ $ThisFileInfo['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found neither';
 120+ unset($ThisFileInfo['ogg']);
 121+ unset($ThisFileInfo['mime_type']);
 122+ return false;
 123+
 124+ }
 125+
 126+
 127+ // Page 2 - Comment Header
 128+
 129+ $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
 130+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 131+
 132+ switch ($ThisFileInfo['audio']['dataformat']) {
 133+
 134+ case 'vorbis':
 135+ $filedata = fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 136+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
 137+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis'
 138+
 139+ getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
 140+ break;
 141+
 142+ case 'flac':
 143+ if (!getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo)) {
 144+ $ThisFileInfo['error'][] = 'Failed to parse FLAC headers';
 145+ return false;
 146+ }
 147+ break;
 148+
 149+ case 'speex':
 150+ fseek($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
 151+ getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
 152+ break;
 153+
 154+ }
 155+
 156+
 157+
 158+ // Last Page - Number of Samples
 159+
 160+ if ($ThisFileInfo['avdataend'] >= pow(2, 31)) {
 161+
 162+ $ThisFileInfo['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond 2GB)';
 163+
 164+ } else {
 165+
 166+ fseek($fd, max($ThisFileInfo['avdataend'] - GETID3_FREAD_BUFFER_SIZE, 0), SEEK_SET);
 167+ $LastChunkOfOgg = strrev(fread($fd, GETID3_FREAD_BUFFER_SIZE));
 168+ if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
 169+ fseek($fd, $ThisFileInfo['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET);
 170+ $ThisFileInfo['avdataend'] = ftell($fd);
 171+ $ThisFileInfo['ogg']['pageheader']['eos'] = getid3_ogg::ParseOggPageHeader($fd);
 172+ $ThisFileInfo['ogg']['samples'] = $ThisFileInfo['ogg']['pageheader']['eos']['pcm_abs_position'];
 173+ if ($ThisFileInfo['ogg']['samples'] == 0) {
 174+ $ThisFileInfo['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
 175+ return false;
 176+ }
 177+ $ThisFileInfo['ogg']['bitrate_average'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['ogg']['samples'] / $ThisFileInfo['audio']['sample_rate']);
 178+ }
 179+
 180+ }
 181+
 182+ if (!empty($ThisFileInfo['ogg']['bitrate_average'])) {
 183+ $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_average'];
 184+ } elseif (!empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
 185+ $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_nominal'];
 186+ } elseif (!empty($ThisFileInfo['ogg']['bitrate_min']) && !empty($ThisFileInfo['ogg']['bitrate_max'])) {
 187+ $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['ogg']['bitrate_min'] + $ThisFileInfo['ogg']['bitrate_max']) / 2;
 188+ }
 189+ if (isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['playtime_seconds'])) {
 190+ if ($ThisFileInfo['audio']['bitrate'] == 0) {
 191+ $ThisFileInfo['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
 192+ return false;
 193+ }
 194+ $ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']);
 195+ }
 196+
 197+ if (isset($ThisFileInfo['ogg']['vendor'])) {
 198+ $ThisFileInfo['audio']['encoder'] = preg_replace('/^Encoded with /', '', $ThisFileInfo['ogg']['vendor']);
 199+
 200+ // Vorbis only
 201+ if ($ThisFileInfo['audio']['dataformat'] == 'vorbis') {
 202+
 203+ // Vorbis 1.0 starts with Xiph.Org
 204+ if (preg_match('/^Xiph.Org/', $ThisFileInfo['audio']['encoder'])) {
 205+
 206+ if ($ThisFileInfo['audio']['bitrate_mode'] == 'abr') {
 207+
 208+ // Set -b 128 on abr files
 209+ $ThisFileInfo['audio']['encoder_options'] = '-b '.round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000);
 210+
 211+ } elseif (($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') && ($ThisFileInfo['audio']['channels'] == 2) && ($ThisFileInfo['audio']['sample_rate'] >= 44100) && ($ThisFileInfo['audio']['sample_rate'] <= 48000)) {
 212+ // Set -q N on vbr files
 213+ $ThisFileInfo['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($ThisFileInfo['ogg']['bitrate_nominal']);
 214+
 215+ }
 216+ }
 217+
 218+ if (empty($ThisFileInfo['audio']['encoder_options']) && !empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
 219+ $ThisFileInfo['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000)).'kbps';
 220+ }
 221+ }
 222+ }
 223+
 224+ return true;
 225+ }
 226+
 227+ static function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$ThisFileInfo, &$oggpageinfo) {
 228+ $ThisFileInfo['audio']['dataformat'] = 'vorbis';
 229+ $ThisFileInfo['audio']['lossless'] = false;
 230+
 231+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 232+ $filedataoffset += 1;
 233+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
 234+ $filedataoffset += 6;
 235+ $ThisFileInfo['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 236+ $filedataoffset += 4;
 237+ $ThisFileInfo['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 238+ $filedataoffset += 1;
 239+ $ThisFileInfo['audio']['channels'] = $ThisFileInfo['ogg']['numberofchannels'];
 240+ $ThisFileInfo['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 241+ $filedataoffset += 4;
 242+ if ($ThisFileInfo['ogg']['samplerate'] == 0) {
 243+ $ThisFileInfo['error'][] = 'Corrupt Ogg file: sample rate == zero';
 244+ return false;
 245+ }
 246+ $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['ogg']['samplerate'];
 247+ $ThisFileInfo['ogg']['samples'] = 0; // filled in later
 248+ $ThisFileInfo['ogg']['bitrate_average'] = 0; // filled in later
 249+ $ThisFileInfo['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 250+ $filedataoffset += 4;
 251+ $ThisFileInfo['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 252+ $filedataoffset += 4;
 253+ $ThisFileInfo['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 254+ $filedataoffset += 4;
 255+ $ThisFileInfo['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
 256+ $ThisFileInfo['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
 257+ $ThisFileInfo['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
 258+
 259+ $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
 260+ if ($ThisFileInfo['ogg']['bitrate_max'] == 0xFFFFFFFF) {
 261+ unset($ThisFileInfo['ogg']['bitrate_max']);
 262+ $ThisFileInfo['audio']['bitrate_mode'] = 'abr';
 263+ }
 264+ if ($ThisFileInfo['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
 265+ unset($ThisFileInfo['ogg']['bitrate_nominal']);
 266+ }
 267+ if ($ThisFileInfo['ogg']['bitrate_min'] == 0xFFFFFFFF) {
 268+ unset($ThisFileInfo['ogg']['bitrate_min']);
 269+ $ThisFileInfo['audio']['bitrate_mode'] = 'abr';
 270+ }
 271+ return true;
 272+ }
 273+
 274+ static function ParseOggPageHeader(&$fd) {
 275+ // http://xiph.org/ogg/vorbis/doc/framing.html
 276+ $oggheader['page_start_offset'] = ftell($fd); // where we started from in the file
 277+
 278+ $filedata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
 279+ $filedataoffset = 0;
 280+ while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
 281+ if ((ftell($fd) - $oggheader['page_start_offset']) >= GETID3_FREAD_BUFFER_SIZE) {
 282+ // should be found before here
 283+ return false;
 284+ }
 285+ if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
 286+ if (feof($fd) || (($filedata .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) === false)) {
 287+ // get some more data, unless eof, in which case fail
 288+ return false;
 289+ }
 290+ }
 291+ }
 292+ $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
 293+
 294+ $oggheader['stream_structver'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 295+ $filedataoffset += 1;
 296+ $oggheader['flags_raw'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 297+ $filedataoffset += 1;
 298+ $oggheader['flags']['fresh'] = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
 299+ $oggheader['flags']['bos'] = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
 300+ $oggheader['flags']['eos'] = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
 301+
 302+ $oggheader['pcm_abs_position'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
 303+ $filedataoffset += 8;
 304+ $oggheader['stream_serialno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 305+ $filedataoffset += 4;
 306+ $oggheader['page_seqno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 307+ $filedataoffset += 4;
 308+ $oggheader['page_checksum'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 309+ $filedataoffset += 4;
 310+ $oggheader['page_segments'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 311+ $filedataoffset += 1;
 312+ $oggheader['page_length'] = 0;
 313+ for ($i = 0; $i < $oggheader['page_segments']; $i++) {
 314+ $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 315+ $filedataoffset += 1;
 316+ $oggheader['page_length'] += $oggheader['segment_table'][$i];
 317+ }
 318+ $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
 319+ $oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length'];
 320+ fseek($fd, $oggheader['header_end_offset'], SEEK_SET);
 321+
 322+ return $oggheader;
 323+ }
 324+
 325+
 326+ static function ParseVorbisCommentsFilepointer(&$fd, &$ThisFileInfo) {
 327+
 328+ $OriginalOffset = ftell($fd);
 329+ $CommentStartOffset = $OriginalOffset;
 330+ $commentdataoffset = 0;
 331+ $VorbisCommentPage = 1;
 332+
 333+ switch ($ThisFileInfo['audio']['dataformat']) {
 334+ case 'vorbis':
 335+ $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
 336+ fseek($fd, $CommentStartOffset, SEEK_SET);
 337+ $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
 338+ $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
 339+
 340+ $commentdataoffset += (strlen('vorbis') + 1);
 341+ break;
 342+
 343+ case 'flac':
 344+ fseek($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET);
 345+ $commentdata = fread($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['block_length']);
 346+ break;
 347+
 348+ case 'speex':
 349+ $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
 350+ fseek($fd, $CommentStartOffset, SEEK_SET);
 351+ $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
 352+ $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
 353+ break;
 354+
 355+ default:
 356+ return false;
 357+ break;
 358+ }
 359+
 360+ $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 361+ $commentdataoffset += 4;
 362+
 363+ $ThisFileInfo['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
 364+ $commentdataoffset += $VendorSize;
 365+
 366+ $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 367+ $commentdataoffset += 4;
 368+ $ThisFileInfo['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
 369+
 370+ $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
 371+ for ($i = 0; $i < $CommentsCount; $i++) {
 372+
 373+ $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
 374+
 375+ if (ftell($fd) < ($ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + 4)) {
 376+ $VorbisCommentPage++;
 377+
 378+ $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
 379+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 380+
 381+ // First, save what we haven't read yet
 382+ $AsYetUnusedData = substr($commentdata, $commentdataoffset);
 383+
 384+ // Then take that data off the end
 385+ $commentdata = substr($commentdata, 0, $commentdataoffset);
 386+
 387+ // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
 388+ $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 389+ $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 390+
 391+ // Finally, stick the unused data back on the end
 392+ $commentdata .= $AsYetUnusedData;
 393+
 394+ //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 395+ $commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
 396+
 397+ }
 398+ $ThisFileInfo['ogg']['comments_raw'][$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 399+
 400+ // replace avdataoffset with position just after the last vorbiscomment
 401+ $ThisFileInfo['avdataoffset'] = $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + $ThisFileInfo['ogg']['comments_raw'][$i]['size'] + 4;
 402+
 403+ $commentdataoffset += 4;
 404+ while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo['ogg']['comments_raw'][$i]['size']) {
 405+ if (($ThisFileInfo['ogg']['comments_raw'][$i]['size'] > $ThisFileInfo['avdataend']) || ($ThisFileInfo['ogg']['comments_raw'][$i]['size'] < 0)) {
 406+ $ThisFileInfo['warning'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo['ogg']['comments_raw'][$i]['size']).' bytes) - aborting reading comments';
 407+ break 2;
 408+ }
 409+
 410+ $VorbisCommentPage++;
 411+
 412+ $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
 413+ $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 414+
 415+ // First, save what we haven't read yet
 416+ $AsYetUnusedData = substr($commentdata, $commentdataoffset);
 417+
 418+ // Then take that data off the end
 419+ $commentdata = substr($commentdata, 0, $commentdataoffset);
 420+
 421+ // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
 422+ $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 423+ $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 424+
 425+ // Finally, stick the unused data back on the end
 426+ $commentdata .= $AsYetUnusedData;
 427+
 428+ //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 429+ if (!isset($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage])) {
 430+ $ThisFileInfo['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($fd);
 431+ break;
 432+ }
 433+ $readlength = getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1);
 434+ if ($readlength <= 0) {
 435+ $ThisFileInfo['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($fd);
 436+ break;
 437+ }
 438+ $commentdata .= fread($fd, $readlength);
 439+
 440+ //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
 441+ }
 442+ $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo['ogg']['comments_raw'][$i]['size']);
 443+ $commentdataoffset += $ThisFileInfo['ogg']['comments_raw'][$i]['size'];
 444+
 445+ if (!$commentstring) {
 446+
 447+ // no comment?
 448+ $ThisFileInfo['warning'][] = 'Blank Ogg comment ['.$i.']';
 449+
 450+ } elseif (strstr($commentstring, '=')) {
 451+
 452+ $commentexploded = explode('=', $commentstring, 2);
 453+ $ThisFileInfo['ogg']['comments_raw'][$i]['key'] = strtoupper($commentexploded[0]);
 454+ $ThisFileInfo['ogg']['comments_raw'][$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : '');
 455+ $ThisFileInfo['ogg']['comments_raw'][$i]['data'] = base64_decode($ThisFileInfo['ogg']['comments_raw'][$i]['value']);
 456+
 457+ $ThisFileInfo['ogg']['comments'][strtolower($ThisFileInfo['ogg']['comments_raw'][$i]['key'])][] = $ThisFileInfo['ogg']['comments_raw'][$i]['value'];
 458+
 459+ $imageinfo = array();
 460+ $imagechunkcheck = getid3_lib::GetDataImageSize($ThisFileInfo['ogg']['comments_raw'][$i]['data'], $imageinfo);
 461+ $ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] = getid3_lib::image_type_to_mime_type($imagechunkcheck[2]);
 462+ if (!$ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] || ($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] == 'application/octet-stream')) {
 463+ unset($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime']);
 464+ unset($ThisFileInfo['ogg']['comments_raw'][$i]['data']);
 465+ }
 466+
 467+ } else {
 468+
 469+ $ThisFileInfo['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
 470+
 471+ }
 472+ }
 473+
 474+
 475+ // Replay Gain Adjustment
 476+ // http://privatewww.essex.ac.uk/~djmrob/replaygain/
 477+ if (isset($ThisFileInfo['ogg']['comments']) && is_array($ThisFileInfo['ogg']['comments'])) {
 478+ foreach ($ThisFileInfo['ogg']['comments'] as $index => $commentvalue) {
 479+ switch ($index) {
 480+ case 'rg_audiophile':
 481+ case 'replaygain_album_gain':
 482+ $ThisFileInfo['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
 483+ unset($ThisFileInfo['ogg']['comments'][$index]);
 484+ break;
 485+
 486+ case 'rg_radio':
 487+ case 'replaygain_track_gain':
 488+ $ThisFileInfo['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
 489+ unset($ThisFileInfo['ogg']['comments'][$index]);
 490+ break;
 491+
 492+ case 'replaygain_album_peak':
 493+ $ThisFileInfo['replay_gain']['album']['peak'] = (double) $commentvalue[0];
 494+ unset($ThisFileInfo['ogg']['comments'][$index]);
 495+ break;
 496+
 497+ case 'rg_peak':
 498+ case 'replaygain_track_peak':
 499+ $ThisFileInfo['replay_gain']['track']['peak'] = (double) $commentvalue[0];
 500+ unset($ThisFileInfo['ogg']['comments'][$index]);
 501+ break;
 502+
 503+
 504+ default:
 505+ // do nothing
 506+ break;
 507+ }
 508+ }
 509+ }
 510+
 511+ fseek($fd, $OriginalOffset, SEEK_SET);
 512+
 513+ return true;
 514+ }
 515+
 516+ static function SpeexBandModeLookup($mode) {
 517+ static $SpeexBandModeLookup = array();
 518+ if (empty($SpeexBandModeLookup)) {
 519+ $SpeexBandModeLookup[0] = 'narrow';
 520+ $SpeexBandModeLookup[1] = 'wide';
 521+ $SpeexBandModeLookup[2] = 'ultra-wide';
 522+ }
 523+ return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
 524+ }
 525+
 526+
 527+ static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
 528+ for ($i = 0; $i < $SegmentNumber; $i++) {
 529+ $segmentlength = 0;
 530+ foreach ($OggInfoArray['segment_table'] as $key => $value) {
 531+ $segmentlength += $value;
 532+ if ($value < 255) {
 533+ break;
 534+ }
 535+ }
 536+ }
 537+ return $segmentlength;
 538+ }
 539+
 540+
 541+ static function get_quality_from_nominal_bitrate($nominal_bitrate) {
 542+
 543+ // decrease precision
 544+ $nominal_bitrate = $nominal_bitrate / 1000;
 545+
 546+ if ($nominal_bitrate < 128) {
 547+ // q-1 to q4
 548+ $qval = ($nominal_bitrate - 64) / 16;
 549+ } elseif ($nominal_bitrate < 256) {
 550+ // q4 to q8
 551+ $qval = $nominal_bitrate / 32;
 552+ } elseif ($nominal_bitrate < 320) {
 553+ // q8 to q9
 554+ $qval = ($nominal_bitrate + 256) / 64;
 555+ } else {
 556+ // q9 to q10
 557+ $qval = ($nominal_bitrate + 1300) / 180;
 558+ }
 559+ //return $qval; // 5.031324
 560+ //return intval($qval); // 5
 561+ return round($qval, 1); // 5 or 4.9
 562+ }
 563+
 564+}
 565+
 566+?>
\ No newline at end of file
Index: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/getid3/getid3.lib.php
@@ -0,0 +1,1340 @@
 2+<?php
 3+/////////////////////////////////////////////////////////////////
 4+/// getID3() by James Heinrich <info@getid3.org> //
 5+// available at http://getid3.sourceforge.net //
 6+// or http://www.getid3.org //
 7+/////////////////////////////////////////////////////////////////
 8+// //
 9+// getid3.lib.php - part of getID3() //
 10+// See readme.txt for more details //
 11+// ///
 12+/////////////////////////////////////////////////////////////////
 13+
 14+
 15+class getid3_lib
 16+{
 17+
 18+ static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlsafe=true) {
 19+ $returnstring = '';
 20+ for ($i = 0; $i < strlen($string); $i++) {
 21+ if ($hex) {
 22+ $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
 23+ } else {
 24+ $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '€');
 25+ }
 26+ if ($spaces) {
 27+ $returnstring .= ' ';
 28+ }
 29+ }
 30+ if ($htmlsafe) {
 31+ $returnstring = htmlentities($returnstring);
 32+ }
 33+ return $returnstring;
 34+ }
 35+
 36+ static function trunc($floatnumber) {
 37+ // truncates a floating-point number at the decimal point
 38+ // returns int (if possible, otherwise float)
 39+ if ($floatnumber >= 1) {
 40+ $truncatednumber = floor($floatnumber);
 41+ } elseif ($floatnumber <= -1) {
 42+ $truncatednumber = ceil($floatnumber);
 43+ } else {
 44+ $truncatednumber = 0;
 45+ }
 46+ if ($truncatednumber <= 1073741824) { // 2^30
 47+ $truncatednumber = (int) $truncatednumber;
 48+ }
 49+ return $truncatednumber;
 50+ }
 51+
 52+
 53+ static function safe_inc(&$variable, $increment=1) {
 54+ if (isset($variable)) {
 55+ $variable += $increment;
 56+ } else {
 57+ $variable = $increment;
 58+ }
 59+ return true;
 60+ }
 61+
 62+ static function CastAsInt($floatnum) {
 63+ // convert to float if not already
 64+ $floatnum = (float) $floatnum;
 65+
 66+ // convert a float to type int, only if possible
 67+ if (getid3_lib::trunc($floatnum) == $floatnum) {
 68+ // it's not floating point
 69+ if ($floatnum <= 2147483647) { // 2^31
 70+ // it's within int range
 71+ $floatnum = (int) $floatnum;
 72+ }
 73+ }
 74+ return $floatnum;
 75+ }
 76+
 77+
 78+ static function DecimalizeFraction($fraction) {
 79+ list($numerator, $denominator) = explode('/', $fraction);
 80+ return $numerator / ($denominator ? $denominator : 1);
 81+ }
 82+
 83+
 84+ static function DecimalBinary2Float($binarynumerator) {
 85+ $numerator = getid3_lib::Bin2Dec($binarynumerator);
 86+ $denominator = getid3_lib::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
 87+ return ($numerator / $denominator);
 88+ }
 89+
 90+
 91+ static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
 92+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
 93+ if (strpos($binarypointnumber, '.') === false) {
 94+ $binarypointnumber = '0.'.$binarypointnumber;
 95+ } elseif ($binarypointnumber{0} == '.') {
 96+ $binarypointnumber = '0'.$binarypointnumber;
 97+ }
 98+ $exponent = 0;
 99+ while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
 100+ if (substr($binarypointnumber, 1, 1) == '.') {
 101+ $exponent--;
 102+ $binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
 103+ } else {
 104+ $pointpos = strpos($binarypointnumber, '.');
 105+ $exponent += ($pointpos - 1);
 106+ $binarypointnumber = str_replace('.', '', $binarypointnumber);
 107+ $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
 108+ }
 109+ }
 110+ $binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
 111+ return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
 112+ }
 113+
 114+
 115+ static function Float2BinaryDecimal($floatvalue) {
 116+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
 117+ $maxbits = 128; // to how many bits of precision should the calculations be taken?
 118+ $intpart = getid3_lib::trunc($floatvalue);
 119+ $floatpart = abs($floatvalue - $intpart);
 120+ $pointbitstring = '';
 121+ while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
 122+ $floatpart *= 2;
 123+ $pointbitstring .= (string) getid3_lib::trunc($floatpart);
 124+ $floatpart -= getid3_lib::trunc($floatpart);
 125+ }
 126+ $binarypointnumber = decbin($intpart).'.'.$pointbitstring;
 127+ return $binarypointnumber;
 128+ }
 129+
 130+
 131+ static function Float2String($floatvalue, $bits) {
 132+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
 133+ switch ($bits) {
 134+ case 32:
 135+ $exponentbits = 8;
 136+ $fractionbits = 23;
 137+ break;
 138+
 139+ case 64:
 140+ $exponentbits = 11;
 141+ $fractionbits = 52;
 142+ break;
 143+
 144+ default:
 145+ return false;
 146+ break;
 147+ }
 148+ if ($floatvalue >= 0) {
 149+ $signbit = '0';
 150+ } else {
 151+ $signbit = '1';
 152+ }
 153+ $normalizedbinary = getid3_lib::NormalizeBinaryPoint(getid3_lib::Float2BinaryDecimal($floatvalue), $fractionbits);
 154+ $biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
 155+ $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
 156+ $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
 157+
 158+ return getid3_lib::BigEndian2String(getid3_lib::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
 159+ }
 160+
 161+
 162+ static function LittleEndian2Float($byteword) {
 163+ return getid3_lib::BigEndian2Float(strrev($byteword));
 164+ }
 165+
 166+
 167+ static function BigEndian2Float($byteword) {
 168+ // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
 169+ // http://www.psc.edu/general/software/packages/ieee/ieee.html
 170+ // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
 171+
 172+ $bitword = getid3_lib::BigEndian2Bin($byteword);
 173+ if (!$bitword) {
 174+ return 0;
 175+ }
 176+ $signbit = $bitword{0};
 177+
 178+ switch (strlen($byteword) * 8) {
 179+ case 32:
 180+ $exponentbits = 8;
 181+ $fractionbits = 23;
 182+ break;
 183+
 184+ case 64:
 185+ $exponentbits = 11;
 186+ $fractionbits = 52;
 187+ break;
 188+
 189+ case 80:
 190+ // 80-bit Apple SANE format
 191+ // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
 192+ $exponentstring = substr($bitword, 1, 15);
 193+ $isnormalized = intval($bitword{16});
 194+ $fractionstring = substr($bitword, 17, 63);
 195+ $exponent = pow(2, getid3_lib::Bin2Dec($exponentstring) - 16383);
 196+ $fraction = $isnormalized + getid3_lib::DecimalBinary2Float($fractionstring);
 197+ $floatvalue = $exponent * $fraction;
 198+ if ($signbit == '1') {
 199+ $floatvalue *= -1;
 200+ }
 201+ return $floatvalue;
 202+ break;
 203+
 204+ default:
 205+ return false;
 206+ break;
 207+ }
 208+ $exponentstring = substr($bitword, 1, $exponentbits);
 209+ $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
 210+ $exponent = getid3_lib::Bin2Dec($exponentstring);
 211+ $fraction = getid3_lib::Bin2Dec($fractionstring);
 212+
 213+ if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
 214+ // Not a Number
 215+ $floatvalue = false;
 216+ } elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
 217+ if ($signbit == '1') {
 218+ $floatvalue = '-infinity';
 219+ } else {
 220+ $floatvalue = '+infinity';
 221+ }
 222+ } elseif (($exponent == 0) && ($fraction == 0)) {
 223+ if ($signbit == '1') {
 224+ $floatvalue = -0;
 225+ } else {
 226+ $floatvalue = 0;
 227+ }
 228+ $floatvalue = ($signbit ? 0 : -0);
 229+ } elseif (($exponent == 0) && ($fraction != 0)) {
 230+ // These are 'unnormalized' values
 231+ $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fractionstring);
 232+ if ($signbit == '1') {
 233+ $floatvalue *= -1;
 234+ }
 235+ } elseif ($exponent != 0) {
 236+ $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fractionstring));
 237+ if ($signbit == '1') {
 238+ $floatvalue *= -1;
 239+ }
 240+ }
 241+ return (float) $floatvalue;
 242+ }
 243+
 244+
 245+ static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
 246+ $intvalue = 0;
 247+ $bytewordlen = strlen($byteword);
 248+ if ($bytewordlen == 0) {
 249+ return false;
 250+ }
 251+ for ($i = 0; $i < $bytewordlen; $i++) {
 252+ if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
 253+ $intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7);
 254+ } else {
 255+ $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
 256+ }
 257+ }
 258+ if ($signed && !$synchsafe) {
 259+ // synchsafe ints are not allowed to be signed
 260+ switch ($bytewordlen) {
 261+ case 1:
 262+ case 2:
 263+ case 3:
 264+ case 4:
 265+ $signMaskBit = 0x80 << (8 * ($bytewordlen - 1));
 266+ if ($intvalue & $signMaskBit) {
 267+ $intvalue = 0 - ($intvalue & ($signMaskBit - 1));
 268+ }
 269+ break;
 270+
 271+ default:
 272+ die('ERROR: Cannot have signed integers larger than 32-bits ('.strlen($byteword).') in getid3_lib::BigEndian2Int()');
 273+ break;
 274+ }
 275+ }
 276+ return getid3_lib::CastAsInt($intvalue);
 277+ }
 278+
 279+
 280+ static function LittleEndian2Int($byteword, $signed=false) {
 281+ return getid3_lib::BigEndian2Int(strrev($byteword), false, $signed);
 282+ }
 283+
 284+
 285+ static function BigEndian2Bin($byteword) {
 286+ $binvalue = '';
 287+ $bytewordlen = strlen($byteword);
 288+ for ($i = 0; $i < $bytewordlen; $i++) {
 289+ $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
 290+ }
 291+ return $binvalue;
 292+ }
 293+
 294+
 295+ static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
 296+ if ($number < 0) {
 297+ return false;
 298+ }
 299+ $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
 300+ $intstring = '';
 301+ if ($signed) {
 302+ if ($minbytes > 4) {
 303+ die('ERROR: Cannot have signed integers larger than 32-bits in getid3_lib::BigEndian2String()');
 304+ }
 305+ $number = $number & (0x80 << (8 * ($minbytes - 1)));
 306+ }
 307+ while ($number != 0) {
 308+ $quotient = ($number / ($maskbyte + 1));
 309+ $intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
 310+ $number = floor($quotient);
 311+ }
 312+ return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT);
 313+ }
 314+
 315+
 316+ static function Dec2Bin($number) {
 317+ while ($number >= 256) {
 318+ $bytes[] = (($number / 256) - (floor($number / 256))) * 256;
 319+ $number = floor($number / 256);
 320+ }
 321+ $bytes[] = $number;
 322+ $binstring = '';
 323+ for ($i = 0; $i < count($bytes); $i++) {
 324+ $binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring;
 325+ }
 326+ return $binstring;
 327+ }
 328+
 329+
 330+ static function Bin2Dec($binstring, $signed=false) {
 331+ $signmult = 1;
 332+ if ($signed) {
 333+ if ($binstring{0} == '1') {
 334+ $signmult = -1;
 335+ }
 336+ $binstring = substr($binstring, 1);
 337+ }
 338+ $decvalue = 0;
 339+ for ($i = 0; $i < strlen($binstring); $i++) {
 340+ $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
 341+ }
 342+ return getid3_lib::CastAsInt($decvalue * $signmult);
 343+ }
 344+
 345+
 346+ static function Bin2String($binstring) {
 347+ // return 'hi' for input of '0110100001101001'
 348+ $string = '';
 349+ $binstringreversed = strrev($binstring);
 350+ for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
 351+ $string = chr(getid3_lib::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
 352+ }
 353+ return $string;
 354+ }
 355+
 356+
 357+ static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
 358+ $intstring = '';
 359+ while ($number > 0) {
 360+ if ($synchsafe) {
 361+ $intstring = $intstring.chr($number & 127);
 362+ $number >>= 7;
 363+ } else {
 364+ $intstring = $intstring.chr($number & 255);
 365+ $number >>= 8;
 366+ }
 367+ }
 368+ return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
 369+ }
 370+
 371+
 372+ static function array_merge_clobber($array1, $array2) {
 373+ // written by kcØhireability*com
 374+ // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
 375+ if (!is_array($array1) || !is_array($array2)) {
 376+ return false;
 377+ }
 378+ $newarray = $array1;
 379+ foreach ($array2 as $key => $val) {
 380+ if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
 381+ $newarray[$key] = getid3_lib::array_merge_clobber($newarray[$key], $val);
 382+ } else {
 383+ $newarray[$key] = $val;
 384+ }
 385+ }
 386+ return $newarray;
 387+ }
 388+
 389+
 390+ static function array_merge_noclobber($array1, $array2) {
 391+ if (!is_array($array1) || !is_array($array2)) {
 392+ return false;
 393+ }
 394+ $newarray = $array1;
 395+ foreach ($array2 as $key => $val) {
 396+ if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
 397+ $newarray[$key] = getid3_lib::array_merge_noclobber($newarray[$key], $val);
 398+ } elseif (!isset($newarray[$key])) {
 399+ $newarray[$key] = $val;
 400+ }
 401+ }
 402+ return $newarray;
 403+ }
 404+
 405+
 406+ static function fileextension($filename, $numextensions=1) {
 407+ if (strstr($filename, '.')) {
 408+ $reversedfilename = strrev($filename);
 409+ $offset = 0;
 410+ for ($i = 0; $i < $numextensions; $i++) {
 411+ $offset = strpos($reversedfilename, '.', $offset + 1);
 412+ if ($offset === false) {
 413+ return '';
 414+ }
 415+ }
 416+ return strrev(substr($reversedfilename, 0, $offset));
 417+ }
 418+ return '';
 419+ }
 420+
 421+
 422+ static function PlaytimeString($playtimeseconds) {
 423+ $sign = (($playtimeseconds < 0) ? '-' : '');
 424+ $playtimeseconds = abs($playtimeseconds);
 425+ $contentseconds = round((($playtimeseconds / 60) - floor($playtimeseconds / 60)) * 60);
 426+ $contentminutes = floor($playtimeseconds / 60);
 427+ if ($contentseconds >= 60) {
 428+ $contentseconds -= 60;
 429+ $contentminutes++;
 430+ }
 431+ return $sign.intval($contentminutes).':'.str_pad($contentseconds, 2, 0, STR_PAD_LEFT);
 432+ }
 433+
 434+
 435+ static function image_type_to_mime_type($imagetypeid) {
 436+ // only available in PHP v4.3.0+
 437+ static $image_type_to_mime_type = array();
 438+ if (empty($image_type_to_mime_type)) {
 439+ $image_type_to_mime_type[1] = 'image/gif'; // GIF
 440+ $image_type_to_mime_type[2] = 'image/jpeg'; // JPEG
 441+ $image_type_to_mime_type[3] = 'image/png'; // PNG
 442+ $image_type_to_mime_type[4] = 'application/x-shockwave-flash'; // Flash
 443+ $image_type_to_mime_type[5] = 'image/psd'; // PSD
 444+ $image_type_to_mime_type[6] = 'image/bmp'; // BMP
 445+ $image_type_to_mime_type[7] = 'image/tiff'; // TIFF: little-endian (Intel)
 446+ $image_type_to_mime_type[8] = 'image/tiff'; // TIFF: big-endian (Motorola)
 447+ //$image_type_to_mime_type[9] = 'image/jpc'; // JPC
 448+ //$image_type_to_mime_type[10] = 'image/jp2'; // JPC
 449+ //$image_type_to_mime_type[11] = 'image/jpx'; // JPC
 450+ //$image_type_to_mime_type[12] = 'image/jb2'; // JPC
 451+ $image_type_to_mime_type[13] = 'application/x-shockwave-flash'; // Shockwave
 452+ $image_type_to_mime_type[14] = 'image/iff'; // IFF
 453+ }
 454+ return (isset($image_type_to_mime_type[$imagetypeid]) ? $image_type_to_mime_type[$imagetypeid] : 'application/octet-stream');
 455+ }
 456+
 457+
 458+ static function DateMac2Unix($macdate) {
 459+ // Macintosh timestamp: seconds since 00:00h January 1, 1904
 460+ // UNIX timestamp: seconds since 00:00h January 1, 1970
 461+ return getid3_lib::CastAsInt($macdate - 2082844800);
 462+ }
 463+
 464+
 465+ static function FixedPoint8_8($rawdata) {
 466+ return getid3_lib::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
 467+ }
 468+
 469+
 470+ static function FixedPoint16_16($rawdata) {
 471+ return getid3_lib::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
 472+ }
 473+
 474+
 475+ static function FixedPoint2_30($rawdata) {
 476+ $binarystring = getid3_lib::BigEndian2Bin($rawdata);
 477+ return getid3_lib::Bin2Dec(substr($binarystring, 0, 2)) + (float) (getid3_lib::Bin2Dec(substr($binarystring, 2, 30)) / 1073741824);
 478+ }
 479+
 480+
 481+ static function CreateDeepArray($ArrayPath, $Separator, $Value) {
 482+ // assigns $Value to a nested array path:
 483+ // $foo = getid3_lib::CreateDeepArray('/path/to/my', '/', 'file.txt')
 484+ // is the same as:
 485+ // $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
 486+ // or
 487+ // $foo['path']['to']['my'] = 'file.txt';
 488+ while ($ArrayPath && ($ArrayPath{0} == $Separator)) {
 489+ $ArrayPath = substr($ArrayPath, 1);
 490+ }
 491+ if (($pos = strpos($ArrayPath, $Separator)) !== false) {
 492+ $ReturnedArray[substr($ArrayPath, 0, $pos)] = getid3_lib::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
 493+ } else {
 494+ $ReturnedArray[$ArrayPath] = $Value;
 495+ }
 496+ return $ReturnedArray;
 497+ }
 498+
 499+ static function array_max($arraydata, $returnkey=false) {
 500+ $maxvalue = false;
 501+ $maxkey = false;
 502+ foreach ($arraydata as $key => $value) {
 503+ if (!is_array($value)) {
 504+ if ($value > $maxvalue) {
 505+ $maxvalue = $value;
 506+ $maxkey = $key;
 507+ }
 508+ }
 509+ }
 510+ return ($returnkey ? $maxkey : $maxvalue);
 511+ }
 512+
 513+ static function array_min($arraydata, $returnkey=false) {
 514+ $minvalue = false;
 515+ $minkey = false;
 516+ foreach ($arraydata as $key => $value) {
 517+ if (!is_array($value)) {
 518+ if ($value > $minvalue) {
 519+ $minvalue = $value;
 520+ $minkey = $key;
 521+ }
 522+ }
 523+ }
 524+ return ($returnkey ? $minkey : $minvalue);
 525+ }
 526+
 527+
 528+ static function md5_file($file) {
 529+
 530+ // md5_file() exists in PHP 4.2.0+.
 531+ if (function_exists('md5_file')) {
 532+ return md5_file($file);
 533+ }
 534+
 535+ if (GETID3_OS_ISWINDOWS) {
 536+
 537+ $RequiredFiles = array('cygwin1.dll', 'md5sum.exe');
 538+ foreach ($RequiredFiles as $required_file) {
 539+ if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
 540+ die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::md5_file() to function under Windows in PHP < v4.2.0');
 541+ }
 542+ }
 543+ $commandline = GETID3_HELPERAPPSDIR.'md5sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"';
 544+ if (preg_match("#^[\\]?([0-9a-f]{32})#", strtolower(`$commandline`), $r)) {
 545+ return $r[1];
 546+ }
 547+
 548+ } else {
 549+
 550+ // The following works under UNIX only
 551+ $file = str_replace('`', '\\`', $file);
 552+ if (preg_match("#^([0-9a-f]{32})[ \t\n\r]#i", `md5sum "$file"`, $r)) {
 553+ return $r[1];
 554+ }
 555+
 556+ }
 557+ return false;
 558+ }
 559+
 560+
 561+ static function sha1_file($file) {
 562+
 563+ // sha1_file() exists in PHP 4.3.0+.
 564+ if (function_exists('sha1_file')) {
 565+ return sha1_file($file);
 566+ }
 567+
 568+ $file = str_replace('`', '\\`', $file);
 569+
 570+ if (GETID3_OS_ISWINDOWS) {
 571+
 572+ $RequiredFiles = array('cygwin1.dll', 'sha1sum.exe');
 573+ foreach ($RequiredFiles as $required_file) {
 574+ if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
 575+ die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::sha1_file() to function under Windows in PHP < v4.3.0');
 576+ }
 577+ }
 578+ $commandline = GETID3_HELPERAPPSDIR.'sha1sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"';
 579+ if (preg_match("#^sha1=([0-9a-f]{40})#", strtolower(`$commandline`), $r)) {
 580+ return $r[1];
 581+ }
 582+
 583+ } else {
 584+
 585+ $commandline = 'sha1sum '.escapeshellarg($file).'';
 586+ if (preg_match("#^([0-9a-f]{40})[ \t\n\r]#", strtolower(`$commandline`), $r)) {
 587+ return $r[1];
 588+ }
 589+
 590+ }
 591+
 592+ return false;
 593+ }
 594+
 595+
 596+ // Allan Hansen <ahØartemis*dk>
 597+ // getid3_lib::md5_data() - returns md5sum for a file from startuing position to absolute end position
 598+ static function hash_data($file, $offset, $end, $algorithm) {
 599+ if ($end >= pow(2, 31)) {
 600+ return false;
 601+ }
 602+
 603+ switch ($algorithm) {
 604+ case 'md5':
 605+ $hash_function = 'md5_file';
 606+ $unix_call = 'md5sum';
 607+ $windows_call = 'md5sum.exe';
 608+ $hash_length = 32;
 609+ break;
 610+
 611+ case 'sha1':
 612+ $hash_function = 'sha1_file';
 613+ $unix_call = 'sha1sum';
 614+ $windows_call = 'sha1sum.exe';
 615+ $hash_length = 40;
 616+ break;
 617+
 618+ default:
 619+ die('Invalid algorithm ('.$algorithm.') in getid3_lib::hash_data()');
 620+ break;
 621+ }
 622+ $size = $end - $offset;
 623+ while (true) {
 624+ if (GETID3_OS_ISWINDOWS) {
 625+
 626+ // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
 627+ // Fall back to create-temp-file method:
 628+ if ($algorithm == 'sha1') {
 629+ break;
 630+ }
 631+
 632+ $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
 633+ foreach ($RequiredFiles as $required_file) {
 634+ if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
 635+ // helper apps not available - fall back to old method
 636+ break;
 637+ }
 638+ }
 639+ $commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' "'.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).'" | ';
 640+ $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
 641+ $commandline .= GETID3_HELPERAPPSDIR.$windows_call;
 642+
 643+ } else {
 644+
 645+ $commandline = 'head -c'.$end.' '.escapeshellarg($file).' | ';
 646+ $commandline .= 'tail -c'.$size.' | ';
 647+ $commandline .= $unix_call;
 648+
 649+ }
 650+ if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
 651+ $ThisFileInfo['warning'][] = 'PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm';
 652+ break;
 653+ }
 654+ return substr(`$commandline`, 0, $hash_length);
 655+ }
 656+
 657+ // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
 658+ if (($data_filename = tempnam(GETID3_TEMP_DIR, 'getID3')) === false) {
 659+ // can't find anywhere to create a temp file, just die
 660+ return false;
 661+ }
 662+
 663+ // Init
 664+ $result = false;
 665+
 666+ // copy parts of file
 667+ ob_start();
 668+ if ($fp = fopen($file, 'rb')) {
 669+ ob_end_clean();
 670+ ob_start();
 671+ if ($fp_data = fopen($data_filename, 'wb')) {
 672+ fseek($fp, $offset, SEEK_SET);
 673+ $byteslefttowrite = $end - $offset;
 674+ while (($byteslefttowrite > 0) && ($buffer = fread($fp, GETID3_FREAD_BUFFER_SIZE))) {
 675+ $byteswritten = fwrite($fp_data, $buffer, $byteslefttowrite);
 676+ $byteslefttowrite -= $byteswritten;
 677+ }
 678+ fclose($fp_data);
 679+ $result = getid3_lib::$hash_function($data_filename);
 680+ } else {
 681+ $errormessage = ob_get_contents();
 682+ ob_end_clean();
 683+ }
 684+ fclose($fp);
 685+ } else {
 686+ $errormessage = ob_get_contents();
 687+ ob_end_clean();
 688+ }
 689+ unlink($data_filename);
 690+ return $result;
 691+ }
 692+
 693+
 694+ static function iconv_fallback_int_utf8($charval) {
 695+ if ($charval < 128) {
 696+ // 0bbbbbbb
 697+ $newcharstring = chr($charval);
 698+ } elseif ($charval < 2048) {
 699+ // 110bbbbb 10bbbbbb
 700+ $newcharstring = chr(($charval >> 6) | 0xC0);
 701+ $newcharstring .= chr(($charval & 0x3F) | 0x80);
 702+ } elseif ($charval < 65536) {
 703+ // 1110bbbb 10bbbbbb 10bbbbbb
 704+ $newcharstring = chr(($charval >> 12) | 0xE0);
 705+ $newcharstring .= chr(($charval >> 6) | 0xC0);
 706+ $newcharstring .= chr(($charval & 0x3F) | 0x80);
 707+ } else {
 708+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
 709+ $newcharstring = chr(($charval >> 18) | 0xF0);
 710+ $newcharstring .= chr(($charval >> 12) | 0xC0);
 711+ $newcharstring .= chr(($charval >> 6) | 0xC0);
 712+ $newcharstring .= chr(($charval & 0x3F) | 0x80);
 713+ }
 714+ return $newcharstring;
 715+ }
 716+
 717+ // ISO-8859-1 => UTF-8
 718+ static function iconv_fallback_iso88591_utf8($string, $bom=false) {
 719+ if (function_exists('utf8_encode')) {
 720+ return utf8_encode($string);
 721+ }
 722+ // utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
 723+ $newcharstring = '';
 724+ if ($bom) {
 725+ $newcharstring .= "\xEF\xBB\xBF";
 726+ }
 727+ for ($i = 0; $i < strlen($string); $i++) {
 728+ $charval = ord($string{$i});
 729+ $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
 730+ }
 731+ return $newcharstring;
 732+ }
 733+
 734+ // ISO-8859-1 => UTF-16BE
 735+ static function iconv_fallback_iso88591_utf16be($string, $bom=false) {
 736+ $newcharstring = '';
 737+ if ($bom) {
 738+ $newcharstring .= "\xFE\xFF";
 739+ }
 740+ for ($i = 0; $i < strlen($string); $i++) {
 741+ $newcharstring .= "\x00".$string{$i};
 742+ }
 743+ return $newcharstring;
 744+ }
 745+
 746+ // ISO-8859-1 => UTF-16LE
 747+ static function iconv_fallback_iso88591_utf16le($string, $bom=false) {
 748+ $newcharstring = '';
 749+ if ($bom) {
 750+ $newcharstring .= "\xFF\xFE";
 751+ }
 752+ for ($i = 0; $i < strlen($string); $i++) {
 753+ $newcharstring .= $string{$i}."\x00";
 754+ }
 755+ return $newcharstring;
 756+ }
 757+
 758+ // ISO-8859-1 => UTF-16LE (BOM)
 759+ static function iconv_fallback_iso88591_utf16($string) {
 760+ return getid3_lib::iconv_fallback_iso88591_utf16le($string, true);
 761+ }
 762+
 763+ // UTF-8 => ISO-8859-1
 764+ static function iconv_fallback_utf8_iso88591($string) {
 765+ if (function_exists('utf8_decode')) {
 766+ return utf8_decode($string);
 767+ }
 768+ // utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
 769+ $newcharstring = '';
 770+ $offset = 0;
 771+ $stringlength = strlen($string);
 772+ while ($offset < $stringlength) {
 773+ if ((ord($string{$offset}) | 0x07) == 0xF7) {
 774+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
 775+ $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
 776+ ((ord($string{($offset + 1)}) & 0x3F) << 12) &
 777+ ((ord($string{($offset + 2)}) & 0x3F) << 6) &
 778+ (ord($string{($offset + 3)}) & 0x3F);
 779+ $offset += 4;
 780+ } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
 781+ // 1110bbbb 10bbbbbb 10bbbbbb
 782+ $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
 783+ ((ord($string{($offset + 1)}) & 0x3F) << 6) &
 784+ (ord($string{($offset + 2)}) & 0x3F);
 785+ $offset += 3;
 786+ } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
 787+ // 110bbbbb 10bbbbbb
 788+ $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
 789+ (ord($string{($offset + 1)}) & 0x3F);
 790+ $offset += 2;
 791+ } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
 792+ // 0bbbbbbb
 793+ $charval = ord($string{$offset});
 794+ $offset += 1;
 795+ } else {
 796+ // error? throw some kind of warning here?
 797+ $charval = false;
 798+ $offset += 1;
 799+ }
 800+ if ($charval !== false) {
 801+ $newcharstring .= (($charval < 256) ? chr($charval) : '?');
 802+ }
 803+ }
 804+ return $newcharstring;
 805+ }
 806+
 807+ // UTF-8 => UTF-16BE
 808+ static function iconv_fallback_utf8_utf16be($string, $bom=false) {
 809+ $newcharstring = '';
 810+ if ($bom) {
 811+ $newcharstring .= "\xFE\xFF";
 812+ }
 813+ $offset = 0;
 814+ $stringlength = strlen($string);
 815+ while ($offset < $stringlength) {
 816+ if ((ord($string{$offset}) | 0x07) == 0xF7) {
 817+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
 818+ $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
 819+ ((ord($string{($offset + 1)}) & 0x3F) << 12) &
 820+ ((ord($string{($offset + 2)}) & 0x3F) << 6) &
 821+ (ord($string{($offset + 3)}) & 0x3F);
 822+ $offset += 4;
 823+ } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
 824+ // 1110bbbb 10bbbbbb 10bbbbbb
 825+ $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
 826+ ((ord($string{($offset + 1)}) & 0x3F) << 6) &
 827+ (ord($string{($offset + 2)}) & 0x3F);
 828+ $offset += 3;
 829+ } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
 830+ // 110bbbbb 10bbbbbb
 831+ $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
 832+ (ord($string{($offset + 1)}) & 0x3F);
 833+ $offset += 2;
 834+ } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
 835+ // 0bbbbbbb
 836+ $charval = ord($string{$offset});
 837+ $offset += 1;
 838+ } else {
 839+ // error? throw some kind of warning here?
 840+ $charval = false;
 841+ $offset += 1;
 842+ }
 843+ if ($charval !== false) {
 844+ $newcharstring .= (($charval < 65536) ? getid3_lib::BigEndian2String($charval, 2) : "\x00".'?');
 845+ }
 846+ }
 847+ return $newcharstring;
 848+ }
 849+
 850+ // UTF-8 => UTF-16LE
 851+ static function iconv_fallback_utf8_utf16le($string, $bom=false) {
 852+ $newcharstring = '';
 853+ if ($bom) {
 854+ $newcharstring .= "\xFF\xFE";
 855+ }
 856+ $offset = 0;
 857+ $stringlength = strlen($string);
 858+ while ($offset < $stringlength) {
 859+ if ((ord($string{$offset}) | 0x07) == 0xF7) {
 860+ // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
 861+ $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
 862+ ((ord($string{($offset + 1)}) & 0x3F) << 12) &
 863+ ((ord($string{($offset + 2)}) & 0x3F) << 6) &
 864+ (ord($string{($offset + 3)}) & 0x3F);
 865+ $offset += 4;
 866+ } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
 867+ // 1110bbbb 10bbbbbb 10bbbbbb
 868+ $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
 869+ ((ord($string{($offset + 1)}) & 0x3F) << 6) &
 870+ (ord($string{($offset + 2)}) & 0x3F);
 871+ $offset += 3;
 872+ } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
 873+ // 110bbbbb 10bbbbbb
 874+ $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
 875+ (ord($string{($offset + 1)}) & 0x3F);
 876+ $offset += 2;
 877+ } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
 878+ // 0bbbbbbb
 879+ $charval = ord($string{$offset});
 880+ $offset += 1;
 881+ } else {
 882+ // error? maybe throw some warning here?
 883+ $charval = false;
 884+ $offset += 1;
 885+ }
 886+ if ($charval !== false) {
 887+ $newcharstring .= (($charval < 65536) ? getid3_lib::LittleEndian2String($charval, 2) : '?'."\x00");
 888+ }
 889+ }
 890+ return $newcharstring;
 891+ }
 892+
 893+ // UTF-8 => UTF-16LE (BOM)
 894+ static function iconv_fallback_utf8_utf16($string) {
 895+ return getid3_lib::iconv_fallback_utf8_utf16le($string, true);
 896+ }
 897+
 898+ // UTF-16BE => UTF-8
 899+ static function iconv_fallback_utf16be_utf8($string) {
 900+ if (substr($string, 0, 2) == "\xFE\xFF") {
 901+ // strip BOM
 902+ $string = substr($string, 2);
 903+ }
 904+ $newcharstring = '';
 905+ for ($i = 0; $i < strlen($string); $i += 2) {
 906+ $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
 907+ $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
 908+ }
 909+ return $newcharstring;
 910+ }
 911+
 912+ // UTF-16LE => UTF-8
 913+ static function iconv_fallback_utf16le_utf8($string) {
 914+ if (substr($string, 0, 2) == "\xFF\xFE") {
 915+ // strip BOM
 916+ $string = substr($string, 2);
 917+ }
 918+ $newcharstring = '';
 919+ for ($i = 0; $i < strlen($string); $i += 2) {
 920+ $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
 921+ $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
 922+ }
 923+ return $newcharstring;
 924+ }
 925+
 926+ // UTF-16BE => ISO-8859-1
 927+ static function iconv_fallback_utf16be_iso88591($string) {
 928+ if (substr($string, 0, 2) == "\xFE\xFF") {
 929+ // strip BOM
 930+ $string = substr($string, 2);
 931+ }
 932+ $newcharstring = '';
 933+ for ($i = 0; $i < strlen($string); $i += 2) {
 934+ $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
 935+ $newcharstring .= (($charval < 256) ? chr($charval) : '?');
 936+ }
 937+ return $newcharstring;
 938+ }
 939+
 940+ // UTF-16LE => ISO-8859-1
 941+ static function iconv_fallback_utf16le_iso88591($string) {
 942+ if (substr($string, 0, 2) == "\xFF\xFE") {
 943+ // strip BOM
 944+ $string = substr($string, 2);
 945+ }
 946+ $newcharstring = '';
 947+ for ($i = 0; $i < strlen($string); $i += 2) {
 948+ $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
 949+ $newcharstring .= (($charval < 256) ? chr($charval) : '?');
 950+ }
 951+ return $newcharstring;
 952+ }
 953+
 954+ // UTF-16 (BOM) => ISO-8859-1
 955+ static function iconv_fallback_utf16_iso88591($string) {
 956+ $bom = substr($string, 0, 2);
 957+ if ($bom == "\xFE\xFF") {
 958+ return getid3_lib::iconv_fallback_utf16be_iso88591(substr($string, 2));
 959+ } elseif ($bom == "\xFF\xFE") {
 960+ return getid3_lib::iconv_fallback_utf16le_iso88591(substr($string, 2));
 961+ }
 962+ return $string;
 963+ }
 964+
 965+ // UTF-16 (BOM) => UTF-8
 966+ static function iconv_fallback_utf16_utf8($string) {
 967+ $bom = substr($string, 0, 2);
 968+ if ($bom == "\xFE\xFF") {
 969+ return getid3_lib::iconv_fallback_utf16be_utf8(substr($string, 2));
 970+ } elseif ($bom == "\xFF\xFE") {
 971+ return getid3_lib::iconv_fallback_utf16le_utf8(substr($string, 2));
 972+ }
 973+ return $string;
 974+ }
 975+
 976+ static function iconv_fallback($in_charset, $out_charset, $string) {
 977+
 978+ if ($in_charset == $out_charset) {
 979+ return $string;
 980+ }
 981+
 982+ // iconv() availble
 983+ if (function_exists('iconv')) {
 984+
 985+ ob_start();
 986+ if ($converted_string = iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
 987+ ob_end_clean();
 988+ switch ($out_charset) {
 989+ case 'ISO-8859-1':
 990+ $converted_string = rtrim($converted_string, "\x00");
 991+ break;
 992+ }
 993+ return $converted_string;
 994+ } else {
 995+ $errormessage = ob_get_contents();
 996+ ob_end_clean();
 997+ }
 998+
 999+ // iconv() may sometimes fail with "illegal character in input string" error message
 1000+ // and return an empty string, but returning the unconverted string is more useful
 1001+ return $string;
 1002+ }
 1003+
 1004+
 1005+ // iconv() not available
 1006+ static $ConversionFunctionList = array();
 1007+ if (empty($ConversionFunctionList)) {
 1008+ $ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8';
 1009+ $ConversionFunctionList['ISO-8859-1']['UTF-16'] = 'iconv_fallback_iso88591_utf16';
 1010+ $ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be';
 1011+ $ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le';
 1012+ $ConversionFunctionList['UTF-8']['ISO-8859-1'] = 'iconv_fallback_utf8_iso88591';
 1013+ $ConversionFunctionList['UTF-8']['UTF-16'] = 'iconv_fallback_utf8_utf16';
 1014+ $ConversionFunctionList['UTF-8']['UTF-16BE'] = 'iconv_fallback_utf8_utf16be';
 1015+ $ConversionFunctionList['UTF-8']['UTF-16LE'] = 'iconv_fallback_utf8_utf16le';
 1016+ $ConversionFunctionList['UTF-16']['ISO-8859-1'] = 'iconv_fallback_utf16_iso88591';
 1017+ $ConversionFunctionList['UTF-16']['UTF-8'] = 'iconv_fallback_utf16_utf8';
 1018+ $ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591';
 1019+ $ConversionFunctionList['UTF-16LE']['UTF-8'] = 'iconv_fallback_utf16le_utf8';
 1020+ $ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591';
 1021+ $ConversionFunctionList['UTF-16BE']['UTF-8'] = 'iconv_fallback_utf16be_utf8';
 1022+ }
 1023+ if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) {
 1024+ $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
 1025+ return getid3_lib::$ConversionFunction($string);
 1026+ }
 1027+ die('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
 1028+ }
 1029+
 1030+
 1031+ static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
 1032+ $HTMLstring = '';
 1033+
 1034+ switch ($charset) {
 1035+ case 'ISO-8859-1':
 1036+ case 'ISO8859-1':
 1037+ case 'ISO-8859-15':
 1038+ case 'ISO8859-15':
 1039+ case 'cp866':
 1040+ case 'ibm866':
 1041+ case '866':
 1042+ case 'cp1251':
 1043+ case 'Windows-1251':
 1044+ case 'win-1251':
 1045+ case '1251':
 1046+ case 'cp1252':
 1047+ case 'Windows-1252':
 1048+ case '1252':
 1049+ case 'KOI8-R':
 1050+ case 'koi8-ru':
 1051+ case 'koi8r':
 1052+ case 'BIG5':
 1053+ case '950':
 1054+ case 'GB2312':
 1055+ case '936':
 1056+ case 'BIG5-HKSCS':
 1057+ case 'Shift_JIS':
 1058+ case 'SJIS':
 1059+ case '932':
 1060+ case 'EUC-JP':
 1061+ case 'EUCJP':
 1062+ $HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
 1063+ break;
 1064+
 1065+ case 'UTF-8':
 1066+ $strlen = strlen($string);
 1067+ for ($i = 0; $i < $strlen; $i++) {
 1068+ $char_ord_val = ord($string{$i});
 1069+ $charval = 0;
 1070+ if ($char_ord_val < 0x80) {
 1071+ $charval = $char_ord_val;
 1072+ } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F && $i+3 < $strlen) {
 1073+ $charval = (($char_ord_val & 0x07) << 18);
 1074+ $charval += ((ord($string{++$i}) & 0x3F) << 12);
 1075+ $charval += ((ord($string{++$i}) & 0x3F) << 6);
 1076+ $charval += (ord($string{++$i}) & 0x3F);
 1077+ } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07 && $i+2 < $strlen) {
 1078+ $charval = (($char_ord_val & 0x0F) << 12);
 1079+ $charval += ((ord($string{++$i}) & 0x3F) << 6);
 1080+ $charval += (ord($string{++$i}) & 0x3F);
 1081+ } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03 && $i+1 < $strlen) {
 1082+ $charval = (($char_ord_val & 0x1F) << 6);
 1083+ $charval += (ord($string{++$i}) & 0x3F);
 1084+ }
 1085+ if (($charval >= 32) && ($charval <= 127)) {
 1086+ $HTMLstring .= htmlentities(chr($charval));
 1087+ } else {
 1088+ $HTMLstring .= '&#'.$charval.';';
 1089+ }
 1090+ }
 1091+ break;
 1092+
 1093+ case 'UTF-16LE':
 1094+ for ($i = 0; $i < strlen($string); $i += 2) {
 1095+ $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
 1096+ if (($charval >= 32) && ($charval <= 127)) {
 1097+ $HTMLstring .= chr($charval);
 1098+ } else {
 1099+ $HTMLstring .= '&#'.$charval.';';
 1100+ }
 1101+ }
 1102+ break;
 1103+
 1104+ case 'UTF-16BE':
 1105+ for ($i = 0; $i < strlen($string); $i += 2) {
 1106+ $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
 1107+ if (($charval >= 32) && ($charval <= 127)) {
 1108+ $HTMLstring .= chr($charval);
 1109+ } else {
 1110+ $HTMLstring .= '&#'.$charval.';';
 1111+ }
 1112+ }
 1113+ break;
 1114+
 1115+ default:
 1116+ $HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()';
 1117+ break;
 1118+ }
 1119+ return $HTMLstring;
 1120+ }
 1121+
 1122+
 1123+
 1124+ static function RGADnameLookup($namecode) {
 1125+ static $RGADname = array();
 1126+ if (empty($RGADname)) {
 1127+ $RGADname[0] = 'not set';
 1128+ $RGADname[1] = 'Track Gain Adjustment';
 1129+ $RGADname[2] = 'Album Gain Adjustment';
 1130+ }
 1131+
 1132+ return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : '');
 1133+ }
 1134+
 1135+
 1136+ static function RGADoriginatorLookup($originatorcode) {
 1137+ static $RGADoriginator = array();
 1138+ if (empty($RGADoriginator)) {
 1139+ $RGADoriginator[0] = 'unspecified';
 1140+ $RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer';
 1141+ $RGADoriginator[2] = 'set by user';
 1142+ $RGADoriginator[3] = 'determined automatically';
 1143+ }
 1144+
 1145+ return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : '');
 1146+ }
 1147+
 1148+
 1149+ static function RGADadjustmentLookup($rawadjustment, $signbit) {
 1150+ $adjustment = $rawadjustment / 10;
 1151+ if ($signbit == 1) {
 1152+ $adjustment *= -1;
 1153+ }
 1154+ return (float) $adjustment;
 1155+ }
 1156+
 1157+
 1158+ static function RGADgainString($namecode, $originatorcode, $replaygain) {
 1159+ if ($replaygain < 0) {
 1160+ $signbit = '1';
 1161+ } else {
 1162+ $signbit = '0';
 1163+ }
 1164+ $storedreplaygain = intval(round($replaygain * 10));
 1165+ $gainstring = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT);
 1166+ $gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT);
 1167+ $gainstring .= $signbit;
 1168+ $gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT);
 1169+
 1170+ return $gainstring;
 1171+ }
 1172+
 1173+ static function RGADamplitude2dB($amplitude) {
 1174+ return 20 * log10($amplitude);
 1175+ }
 1176+
 1177+
 1178+ static function GetDataImageSize($imgData, &$imageinfo) {
 1179+ $GetDataImageSize = false;
 1180+ if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) {
 1181+ ob_start();
 1182+ if ($tmp = fopen($tempfilename, 'wb')) {
 1183+ ob_end_clean();
 1184+ fwrite($tmp, $imgData);
 1185+ fclose($tmp);
 1186+ ob_start();
 1187+ $GetDataImageSize = GetImageSize($tempfilename, $imageinfo);
 1188+ $errormessage = ob_get_contents();
 1189+ ob_end_clean();
 1190+ } else {
 1191+ $errormessage = ob_get_contents();
 1192+ ob_end_clean();
 1193+ }
 1194+ unlink($tempfilename);
 1195+ }
 1196+ return $GetDataImageSize;
 1197+ }
 1198+
 1199+ static function ImageTypesLookup($imagetypeid) {
 1200+ static $ImageTypesLookup = array();
 1201+ if (empty($ImageTypesLookup)) {
 1202+ $ImageTypesLookup[1] = 'gif';
 1203+ $ImageTypesLookup[2] = 'jpeg';
 1204+ $ImageTypesLookup[3] = 'png';
 1205+ $ImageTypesLookup[4] = 'swf';
 1206+ $ImageTypesLookup[5] = 'psd';
 1207+ $ImageTypesLookup[6] = 'bmp';
 1208+ $ImageTypesLookup[7] = 'tiff (little-endian)';
 1209+ $ImageTypesLookup[8] = 'tiff (big-endian)';
 1210+ $ImageTypesLookup[9] = 'jpc';
 1211+ $ImageTypesLookup[10] = 'jp2';
 1212+ $ImageTypesLookup[11] = 'jpx';
 1213+ $ImageTypesLookup[12] = 'jb2';
 1214+ $ImageTypesLookup[13] = 'swc';
 1215+ $ImageTypesLookup[14] = 'iff';
 1216+ }
 1217+ return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : '');
 1218+ }
 1219+
 1220+ static function CopyTagsToComments(&$ThisFileInfo) {
 1221+
 1222+ // Copy all entries from ['tags'] into common ['comments']
 1223+ if (!empty($ThisFileInfo['tags'])) {
 1224+ foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
 1225+ foreach ($tagarray as $tagname => $tagdata) {
 1226+ foreach ($tagdata as $key => $value) {
 1227+ if (!empty($value)) {
 1228+ if (empty($ThisFileInfo['comments'][$tagname])) {
 1229+
 1230+ // fall through and append value
 1231+
 1232+ } elseif ($tagtype == 'id3v1') {
 1233+
 1234+ $newvaluelength = strlen(trim($value));
 1235+ foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
 1236+ $oldvaluelength = strlen(trim($existingvalue));
 1237+ if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) {
 1238+ // new value is identical but shorter-than (or equal-length to) one already in comments - skip
 1239+ break 2;
 1240+ }
 1241+ }
 1242+
 1243+ } else {
 1244+
 1245+ $newvaluelength = strlen(trim($value));
 1246+ foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
 1247+ $oldvaluelength = strlen(trim($existingvalue));
 1248+ if (($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
 1249+ $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
 1250+ break 2;
 1251+ }
 1252+ }
 1253+
 1254+ }
 1255+ if (empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
 1256+ $ThisFileInfo['comments'][$tagname][] = trim($value);
 1257+ }
 1258+ }
 1259+ }
 1260+ }
 1261+ }
 1262+
 1263+ // Copy to ['comments_html']
 1264+ foreach ($ThisFileInfo['comments'] as $field => $values) {
 1265+ foreach ($values as $index => $value) {
 1266+ $ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
 1267+ }
 1268+ }
 1269+ }
 1270+ }
 1271+
 1272+
 1273+ static function EmbeddedLookup($key, $begin, $end, $file, $name) {
 1274+
 1275+ // Cached
 1276+ static $cache;
 1277+ if (isset($cache[$file][$name])) {
 1278+ return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
 1279+ }
 1280+
 1281+ // Init
 1282+ $keylength = strlen($key);
 1283+ $line_count = $end - $begin - 7;
 1284+
 1285+ // Open php file
 1286+ $fp = fopen($file, 'r');
 1287+
 1288+ // Discard $begin lines
 1289+ for ($i = 0; $i < ($begin + 3); $i++) {
 1290+ fgets($fp, 1024);
 1291+ }
 1292+
 1293+ // Loop thru line
 1294+ while (0 < $line_count--) {
 1295+
 1296+ // Read line
 1297+ $line = ltrim(fgets($fp, 1024), "\t ");
 1298+
 1299+ // METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key
 1300+ //$keycheck = substr($line, 0, $keylength);
 1301+ //if ($key == $keycheck) {
 1302+ // $cache[$file][$name][$keycheck] = substr($line, $keylength + 1);
 1303+ // break;
 1304+ //}
 1305+
 1306+ // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key
 1307+ //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1));
 1308+ $explodedLine = explode("\t", $line, 2);
 1309+ $ThisKey = (isset($explodedLine[0]) ? $explodedLine[0] : '');
 1310+ $ThisValue = (isset($explodedLine[1]) ? $explodedLine[1] : '');
 1311+ $cache[$file][$name][$ThisKey] = trim($ThisValue);
 1312+ }
 1313+
 1314+ // Close and return
 1315+ fclose($fp);
 1316+ return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
 1317+ }
 1318+
 1319+ static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
 1320+ global $GETID3_ERRORARRAY;
 1321+
 1322+ if (file_exists($filename)) {
 1323+ if (include_once($filename)) {
 1324+ return true;
 1325+ } else {
 1326+ $diemessage = basename($sourcefile).' depends on '.$filename.', which has errors';
 1327+ }
 1328+ } else {
 1329+ $diemessage = basename($sourcefile).' depends on '.$filename.', which is missing';
 1330+ }
 1331+ if ($DieOnFailure) {
 1332+ die($diemessage);
 1333+ } else {
 1334+ $GETID3_ERRORARRAY[] = $diemessage;
 1335+ }
 1336+ return false;
 1337+ }
 1338+
 1339+}
 1340+
 1341+?>
\ No newline at end of file
Index: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/getid3/readme.txt
@@ -0,0 +1,574 @@
 2+/////////////////////////////////////////////////////////////////
 3+/// getID3() by James Heinrich <info@getid3.org> //
 4+// available at http://getid3.sourceforge.net //
 5+// or http://www.getid3.org //
 6+/////////////////////////////////////////////////////////////////
 7+// //
 8+// changelog.txt - part of getID3() //
 9+// See readme.txt for more details //
 10+// ///
 11+/////////////////////////////////////////////////////////////////
 12+
 13+ This code is released under the GNU GPL:
 14+ http://www.gnu.org/copyleft/gpl.html
 15+
 16+ +---------------------------------------------+
 17+ | If you do use this code somewhere, send me |
 18+ | an email and tell me how/where you used it. |
 19+ | |
 20+ | If you want to donate, there is a link on |
 21+ | http://www.getid3.org for PayPal donations. |
 22+ +---------------------------------------------+
 23+
 24+
 25+
 26+Quick Start
 27+===========================================================================
 28+
 29+Q: How can I check that getID3() works on my server/files?
 30+A: Unzip getID3() to a directory, then access /demos/demo.browse.php
 31+
 32+
 33+
 34+Support
 35+===========================================================================
 36+
 37+Q: I have a question, or I found a bug. What do I do?
 38+A: The preferred method of support requests and/or bug reports is the
 39+ forum at http://support.getid3.org/
 40+
 41+
 42+
 43+Sourceforge Notification
 44+===========================================================================
 45+
 46+It's highly recommended that you sign up for notification from
 47+Sourceforge for when new versions are released. Please visit:
 48+http://sourceforge.net/project/showfiles.php?group_id=55859
 49+and click the little "monitor package" icon/link. If you're
 50+previously signed up for the mailing list, be aware that it has
 51+been discontinued, only the automated Sourceforge notification
 52+will be used from now on.
 53+
 54+
 55+
 56+What does getID3() do?
 57+===========================================================================
 58+
 59+Reads & parses (to varying degrees):
 60+ � tags:
 61+ * APE (v1 and v2)
 62+ * ID3v1 (& ID3v1.1)
 63+ * ID3v2 (v2.4, v2.3, v2.2)
 64+ * Lyrics3 (v1 & v2)
 65+
 66+ � audio-lossy:
 67+ * MP3/MP2/MP1
 68+ * MPC / Musepack
 69+ * Ogg (Vorbis, OggFLAC, Speex)
 70+ * AC3
 71+ * DTS
 72+ * RealAudio
 73+ * Speex
 74+ * DSS
 75+ * VQF
 76+
 77+ � audio-lossless:
 78+ * AIFF
 79+ * AU
 80+ * Bonk
 81+ * CD-audio (*.cda)
 82+ * FLAC
 83+ * LA (Lossless Audio)
 84+ * LiteWave
 85+ * LPAC
 86+ * MIDI
 87+ * Monkey's Audio
 88+ * OptimFROG
 89+ * RKAU
 90+ * Shorten
 91+ * TTA
 92+ * VOC
 93+ * WAV (RIFF)
 94+ * WavPack
 95+
 96+ � audio-video:
 97+ * ASF: ASF, Windows Media Audio (WMA), Windows Media Video (WMV)
 98+ * AVI (RIFF)
 99+ * Flash
 100+ * Matroska (MKV)
 101+ * MPEG-1 / MPEG-2
 102+ * NSV (Nullsoft Streaming Video)
 103+ * Quicktime
 104+ * RealVideo
 105+
 106+ � still image:
 107+ * BMP
 108+ * GIF
 109+ * JPEG
 110+ * PNG
 111+ * TIFF
 112+ * SWF (Flash)
 113+ * PhotoCD
 114+
 115+ � data:
 116+ * ISO-9660 CD-ROM image (directory structure)
 117+ * SZIP (limited support)
 118+ * ZIP (directory structure)
 119+ * TAR
 120+ * CUE
 121+
 122+
 123+Writes:
 124+ * ID3v1 (& ID3v1.1)
 125+ * ID3v2 (v2.3 & v2.4)
 126+ * VorbisComment on OggVorbis
 127+ * VorbisComment on FLAC (not OggFLAC)
 128+ * APE v2
 129+ * Lyrics3 (delete only)
 130+
 131+
 132+
 133+Requirements
 134+===========================================================================
 135+
 136+* PHP 4.2.0 (or higher) for getID3() 1.7.x (and earlier)
 137+* PHP 5.3.0 (or higher) for getID3() 1.8.x (and up)
 138+* PHP 5.0.0 (or higher) for getID3() 2.0.x (and up)
 139+* at least 4MB memory for PHP. 8MB is highly recommended.
 140+ 12MB is required with all modules loaded.
 141+
 142+
 143+
 144+Usage
 145+===========================================================================
 146+
 147+See /demos/demo.basic.php for a very basic use of getID3() with no
 148+fancy output, just scanning one file.
 149+
 150+See structure.txt for the returned data structure.
 151+
 152+*> For an example of a complete directory-browsing, <*
 153+*> file-scanning implementation of getID3(), please run <*
 154+*> /demos/demo.browse.php <*
 155+
 156+See /demos/demo.mysql.php for a sample recursive scanning code that
 157+scans every file in a given directory, and all sub-directories, stores
 158+the results in a database and allows various analysis / maintenance
 159+operations
 160+
 161+To analyze remote files over HTTP or FTP you need to copy the file
 162+locally first before running getID3(). Your code would look something
 163+like this:
 164+
 165+// Copy remote file locally to scan with getID3()
 166+$remotefilename = 'http://www.example.com/filename.mp3';
 167+if ($fp_remote = fopen($remotefilename, 'rb')) {
 168+ $localtempfilename = tempnam('/tmp', 'getID3');
 169+ if ($fp_local = fopen($localtempfilename, 'wb')) {
 170+ while ($buffer = fread($fp_remote, 8192)) {
 171+ fwrite($fp_local, $buffer);
 172+ }
 173+ fclose($fp_local);
 174+
 175+ // Initialize getID3 engine
 176+ $getID3 = new getID3;
 177+
 178+ $ThisFileInfo = $getID3->analyze($filename);
 179+
 180+ // Delete temporary file
 181+ unlink($localtempfilename);
 182+ }
 183+ fclose($fp_remote);
 184+}
 185+
 186+
 187+See /demos/demo.write.php for how to write tags.
 188+
 189+
 190+
 191+What does the returned data structure look like?
 192+===========================================================================
 193+
 194+See structure.txt
 195+
 196+It is recommended that you look at the output of
 197+/demos/demo.browse.php scanning the file(s) you're interested in to
 198+confirm what data is actually returned for any particular filetype in
 199+general, and your files in particular, as the actual data returned
 200+may vary considerably depending on what information is available in
 201+the file itself.
 202+
 203+
 204+
 205+Notes
 206+===========================================================================
 207+
 208+getID3() 1.x:
 209+If the format parser encounters a critical problem, it will return
 210+something in $fileinfo['error'], describing the encountered error. If
 211+a less critical error or notice is generated it will appear in
 212+$fileinfo['warning']. Both keys may contain more than one warning or
 213+error. If something is returned in ['error'] then the file was not
 214+correctly parsed and returned data may or may not be correct and/or
 215+complete. If something is returned in ['warning'] (and not ['error'])
 216+then the data that is returned is OK - usually getID3() is reporting
 217+errors in the file that have been worked around due to known bugs in
 218+other programs. Some warnings may indicate that the data that is
 219+returned is OK but that some data could not be extracted due to
 220+errors in the file.
 221+
 222+getID3() 2.x:
 223+See above except errors are thrown (so you will only get one error).
 224+
 225+
 226+
 227+Disclaimer
 228+===========================================================================
 229+
 230+getID3() has been tested on many systems, on many types of files,
 231+under many operating systems, and is generally believe to be stable
 232+and safe. That being said, there is still the chance there is an
 233+undiscovered and/or unfixed bug that may potentially corrupt your
 234+file, especially within the writing functions. By using getID3() you
 235+agree that it's not my fault if any of your files are corrupted.
 236+In fact, I'm not liable for anything :)
 237+
 238+
 239+
 240+License
 241+===========================================================================
 242+
 243+GNU General Public License - see license.txt
 244+
 245+This program is free software; you can redistribute it and/or
 246+modify it under the terms of the GNU General Public License
 247+as published by the Free Software Foundation; either version 2
 248+of the License, or (at your option) any later version.
 249+
 250+This program is distributed in the hope that it will be useful,
 251+but WITHOUT ANY WARRANTY; without even the implied warranty of
 252+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 253+GNU General Public License for more details.
 254+
 255+You should have received a copy of the GNU General Public License
 256+along with this program; if not, write to:
 257+Free Software Foundation, Inc.
 258+59 Temple Place - Suite 330
 259+Boston, MA 02111-1307, USA.
 260+
 261+FAQ:
 262+Q: Can I use getID3() in my program? Do I need a commercial license?
 263+A: You're generally free to use getID3 however you see fit. The only
 264+ case in which you would require a commercial license is if you're
 265+ selling your closed-source program that integrates getID3. If you
 266+ sell your program including a copy of getID3, that's fine as long
 267+ as you include a copy of the sourcecode when you sell it. Or you
 268+ can distribute your code without getID3 and say "download it from
 269+ getid3.sourceforge.net"
 270+
 271+
 272+
 273+Future Plans
 274+===========================================================================
 275+
 276+* Writing support for Real
 277+* Better support for MP4 container format
 278+* Scan for appended ID3v2 tag at end of file per ID3v2.4 specs (Section 5.0)
 279+* Support for JPEG-2000 (http://www.morgan-multimedia.com/jpeg2000_overview.htm)
 280+* Support for MOD (mod/stm/s3m/it/xm/mtm/ult/669)
 281+* Support for ACE (thanks Vince)
 282+* Support for Ogg other than Vorbis, Speex and OggFlac (ie. Ogg+Xvid)
 283+* Ability to create Xing/LAME VBR header for VBR MP3s that are missing VBR header
 284+* Ability to "clean" ID3v2 padding (replace invalid padding with valid padding)
 285+* Warn if MP3s change version mid-stream (in full-scan mode)
 286+* check for corrupt/broken mid-file MP3 streams in histogram scan
 287+* Support for lossless-compression formats
 288+ (http://www.firstpr.com.au/audiocomp/lossless/#Links)
 289+ (http://compression.ca/act-sound.html)
 290+ (http://web.inter.nl.net/users/hvdh/lossless/lossless.htm)
 291+* Support for RIFF-INFO chunks
 292+ * http://lotto.st-andrews.ac.uk/~njh/tag_interchange.html
 293+ (thanks Nick Humfrey <njh�surgeradio*co*uk>)
 294+ * http://abcavi.narod.ru/sof/abcavi/infotags.htm
 295+ (thanks Kibi)
 296+* Better support for Bink video
 297+* http://www.hr/josip/DSP/AudioFile2.html
 298+* http://www.pcisys.net/~melanson/codecs/
 299+* Detect mp3PRO
 300+* Support for PSD
 301+* Support for JPC
 302+* Support for JP2
 303+* Support for JPX
 304+* Support for JB2
 305+* Support for IFF
 306+* Support for ICO
 307+* Support for ANI
 308+* Support for EXE (comments, author, etc) (thanks p*quaedackers�planet*nl)
 309+* Support for DVD-IFO (region, subtitles, aspect ratio, etc)
 310+ (thanks p*quaedackers�planet*nl)
 311+* More complete support for SWF - parsing encapsulated MP3 and/or JPEG content
 312+ (thanks n8n8�yahoo*com)
 313+* Support for a2b
 314+* Optional scan-through-frames for AVI verification
 315+ (thanks rockcohen�massive-interactive*nl)
 316+* Support for TTF (thanks info�butterflyx*com)
 317+* Support for DSS (http://www.getid3.org/phpBB3/viewtopic.php?t=171)
 318+* Support for SMAF (http://smaf-yamaha.com/what/demo.html)
 319+ http://www.getid3.org/phpBB3/viewtopic.php?t=182
 320+* Support for AMR (http://www.getid3.org/phpBB3/viewtopic.php?t=195)
 321+* Support for 3gpp (http://www.getid3.org/phpBB3/viewtopic.php?t=195)
 322+* Support for ID4 (http://www.wackysoft.cjb.net grizlyY2K�hotmail*com)
 323+* Parse XML data returned in Ogg comments
 324+* Parse XML data from Quicktime SMIL metafiles (klausrath�mac*com)
 325+* ID3v2 genre string creator function
 326+* More complete parsing of JPG
 327+* Support for all old-style ASF packets
 328+* ASF/WMA/WMV tag writing
 329+* Parse declared T??? ID3v2 text information frames, where appropriate
 330+ (thanks Christian Fritz for the idea)
 331+* Recognize encoder:
 332+ http://www.guerillasoft.com/EncSpot2/index.html
 333+ http://ff123.net/identify.html
 334+ http://www.hydrogenaudio.org/?act=ST&f=16&t=9414
 335+ http://www.hydrogenaudio.org/?showtopic=11785
 336+* Support for other OS/2 bitmap structures: Bitmap Array('BA'),
 337+ Color Icon('CI'), Color Pointer('CP'), Icon('IC'), Pointer ('PT')
 338+ http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
 339+* Support for WavPack RAW mode
 340+* ASF/WMA/WMV data packet parsing
 341+* ID3v2FrameFlagsLookupTagAlter()
 342+* ID3v2FrameFlagsLookupFileAlter()
 343+* obey ID3v2 tag alter/preserve/discard rules
 344+* http://www.geocities.com/SiliconValley/Sector/9654/Softdoc/Illyrium/Aolyr.htm
 345+* proper checking for LINK/LNK frame validity in ID3v2 writing
 346+* proper checking for ASPI-TLEN frame validity in ID3v2 writing
 347+* proper checking for COMR frame validity in ID3v2 writing
 348+* http://www.geocities.co.jp/SiliconValley-Oakland/3664/index.html
 349+* decode GEOB ID3v2 structure as encoded by RealJukebox,
 350+ decode NCON ID3v2 structure as encoded by MusicMatch
 351+ (probably won't happen - the formats are proprietary)
 352+
 353+
 354+
 355+Known Bugs/Issues in getID3() that may be fixed eventually
 356+===========================================================================
 357+
 358+* Cannot determine bitrate for MPEG video with VBR video data
 359+ (need documentation)
 360+* Interlace/progressive cannot be determined for MPEG video
 361+ (need documentation)
 362+* MIDI playtime is sometimes inaccurate
 363+* AAC-RAW mode files cannot be identified
 364+* WavPack-RAW mode files cannot be identified
 365+* mp4 files report lots of "Unknown QuickTime atom type"
 366+ (need documentation)
 367+* Encrypted ASF/WMA/WMV files warn about "unhandled GUID
 368+ ASF_Content_Encryption_Object"
 369+* Bitrate split between audio and video cannot be calculated for
 370+ NSV, only the total bitrate. (need documentation)
 371+* All Ogg formats (Vorbis, OggFLAC, Speex) are affected by the
 372+ problem of large VorbisComments spanning multiple Ogg pages, but
 373+ but only OggVorbis files can be processed with vorbiscomment.
 374+* The version of "head" supplied with Mac OS 10.2.8 (maybe other
 375+ versions too) does only understands a single option (-n) and
 376+ therefore fails. getID3 ignores this and returns wrong md5_data.
 377+
 378+
 379+
 380+Known Bugs/Issues in getID3() that cannot be fixed
 381+--------------------------------------------------
 382+
 383+* Files larger than 2GB cannot always be parsed fully by getID3()
 384+ due to limitations in the PHP filesystem functions.
 385+ NOTE: Since v1.7.8b3 there is partial support for larger-than-
 386+ 2GB files, most of which will parse OK, as long as no critical
 387+ data is located beyond the 2GB offset.
 388+ Known will-work:
 389+ * ZIP (format doesn't support files >2GB)
 390+ * FLAC (current encoders don't support files >2GB)
 391+ Known will-not-work:
 392+ * ID3v1 tags (always located at end-of-file)
 393+ * Lyrics3 tags (always located at end-of-file)
 394+ * APE tags (always located at end-of-file)
 395+ Maybe-will-work:
 396+ * Quicktime (will work if needed metadata is before 2GB offset,
 397+ that is if the file has been hinted/optimized for streaming)
 398+ * RIFF.WAV (should work fine, but gives warnings about not being
 399+ able to parse all chunks)
 400+ * RIFF.AVI (playtime will probably be wrong, is only based on
 401+ "movi" chunk that fits in the first 2GB, should issue error
 402+ to show that playtime is incorrect. Other data should be mostly
 403+ correct, assuming that data is constant throughout the file)
 404+
 405+
 406+
 407+Known Bugs/Issues in other programs
 408+-----------------------------------
 409+
 410+* Windows Media Player (up to v11) and iTunes (up to v10+) do
 411+ not correctly handle ID3v2.3 tags with UTF-16BE+BOM
 412+ encoding (they assume the data is UTF-16LE+BOM and either
 413+ crash (WMP) or output Asian character set (iTunes)
 414+* Winamp (up to v2.80 at least) does not support ID3v2.4 tags,
 415+ only ID3v2.3
 416+ see: http://forums.winamp.com/showthread.php?postid=387524
 417+* Some versions of Helium2 (www.helium2.com) do not write
 418+ ID3v2.4-compliant Frame Sizes, even though the tag is marked
 419+ as ID3v2.4) (detected by getID3())
 420+* MP3ext V3.3.17 places a non-compliant padding string at the end
 421+ of the ID3v2 header. This is supposedly fixed in v3.4b21 but
 422+ only if you manually add a registry key. This fix is not yet
 423+ confirmed. (detected by getID3())
 424+* CDex v1.40 (fixed by v1.50b7) writes non-compliant Ogg comment
 425+ strings, supposed to be in the format "NAME=value" but actually
 426+ written just "value" (detected by getID3())
 427+* Oggenc 0.9-rc3 flags the encoded file as ABR whether it's
 428+ actually ABR or VBR.
 429+* iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably
 430+ other versions are too) writes ID3v2.3 comment tags using a
 431+ frame name 'COM ' which is not valid for ID3v2.3+ (it's an
 432+ ID3v2.2-style frame name) (detected by getID3())
 433+* MP2enc does not encode mono CBR MP2 files properly (half speed
 434+ sound and double playtime)
 435+* MP2enc does not encode mono VBR MP2 files properly (actually
 436+ encoded as stereo)
 437+* tooLAME does not encode mono VBR MP2 files properly (actually
 438+ encoded as stereo)
 439+* AACenc encodes files in VBR mode (actually ABR) even if CBR is
 440+ specified
 441+* AAC/ADIF - bitrate_mode = cbr for vbr files
 442+* LAME 3.90-3.92 prepends one frame of null data (space for the
 443+ LAME/VBR header, but it never gets written) when encoding in CBR
 444+ mode with the DLL
 445+* Ahead Nero encodes TwinVQF with a DSIZ value (which is supposed
 446+ to be the filesize in bytes) of "0" for TwinVQF v1.0 and "1" for
 447+ TwinVQF v2.0 (detected by getID3())
 448+* Ahead Nero encodes TwinVQF files 1 second shorter than they
 449+ should be
 450+* AAC-ADTS files are always actually encoded VBR, even if CBR mode
 451+ is specified (the CBR-mode switches on the encoder enable ABR
 452+ mode, not CBR as such, but it's not possible to tell the
 453+ difference between such ABR files and true VBR)
 454+* STREAMINFO.audio_signature in OggFLAC is always null. "The reason
 455+ it's like that is because there is no seeking support in
 456+ libOggFLAC yet, so it has no way to go back and write the
 457+ computed sum after encoding. Seeking support in Ogg FLAC is the
 458+ #1 item for the next release." - Josh Coalson (FLAC developer)
 459+ NOTE: getID3() will calculate md5_data in a method similar to
 460+ other file formats, but that value cannot be compared to the
 461+ md5_data value from FLAC data in a FLAC file format.
 462+* STREAMINFO.audio_signature is not calculated in FLAC v0.3.0 &
 463+ v0.4.0 - getID3() will calculate md5_data in a method similar to
 464+ other file formats, but that value cannot be compared to the
 465+ md5_data value from FLAC v0.5.0+
 466+* RioPort (various versions including 2.0 and 3.11) tags ID3v2 with
 467+ a WCOM frame that has no data portion
 468+* Earlier versions of Coolplayer adds illegal ID3 tags to Ogg Vorbis
 469+ files, thus making them corrupt.
 470+* Meracl ID3 Tag Writer v1.3.4 (and older) incorrectly truncates the
 471+ last byte of data from an MP3 file when appending a new ID3v1 tag.
 472+ (detected by getID3())
 473+* Lossless-Audio files encoded with and without the -noseek switch
 474+ do actually differ internally and therefore cannot match md5_data
 475+* iTunes has been known to append a new ID3v1 tag on the end of an
 476+ existing ID3v1 tag when ID3v2 tag is also present
 477+ (detected by getID3())
 478+
 479+
 480+
 481+
 482+Reference material:
 483+===========================================================================
 484+
 485+[www.id3.org material now mirrored at http://id3lib.sourceforge.net/id3/]
 486+* http://www.id3.org/id3v2.4.0-structure.txt
 487+* http://www.id3.org/id3v2.4.0-frames.txt
 488+* http://www.id3.org/id3v2.4.0-changes.txt
 489+* http://www.id3.org/id3v2.3.0.txt
 490+* http://www.id3.org/id3v2-00.txt
 491+* http://www.id3.org/mp3frame.html
 492+* http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html <mathewhendry@hotmail.com>
 493+* http://www.dv.co.yu/mpgscript/mpeghdr.htm
 494+* http://www.mp3-tech.org/programmer/frame_header.html
 495+* http://users.belgacom.net/gc247244/extra/tag.html
 496+* http://gabriel.mp3-tech.org/mp3infotag.html
 497+* http://www.id3.org/iso4217.html
 498+* http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT
 499+* http://www.xiph.org/ogg/vorbis/doc/framing.html
 500+* http://www.xiph.org/ogg/vorbis/doc/v-comment.html
 501+* http://leknor.com/code/php/class.ogg.php.txt
 502+* http://www.id3.org/iso639-2.html
 503+* http://www.id3.org/lyrics3.html
 504+* http://www.id3.org/lyrics3200.html
 505+* http://www.psc.edu/general/software/packages/ieee/ieee.html
 506+* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
 507+* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
 508+* http://www.jmcgowan.com/avi.html
 509+* http://www.wotsit.org/
 510+* http://www.herdsoft.com/ti/davincie/davp3xo2.htm
 511+* http://www.mathdogs.com/vorbis-illuminated/bitstream-appendix.html
 512+* "Standard MIDI File Format" by Dustin Caldwell (from www.wotsit.org)
 513+* http://midistudio.com/Help/GMSpecs_Patches.htm
 514+* http://www.xiph.org/archives/vorbis/200109/0459.html
 515+* http://www.replaygain.org/
 516+* http://www.lossless-audio.com/
 517+* http://download.microsoft.com/download/winmediatech40/Doc/1.0/WIN98MeXP/EN-US/ASF_Specification_v.1.0.exe
 518+* http://mediaxw.sourceforge.net/files/doc/Active%20Streaming%20Format%20(ASF)%201.0%20Specification.pdf
 519+* http://www.uni-jena.de/~pfk/mpp/sv8/
 520+* http://jfaul.de/atl/
 521+* http://www.uni-jena.de/~pfk/mpp/
 522+* http://www.libpng.org/pub/png/spec/png-1.2-pdg.html
 523+* http://www.real.com/devzone/library/creating/rmsdk/doc/rmff.htm
 524+* http://www.fastgraph.com/help/bmp_os2_header_format.html
 525+* http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
 526+* http://flac.sourceforge.net/format.html
 527+* http://www.research.att.com/projects/mpegaudio/mpeg2.html
 528+* http://www.audiocoding.com/wiki/index.php?page=AAC
 529+* http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
 530+* http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
 531+* http://developer.apple.com/techpubs/quicktime/qtdevdocs/RM/frameset.htm
 532+* http://www.nullsoft.com/nsv/
 533+* http://www.wotsit.org/download.asp?f=iso9660
 534+* http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
 535+* http://www.cdroller.com/htm/readdata.html
 536+* http://www.speex.org/manual/node10.html
 537+* http://www.harmony-central.com/Computer/Programming/aiff-file-format.doc
 538+* http://www.faqs.org/rfcs/rfc2361.html
 539+* http://ghido.shelter.ro/
 540+* http://www.ebu.ch/tech_t3285.pdf
 541+* http://www.sr.se/utveckling/tu/bwf
 542+* http://ftp.aessc.org/pub/aes46-2002.pdf
 543+* http://cartchunk.org:8080/
 544+* http://www.broadcastpapers.com/radio/cartchunk01.htm
 545+* http://www.hr/josip/DSP/AudioFile2.html
 546+* http://home.attbi.com/~chris.bagwell/AudioFormats-11.html
 547+* http://www.pure-mac.com/extkey.html
 548+* http://cesnet.dl.sourceforge.net/sourceforge/bonkenc/bonk-binary-format-0.9.txt
 549+* http://www.headbands.com/gspot/
 550+* http://www.openswf.org/spec/SWFfileformat.html
 551+* http://j-faul.virtualave.net/
 552+* http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
 553+* http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
 554+* http://sswf.sourceforge.net/SWFalexref.html
 555+* http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
 556+* http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
 557+* http://developer.apple.com/quicktime/icefloe/dispatch012.html
 558+* http://www.csdn.net/Dev/Format/graphics/PCD.htm
 559+* http://tta.iszf.irk.ru/
 560+* http://www.atsc.org/standards/a_52a.pdf
 561+* http://www.alanwood.net/unicode/
 562+* http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
 563+* http://www.its.msstate.edu/net/real/reports/config/tags.stats
 564+* http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
 565+* http://brennan.young.net/Comp/LiveStage/things.html
 566+* http://www.multiweb.cz/twoinches/MP3inside.htm
 567+* http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
 568+* http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
 569+* http://www.unicode.org/unicode/faq/utf_bom.html
 570+* http://tta.corecodec.org/?menu=format
 571+* http://www.scvi.net/nsvformat.htm
 572+* http://pda.etsi.org/pda/queryform.asp
 573+* http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
 574+* http://trac.musepack.net/trac/wiki/SV8Specification
 575+* http://wyday.com/cuesharp/specification.php
\ No newline at end of file
Property changes on: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/getid3/readme.txt
___________________________________________________________________
Added: svn:mime-type
1576 + text/plain
Index: trunk/extensions/TimedMediaHandler/handlers/WebMHandler/WebMHandler.php
@@ -0,0 +1,91 @@
 2+<?php
 3+/**
 4+ * WebM handler
 5+ */
 6+class WebMHandler extends TimedMediaHandler {
 7+ const METADATA_VERSION = 1;
 8+
 9+ function getMetadata( $image, $path ) {
 10+ $metadata = array( 'version' => self::METADATA_VERSION );
 11+
 12+ $getID3 = new getID3();
 13+
 14+ // Don't grab stuff we don't use:
 15+ $getID3->option_tag_id3v1 = false; // Read and process ID3v1 tags
 16+ $getID3->option_tag_id3v2 = false; // Read and process ID3v2 tags
 17+ $getID3->option_tag_lyrics3 = false; // Read and process Lyrics3 tags
 18+ $getID3->option_tag_apetag = false; // Read and process APE tags
 19+ $getID3->option_tags_process = false; // Copy tags to root key 'tags' and encode to $this->encoding
 20+ $getID3->option_tags_html = false; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
 21+
 22+ // Analyze file and store returned data in $ThisFileInfo
 23+ $id3 = $getID3->analyze( $path );
 24+ // Unset some parts of id3 that are too detailed and matroska specific:
 25+ unset( $id3['matroska'] );
 26+ // remove file paths
 27+ unset( $id3['filename'] );
 28+ unset( $id3['filepath'] );
 29+ unset( $id3['filenamepath']);
 30+ return serialize( $id3 );
 31+ }
 32+
 33+ /**
 34+ * Get the "media size"
 35+ *
 36+ */
 37+ function getImageSize( $file, $path, $metadata = false ) {
 38+ global $wgMediaVideoTypes;
 39+ // Just return the size of the first video stream
 40+ if ( $metadata === false ) {
 41+ $metadata = $file->getMetadata();
 42+ }
 43+ $metadata = $this->unpackMetadata( $metadata );
 44+ if ( isset( $metadata['error'] ) || !isset( $metadata['streams'] ) ) {
 45+ return false;
 46+ }
 47+ foreach ( $metadata['video'] as $stream ) {
 48+ return array(
 49+ $stream['resolution_x'],
 50+ $stream['resolution_y']
 51+ );
 52+ }
 53+ return array( false, false );
 54+ }
 55+
 56+ function unpackMetadata( $metadata ) {
 57+ $unser = @unserialize( $metadata );
 58+ if ( isset( $unser['version'] ) && $unser['version'] == self::METADATA_VERSION ) {
 59+ return $unser;
 60+ } else {
 61+ return false;
 62+ }
 63+ }
 64+
 65+ function getMetadataType( $image = '' ) {
 66+ return 'webm';
 67+ }
 68+
 69+ function getStreamTypes( $file ) {
 70+ $streamTypes = '';
 71+ $metadata = self::unpackMetadata( $file->getMetadata() );
 72+ if ( !$metadata || isset( $metadata['error'] ) ) {
 73+ return false;
 74+ }
 75+ print_r( $metadata );
 76+ die();
 77+ foreach ( $metadata['streams'] as $stream ) {
 78+ $streamTypes[$stream['type']] = true;
 79+ }
 80+ return array_keys( $streamTypes );
 81+ }
 82+
 83+ function getLength( $file ) {
 84+ $metadata = $this->unpackMetadata( $file->getMetadata() );
 85+ if ( !$metadata || isset( $metadata['error'] ) ) {
 86+ return 0;
 87+ } else {
 88+ return $metadata['playtime_seconds'];
 89+ }
 90+ }
 91+
 92+}
\ No newline at end of file
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/OggHandler.php
@@ -0,0 +1,113 @@
 2+<?php
 3+/**
 4+ * ogg handler
 5+ */
 6+class OggHandler extends TimedMediaHandler {
 7+ const METADATA_VERSION = 2;
 8+
 9+ function getMetadata( $image, $path ) {
 10+ $metadata = array( 'version' => self::METADATA_VERSION );
 11+
 12+ if ( !class_exists( 'File_Ogg' ) ) {
 13+ require( 'File/Ogg.php' );
 14+ }
 15+ try {
 16+ $f = new File_Ogg( $path );
 17+ $streams = array();
 18+ foreach ( $f->listStreams() as $streamType => $streamIDs ) {
 19+ foreach ( $streamIDs as $streamID ) {
 20+ $stream = $f->getStream( $streamID );
 21+ $streams[$streamID] = array(
 22+ 'serial' => $stream->getSerial(),
 23+ 'group' => $stream->getGroup(),
 24+ 'type' => $stream->getType(),
 25+ 'vendor' => $stream->getVendor(),
 26+ 'length' => $stream->getLength(),
 27+ 'size' => $stream->getSize(),
 28+ 'header' => $stream->getHeader(),
 29+ 'comments' => $stream->getComments()
 30+ );
 31+ }
 32+ }
 33+ $metadata['streams'] = $streams;
 34+ $metadata['length'] = $f->getLength();
 35+ // Get the offset of the file (in cases where the file is a segment copy)
 36+ $metadata['offset'] = $f->getStartOffset();
 37+ } catch ( PEAR_Exception $e ) {
 38+ // File not found, invalid stream, etc.
 39+ $metadata['error'] = array(
 40+ 'message' => $e->getMessage(),
 41+ 'code' => $e->getCode()
 42+ );
 43+ }
 44+ return serialize( $metadata );
 45+ }
 46+ /**
 47+ * Get the "media size"
 48+ *
 49+ */
 50+ function getImageSize( $file, $path, $metadata = false ) {
 51+ global $wgMediaVideoTypes;
 52+ // Just return the size of the first video stream
 53+ if ( $metadata === false ) {
 54+ $metadata = $file->getMetadata();
 55+ }
 56+ $metadata = $this->unpackMetadata( $metadata );
 57+ if ( isset( $metadata['error'] ) || !isset( $metadata['streams'] ) ) {
 58+ return false;
 59+ }
 60+ foreach ( $metadata['streams'] as $stream ) {
 61+ if ( in_array( $stream['type'], $wgMediaVideoTypes ) ) {
 62+ return array(
 63+ $stream['header']['PICW'],
 64+ $stream['header']['PICH']
 65+ );
 66+ }
 67+ }
 68+ return array( false, false );
 69+ }
 70+
 71+ function unpackMetadata( $metadata ) {
 72+ $unser = @unserialize( $metadata );
 73+ if ( isset( $unser['version'] ) && $unser['version'] == self::METADATA_VERSION ) {
 74+ return $unser;
 75+ } else {
 76+ return false;
 77+ }
 78+ }
 79+
 80+ function getMetadataType( $image = '' ) {
 81+ return 'ogg';
 82+ }
 83+
 84+ function getStreamTypes( $file ) {
 85+ $streamTypes = '';
 86+ $metadata = $this->unpackMetadata( $file->getMetadata() );
 87+ if ( !$metadata || isset( $metadata['error'] ) ) {
 88+ return false;
 89+ }
 90+ foreach ( $metadata['streams'] as $stream ) {
 91+ $streamTypes[$stream['type']] = true;
 92+ }
 93+ return array_keys( $streamTypes );
 94+ }
 95+
 96+ function getOffset( $file ){
 97+ $metadata = $this->unpackMetadata( $file->getMetadata() );
 98+ if ( !$metadata || isset( $metadata['error'] ) || !isset( $metadata['offset']) ) {
 99+ return 0;
 100+ } else {
 101+ return $metadata['offset'];
 102+ }
 103+ }
 104+
 105+ function getLength( $file ) {
 106+ $metadata = $this->unpackMetadata( $file->getMetadata() );
 107+ if ( !$metadata || isset( $metadata['error'] ) ) {
 108+ return 0;
 109+ } else {
 110+ return $metadata['length'];
 111+ }
 112+ }
 113+
 114+}
\ No newline at end of file
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg.php
@@ -0,0 +1,627 @@
 2+<?php
 3+/* vim: set expandtab tabstop=4 shiftwidth=4: */
 4+// +----------------------------------------------------------------------------+
 5+// | File_Ogg PEAR Package for Accessing Ogg Bitstreams |
 6+// | Copyright (c) 2005-2007 |
 7+// | David Grant <david@grant.org.uk> |
 8+// | Tim Starling <tstarling@wikimedia.org> |
 9+// +----------------------------------------------------------------------------+
 10+// | This library is free software; you can redistribute it and/or |
 11+// | modify it under the terms of the GNU Lesser General Public |
 12+// | License as published by the Free Software Foundation; either |
 13+// | version 2.1 of the License, or (at your option) any later version. |
 14+// | |
 15+// | This library is distributed in the hope that it will be useful, |
 16+// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
 17+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
 18+// | Lesser General Public License for more details. |
 19+// | |
 20+// | You should have received a copy of the GNU Lesser General Public |
 21+// | License along with this library; if not, write to the Free Software |
 22+// | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
 23+// +----------------------------------------------------------------------------+
 24+
 25+/**
 26+ * @author David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 27+ * @category File
 28+ * @copyright David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 29+ * @license http://www.gnu.org/copyleft/lesser.html GNU LGPL
 30+ * @link http://pear.php.net/package/File_Ogg
 31+ * @package File_Ogg
 32+ * @version CVS: $Id: Ogg.php,v 1.14 2005/11/19 09:06:30 djg Exp $
 33+ */
 34+
 35+/**
 36+ * @access public
 37+ */
 38+define("OGG_STREAM_VORBIS", 1);
 39+/**
 40+ * @access public
 41+ */
 42+define("OGG_STREAM_THEORA", 2);
 43+/**
 44+ * @access public
 45+ */
 46+define("OGG_STREAM_SPEEX", 3);
 47+/**
 48+ * @access public
 49+ */
 50+define("OGG_STREAM_FLAC", 4);
 51+
 52+/**
 53+ * Capture pattern to determine if a file is an Ogg physical stream.
 54+ *
 55+ * @access private
 56+ */
 57+define("OGG_CAPTURE_PATTERN", "OggS");
 58+/**
 59+ * Maximum size of an Ogg stream page plus four. This value is specified to allow
 60+ * efficient parsing of the physical stream. The extra four is a paranoid measure
 61+ * to make sure a capture pattern is not split into two parts accidentally.
 62+ *
 63+ * @access private
 64+ */
 65+define("OGG_MAXIMUM_PAGE_SIZE", 65311);
 66+/**
 67+ * Capture pattern for an Ogg Vorbis logical stream.
 68+ *
 69+ * @access private
 70+ */
 71+define("OGG_STREAM_CAPTURE_VORBIS", "vorbis");
 72+/**
 73+ * Capture pattern for an Ogg Speex logical stream.
 74+ * @access private
 75+ */
 76+define("OGG_STREAM_CAPTURE_SPEEX", "Speex ");
 77+/**
 78+ * Capture pattern for an Ogg FLAC logical stream.
 79+ *
 80+ * @access private
 81+ */
 82+define("OGG_STREAM_CAPTURE_FLAC", "FLAC");
 83+/**
 84+ * Capture pattern for an Ogg Theora logical stream.
 85+ *
 86+ * @access private
 87+ */
 88+define("OGG_STREAM_CAPTURE_THEORA", "theora");
 89+/**
 90+ * Error thrown if the file location passed is nonexistant or unreadable.
 91+ *
 92+ * @access private
 93+ */
 94+define("OGG_ERROR_INVALID_FILE", 1);
 95+/**
 96+ * Error thrown if the user attempts to extract an unsupported logical stream.
 97+ *
 98+ * @access private
 99+ */
 100+define("OGG_ERROR_UNSUPPORTED", 2);
 101+/**
 102+ * Error thrown if the user attempts to extract an logical stream with no
 103+ * corresponding serial number.
 104+ *
 105+ * @access private
 106+ */
 107+define("OGG_ERROR_BAD_SERIAL", 3);
 108+/**
 109+ * Error thrown if the stream appears to be corrupted.
 110+ *
 111+ * @access private
 112+ */
 113+define("OGG_ERROR_UNDECODABLE", 4);
 114+
 115+require_once('PEAR.php');
 116+require_once('PEAR/Exception.php');
 117+require_once('File/Ogg/Bitstream.php');
 118+require_once("File/Ogg/Flac.php");
 119+require_once("File/Ogg/Speex.php");
 120+require_once("File/Ogg/Theora.php");
 121+require_once("File/Ogg/Vorbis.php");
 122+
 123+
 124+/**
 125+ * Class for parsing a ogg bitstream.
 126+ *
 127+ * This class provides a means to access several types of logical bitstreams (e.g. Vorbis)
 128+ * within a Ogg physical bitstream.
 129+ *
 130+ * @link http://www.xiph.org/ogg/doc/
 131+ * @package File_Ogg
 132+ */
 133+class File_Ogg
 134+{
 135+ /**
 136+ * File pointer to Ogg container.
 137+ *
 138+ * This is the file pointer used for extracting data from the Ogg stream. It is
 139+ * the result of a standard fopen call.
 140+ *
 141+ * @var pointer
 142+ * @access private
 143+ */
 144+ var $_filePointer;
 145+
 146+ /**
 147+ * The container for all logical streams.
 148+ *
 149+ * List of all of the unique streams in the Ogg physical stream. The key
 150+ * used is the unique serial number assigned to the logical stream by the
 151+ * encoding application.
 152+ *
 153+ * @var array
 154+ * @access private
 155+ */
 156+ var $_streamList = array();
 157+ var $_streams = array();
 158+
 159+ /**
 160+ * Length in seconds of each stream group
 161+ */
 162+ var $_groupLengths = array();
 163+
 164+ /**
 165+ * Total length in seconds of the entire file
 166+ */
 167+ var $_totalLength;
 168+ var $_startOffset = false;
 169+
 170+ /**
 171+ * Maximum number of pages to store detailed metadata for, per stream.
 172+ * We can't store every page because there could be millions, causing an OOM.
 173+ * This must be big enough so that all the codecs can get the metadata they
 174+ * need without re-reading the file.
 175+ */
 176+ var $_maxPageCacheSize = 4;
 177+
 178+ /**
 179+ * Returns an interface to an Ogg physical stream.
 180+ *
 181+ * This method takes the path to a local file and examines it for a physical
 182+ * ogg bitsream. After instantiation, the user should query the object for
 183+ * the logical bitstreams held within the ogg container.
 184+ *
 185+ * @access public
 186+ * @param string $fileLocation The path of the file to be examined.
 187+ */
 188+ function __construct($fileLocation)
 189+ {
 190+ clearstatcache();
 191+ if (! file_exists($fileLocation)) {
 192+ throw new PEAR_Exception("Couldn't Open File. Check File Path.", OGG_ERROR_INVALID_FILE);
 193+ }
 194+
 195+ // Open this file as a binary, and split the file into streams.
 196+ $this->_filePointer = fopen($fileLocation, "rb");
 197+ if (!is_resource($this->_filePointer))
 198+ throw new PEAR_Exception("Couldn't Open File. Check File Permissions.", OGG_ERROR_INVALID_FILE);
 199+
 200+ // Check for a stream at the start
 201+ $magic = fread($this->_filePointer, strlen(OGG_CAPTURE_PATTERN));
 202+ if ($magic !== OGG_CAPTURE_PATTERN) {
 203+ throw new PEAR_Exception("Couldn't read file: Incorrect magic number.", OGG_ERROR_UNDECODABLE);
 204+ }
 205+ fseek($this->_filePointer, 0, SEEK_SET);
 206+
 207+ $this->_splitStreams();
 208+ fclose($this->_filePointer);
 209+ }
 210+
 211+ /**
 212+ * Little-endian equivalent for bin2hex
 213+ * @static
 214+ */
 215+ static function _littleEndianBin2Hex( $bin ) {
 216+ $bigEndian = bin2hex( $bin );
 217+ // Reverse entire string
 218+ $reversed = strrev( $bigEndian );
 219+ // Swap nibbles back
 220+ for ( $i = 0; $i < strlen( $bigEndian ); $i += 2 ) {
 221+ $temp = $reversed[$i];
 222+ $reversed[$i] = $reversed[$i+1];
 223+ $reversed[$i+1] = $temp;
 224+ }
 225+ return $reversed;
 226+ }
 227+
 228+
 229+ /**
 230+ * Read a binary structure from a file. An array of unsigned integers are read.
 231+ * Large integers are upgraded to floating point on overflow.
 232+ *
 233+ * Format is big-endian as per Theora bit packing convention, this function
 234+ * won't work for Vorbis.
 235+ *
 236+ * @param resource $file
 237+ * @param array $fields Associative array mapping name to length in bits
 238+ */
 239+ static function _readBigEndian($file, $fields)
 240+ {
 241+ $bufferLength = ceil(array_sum($fields) / 8);
 242+ $buffer = fread($file, $bufferLength);
 243+ if (strlen($buffer) != $bufferLength) {
 244+ throw new PEAR_Exception('Unexpected end of file', OGG_ERROR_UNDECODABLE);
 245+ }
 246+ $bytePos = 0;
 247+ $bitPos = 0;
 248+ $byteValue = ord($buffer[0]);
 249+ $output = array();
 250+ foreach ($fields as $name => $width) {
 251+ if ($width % 8 == 0 && $bitPos == 0) {
 252+ // Byte aligned case
 253+ $bytes = $width / 8;
 254+ $endBytePos = $bytePos + $bytes;
 255+ $value = 0;
 256+ while ($bytePos < $endBytePos) {
 257+ $value = ($value * 256) + ord($buffer[$bytePos]);
 258+ $bytePos++;
 259+ }
 260+ if ($bytePos < strlen($buffer)) {
 261+ $byteValue = ord($buffer[$bytePos]);
 262+ }
 263+ } else {
 264+ // General case
 265+ $bitsRemaining = $width;
 266+ $value = 0;
 267+ while ($bitsRemaining > 0) {
 268+ $bitsToRead = min($bitsRemaining, 8 - $bitPos);
 269+ $byteValue <<= $bitsToRead;
 270+ $overflow = ($byteValue & 0xff00) >> 8;
 271+ $byteValue &= $byteValue & 0xff;
 272+
 273+ $bitPos += $bitsToRead;
 274+ $bitsRemaining -= $bitsToRead;
 275+ $value += $overflow * pow(2, $bitsRemaining);
 276+
 277+ if ($bitPos >= 8) {
 278+ $bitPos = 0;
 279+ $bytePos++;
 280+ if ($bitsRemaining <= 0) {
 281+ break;
 282+ }
 283+ $byteValue = ord($buffer[$bytePos]);
 284+ }
 285+ }
 286+ }
 287+ $output[$name] = $value;
 288+ assert($bytePos <= $bufferLength);
 289+ }
 290+ return $output;
 291+ }
 292+
 293+ /**
 294+ * Read a binary structure from a file. An array of unsigned integers are read.
 295+ * Large integers are upgraded to floating point on overflow.
 296+ *
 297+ * Format is little-endian as per Vorbis bit packing convention.
 298+ *
 299+ * @param resource $file
 300+ * @param array $fields Associative array mapping name to length in bits
 301+ */
 302+ static function _readLittleEndian( $file, $fields ) {
 303+ $bufferLength = ceil(array_sum($fields) / 8);
 304+ $buffer = fread($file, $bufferLength);
 305+ if (strlen($buffer) != $bufferLength) {
 306+ throw new PEAR_Exception('Unexpected end of file', OGG_ERROR_UNDECODABLE);
 307+ }
 308+
 309+ $bytePos = 0;
 310+ $bitPos = 0;
 311+ $byteValue = ord($buffer[0]) << 8;
 312+ $output = array();
 313+ foreach ($fields as $name => $width) {
 314+ if ($width % 8 == 0 && $bitPos == 0) {
 315+ // Byte aligned case
 316+ $bytes = $width / 8;
 317+ $value = 0;
 318+ for ($i = 0; $i < $bytes; $i++, $bytePos++) {
 319+ $value += pow(256, $i) * ord($buffer[$bytePos]);
 320+ }
 321+ if ($bytePos < strlen($buffer)) {
 322+ $byteValue = ord($buffer[$bytePos]) << 8;
 323+ }
 324+ } else {
 325+ // General case
 326+ $bitsRemaining = $width;
 327+ $value = 0;
 328+ while ($bitsRemaining > 0) {
 329+ $bitsToRead = min($bitsRemaining, 8 - $bitPos);
 330+ $byteValue >>= $bitsToRead;
 331+ $overflow = ($byteValue & 0xff) >> (8 - $bitsToRead);
 332+ $byteValue &= 0xff00;
 333+
 334+ $value += $overflow * pow(2, $width - $bitsRemaining);
 335+ $bitPos += $bitsToRead;
 336+ $bitsRemaining -= $bitsToRead;
 337+
 338+ if ($bitPos >= 8) {
 339+ $bitPos = 0;
 340+ $bytePos++;
 341+ if ($bitsRemaining <= 0) {
 342+ break;
 343+ }
 344+ $byteValue = ord($buffer[$bytePos]) << 8;
 345+ }
 346+ }
 347+ }
 348+ $output[$name] = $value;
 349+ assert($bytePos <= $bufferLength);
 350+ }
 351+ return $output;
 352+ }
 353+
 354+
 355+ /**
 356+ * @access private
 357+ */
 358+ function _decodePageHeader($pageData, $pageOffset, $groupId)
 359+ {
 360+ // Extract the various bits and pieces found in each packet header.
 361+ if (substr($pageData, 0, 4) != OGG_CAPTURE_PATTERN)
 362+ return (false);
 363+
 364+ $stream_version = unpack("C1data", substr($pageData, 4, 1));
 365+ if ($stream_version['data'] != 0x00)
 366+ return (false);
 367+
 368+ $header_flag = unpack("Cdata", substr($pageData, 5, 1));
 369+
 370+ // Exact granule position
 371+ $abs_granule_pos = self::_littleEndianBin2Hex( substr($pageData, 6, 8));
 372+
 373+ // Approximate (floating point) granule position
 374+ $pos = unpack("Va/Vb", substr($pageData, 6, 8));
 375+ $approx_granule_pos = $pos['a'] + $pos['b'] * pow(2, 32);
 376+
 377+ // Serial number for the current datastream.
 378+ $stream_serial = unpack("Vdata", substr($pageData, 14, 4));
 379+ $page_sequence = unpack("Vdata", substr($pageData, 18, 4));
 380+ $checksum = unpack("Vdata", substr($pageData, 22, 4));
 381+ $page_segments = unpack("Cdata", substr($pageData, 26, 1));
 382+ $segments_total = 0;
 383+ for ($i = 0; $i < $page_segments['data']; ++$i) {
 384+ $segment_length = unpack("Cdata", substr($pageData, 26 + ($i + 1), 1));
 385+ $segments_total += $segment_length['data'];
 386+ }
 387+ $pageFinish = $pageOffset + 27 + $page_segments['data'] + $segments_total;
 388+ $page = array(
 389+ 'stream_version' => $stream_version['data'],
 390+ 'header_flag' => $header_flag['data'],
 391+ 'abs_granule_pos' => $abs_granule_pos,
 392+ 'approx_granule_pos' => $approx_granule_pos,
 393+ 'checksum' => sprintf("%u", $checksum['data']),
 394+ 'segments' => $page_segments['data'],
 395+ 'head_offset' => $pageOffset,
 396+ 'body_offset' => $pageOffset + 27 + $page_segments['data'],
 397+ 'body_finish' => $pageFinish,
 398+ 'data_length' => $pageFinish - $pageOffset,
 399+ 'group' => $groupId,
 400+ );
 401+ if ( !isset( $this->_streamList[$stream_serial['data']] ) ) {
 402+ $this->_streamList[$stream_serial['data']] = array(
 403+ 'pages' => array(),
 404+ 'data_length' => 0,
 405+ 'first_granule_pos' => null,
 406+ 'last_granule_pos' => null,
 407+ );
 408+ }
 409+ $stream =& $this->_streamList[$stream_serial['data']];
 410+ if ( count( $stream['pages'] ) < $this->_maxPageCacheSize ) {
 411+ $stream['pages'][$page_sequence['data']] = $page;
 412+ }
 413+ $stream['last_page'] = $page;
 414+ $stream['data_length'] += $page['data_length'];
 415+
 416+ # Reject -1 as a granule pos, that means no segment finished in the packet
 417+ if ( $abs_granule_pos !== 'ffffffffffffffff' ) {
 418+ if ( $stream['first_granule_pos'] === null ) {
 419+ $stream['first_granule_pos'] = $abs_granule_pos;
 420+ }
 421+ $stream['last_granule_pos'] = $abs_granule_pos;
 422+ }
 423+
 424+ $pageData = null;
 425+ return $page;
 426+ }
 427+
 428+ /**
 429+ * @access private
 430+ */
 431+ function _splitStreams()
 432+ {
 433+ // Loop through the physical stream until there are no more pages to read.
 434+ $groupId = 0;
 435+ $openStreams = 0;
 436+ $this_page_offset = 0;
 437+ while (!feof($this->_filePointer)) {
 438+ $pageData = fread($this->_filePointer, 282);
 439+ if (strval($pageData) === '') {
 440+ break;
 441+ }
 442+ $page = $this->_decodePageHeader($pageData, $this_page_offset, $groupId);
 443+ if ($page === false) {
 444+ throw new PEAR_Exception("Cannot decode Ogg file: Invalid page at offset $this_page_offset", OGG_ERROR_UNDECODABLE);
 445+ }
 446+
 447+ // Keep track of multiplexed groups
 448+ if ($page['header_flag'] & 2/*bos*/) {
 449+ $openStreams++;
 450+ } elseif ($page['header_flag'] & 4/*eos*/) {
 451+ $openStreams--;
 452+ if (!$openStreams) {
 453+ // End of group
 454+ $groupId++;
 455+ }
 456+ }
 457+ if ($openStreams < 0) {
 458+ throw new PEAR_Exception("Unexpected end of stream", OGG_ERROR_UNDECODABLE);
 459+ }
 460+
 461+ $this_page_offset = $page['body_finish'];
 462+ fseek( $this->_filePointer, $this_page_offset, SEEK_SET );
 463+ }
 464+ // Loop through the streams, and find out what type of stream is available.
 465+ $groupLengths = array();
 466+ foreach ($this->_streamList as $stream_serial => $streamData) {
 467+ fseek($this->_filePointer, $streamData['pages'][0]['body_offset'], SEEK_SET);
 468+ $pattern = fread($this->_filePointer, 8);
 469+ if (preg_match("/" . OGG_STREAM_CAPTURE_VORBIS . "/", $pattern)) {
 470+ $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_VORBIS;
 471+ $stream = new File_Ogg_Vorbis($stream_serial, $streamData, $this->_filePointer);
 472+ } elseif (preg_match("/" . OGG_STREAM_CAPTURE_SPEEX . "/", $pattern)) {
 473+ $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_SPEEX;
 474+ $stream = new File_Ogg_Speex($stream_serial, $streamData, $this->_filePointer);
 475+ } elseif (preg_match("/" . OGG_STREAM_CAPTURE_FLAC . "/", $pattern)) {
 476+ $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_FLAC;
 477+ $stream = new File_Ogg_Flac($stream_serial, $streamData, $this->_filePointer);
 478+ } elseif (preg_match("/" . OGG_STREAM_CAPTURE_THEORA . "/", $pattern)) {
 479+ $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_THEORA;
 480+ $stream = new File_Ogg_Theora($stream_serial, $streamData, $this->_filePointer);
 481+ } else {
 482+ $streamData['stream_type'] = "unknown";
 483+ $stream = false;
 484+ }
 485+
 486+ if ($stream) {
 487+ $this->_streams[$stream_serial] = $stream;
 488+ $group = $streamData['pages'][0]['group'];
 489+ if (isset($groupLengths[$group])) {
 490+ $groupLengths[$group] = max($groupLengths[$group], $stream->getLength());
 491+ } else {
 492+ $groupLengths[$group] = $stream->getLength();
 493+ }
 494+ //just store the startOffset for the first stream:
 495+ if( $this->_startOffset === false ){
 496+ $this->_startOffset = $stream->getStartOffset();
 497+ }
 498+
 499+ }
 500+ }
 501+ $this->_groupLengths = $groupLengths;
 502+ $this->_totalLength = array_sum( $groupLengths );
 503+ unset($this->_streamList);
 504+ }
 505+
 506+ /**
 507+ * Returns the overead percentage used by the Ogg headers.
 508+ *
 509+ * This function returns the percentage of the total stream size
 510+ * used for Ogg headers.
 511+ *
 512+ * @return float
 513+ */
 514+ function getOverhead() {
 515+ $header_size = 0;
 516+ $stream_size = 0;
 517+ foreach ($this->_streams as $serial => $stream) {
 518+ foreach ($stream->_streamList as $offset => $stream_data) {
 519+ $header_size += $stream_data['body_offset'] - $stream_data['head_offset'];
 520+ $stream_size = $stream_data['body_finish'];
 521+ }
 522+ }
 523+ return sprintf("%0.2f", ($header_size / $stream_size) * 100);
 524+ }
 525+
 526+ /**
 527+ * Returns the appropriate logical bitstream that corresponds to the provided serial.
 528+ *
 529+ * This function returns a logical bitstream contained within the Ogg physical
 530+ * stream, corresponding to the serial used as the offset for that bitstream.
 531+ * The returned stream may be Vorbis, Speex, FLAC or Theora, although the only
 532+ * usable bitstream is Vorbis.
 533+ *
 534+ * @return File_Ogg_Bitstream
 535+ */
 536+ function &getStream($streamSerial)
 537+ {
 538+ if (! array_key_exists($streamSerial, $this->_streams))
 539+ throw new PEAR_Exception("The stream number is invalid.", OGG_ERROR_BAD_SERIAL);
 540+
 541+ return $this->_streams[$streamSerial];
 542+ }
 543+
 544+ /**
 545+ * This function returns true if a logical bitstream of the requested type can be found.
 546+ *
 547+ * This function checks the contents of this ogg physical bitstream for of logical
 548+ * bitstream corresponding to the supplied type. If one is found, the function returns
 549+ * true, otherwise it return false.
 550+ *
 551+ * @param int $streamType
 552+ * @return boolean
 553+ */
 554+ function hasStream($streamType)
 555+ {
 556+ foreach ($this->_stream as $stream) {
 557+ if ($stream['stream_type'] == $streamType)
 558+ return (true);
 559+ }
 560+ return (false);
 561+ }
 562+
 563+ /**
 564+ * Returns an array of logical streams inside this physical bitstream.
 565+ *
 566+ * This function returns an array of logical streams found within this physical
 567+ * bitstream. If a filter is provided, only logical streams of the requested type
 568+ * are returned, as an array of serial numbers. If no filter is provided, this
 569+ * function returns a two-dimensional array, with the stream type as the primary key,
 570+ * and a value consisting of an array of stream serial numbers.
 571+ *
 572+ * @param int $filter
 573+ * @return array
 574+ */
 575+ function listStreams($filter = null)
 576+ {
 577+ $streams = array();
 578+ // Loops through the streams and assign them to an appropriate index,
 579+ // ready for filtering the second part of this function.
 580+ foreach ($this->_streams as $serial => $stream) {
 581+ $stream_type = 0;
 582+ switch (get_class($stream)) {
 583+ case "file_ogg_flac":
 584+ $stream_type = OGG_STREAM_FLAC;
 585+ break;
 586+ case "file_ogg_speex":
 587+ $stream_type = OGG_STREAM_SPEEX;
 588+ break;
 589+ case "file_ogg_theora":
 590+ $stream_type = OGG_STREAM_THEORA;
 591+ break;
 592+ case "file_ogg_vorbis":
 593+ $stream_type = OGG_STREAM_VORBIS;
 594+ break;
 595+ }
 596+ if (! isset($streams[$stream_type]))
 597+ // Initialise the result list for this stream type.
 598+ $streams[$stream_type] = array();
 599+
 600+ $streams[$stream_type][] = $serial;
 601+ }
 602+
 603+ // Perform filtering.
 604+ if (is_null($filter))
 605+ return ($streams);
 606+ elseif (isset($streams[$filter]))
 607+ return ($streams[$filter]);
 608+ else
 609+ return array();
 610+ }
 611+ /**
 612+ * getStartOffset
 613+ *
 614+ * @return unknown
 615+ */
 616+ function getStartOffset(){
 617+ if( $this->_startOffset === false)
 618+ return 0;
 619+ return $this->_startOffset;
 620+ }
 621+ /**
 622+ * Get the total length of the group of streams
 623+ */
 624+ function getLength() {
 625+ return $this->_totalLength;
 626+ }
 627+}
 628+?>
Property changes on: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg.php
___________________________________________________________________
Added: svn:eol-style
1629 + native
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Bitstream.php
@@ -0,0 +1,125 @@
 2+<?php
 3+/* vim: set expandtab tabstop=4 shiftwidth=4: */
 4+// +----------------------------------------------------------------------------+
 5+// | File_Ogg PEAR Package for Accessing Ogg Bitstreams |
 6+// | Copyright (c) 2005-2007 |
 7+// | David Grant <david@grant.org.uk> |
 8+// | Tim Starling <tstarling@wikimedia.org> |
 9+// +----------------------------------------------------------------------------+
 10+// | This library is free software; you can redistribute it and/or |
 11+// | modify it under the terms of the GNU Lesser General Public |
 12+// | License as published by the Free Software Foundation; either |
 13+// | version 2.1 of the License, or (at your option) any later version. |
 14+// | |
 15+// | This library is distributed in the hope that it will be useful, |
 16+// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
 17+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
 18+// | Lesser General Public License for more details. |
 19+// | |
 20+// | You should have received a copy of the GNU Lesser General Public |
 21+// | License along with this library; if not, write to the Free Software |
 22+// | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
 23+// +----------------------------------------------------------------------------+
 24+
 25+
 26+/**
 27+ * @author David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 28+ * @category File
 29+ * @copyright David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 30+ * @license http://www.gnu.org/copyleft/lesser.html GNU LGPL
 31+ * @link http://pear.php.net/package/File_Ogg
 32+ * @package File_Ogg
 33+ * @version CVS: $Id: Bitstream.php,v 1.3 2005/11/08 19:36:18 djg Exp $
 34+ */
 35+class File_Ogg_Bitstream
 36+{
 37+ /**
 38+ * The serial number of this logical stream.
 39+ *
 40+ * @var int
 41+ * @access private
 42+ */
 43+ var $_streamSerial;
 44+ /**
 45+ * @access private
 46+ */
 47+ var $_streamData;
 48+ /**
 49+ * @access private
 50+ */
 51+ var $_filePointer;
 52+ /**
 53+ * The number of bits used in this stream.
 54+ *
 55+ * @var int
 56+ * @access private
 57+ */
 58+ var $_streamSize;
 59+
 60+ /**
 61+ * The last granule position in the stream
 62+ * @var int
 63+ * @access private
 64+ */
 65+ var $_lastGranulePos;
 66+
 67+ /**
 68+ * Constructor for a generic logical stream.
 69+ *
 70+ * @param int $streamSerial Serial number of the logical stream.
 71+ * @param array $streamData Data for the requested logical stream.
 72+ * @param string $filePath Location of a file on the filesystem.
 73+ * @param pointer $filePointer File pointer for the current physical stream.
 74+ * @access private
 75+ */
 76+ function __construct($streamSerial, $streamData, $filePointer)
 77+ {
 78+ $this->_streamSerial = $streamSerial;
 79+ $this->_streamData = $streamData;
 80+ $this->_filePointer = $filePointer;
 81+ $this->_firstGranulePos = $streamData['first_granule_pos'];
 82+ $this->_lastGranulePos = $streamData['last_granule_pos'];
 83+ $this->_streamSize = $streamData['data_length'];
 84+ $this->_group = $streamData['pages'][0]['group'];
 85+ }
 86+
 87+ /**
 88+ * Gives the serial number of this stream.
 89+ *
 90+ * The stream serial number is of fairly academic importance, as it makes little
 91+ * difference to the end user. The serial number is used by the Ogg physical
 92+ * stream to distinguish between concurrent logical streams.
 93+ *
 94+ * @return int
 95+ * @access public
 96+ */
 97+ function getSerial()
 98+ {
 99+ return ($this->_streamSerial);
 100+ }
 101+
 102+ /**
 103+ * Gives the size (in bits) of this stream.
 104+ *
 105+ * This function returns the size of the Vorbis stream within the Ogg
 106+ * physical stream.
 107+ *
 108+ * @return int
 109+ * @access public
 110+ */
 111+ function getSize()
 112+ {
 113+ return ($this->_streamSize);
 114+ }
 115+
 116+ /**
 117+ * Get the multiplexed group ID
 118+ */
 119+ function getGroup()
 120+ {
 121+ return $this->_group;
 122+ }
 123+
 124+}
 125+
 126+?>
Property changes on: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Bitstream.php
___________________________________________________________________
Added: svn:eol-style
1127 + native
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Media.php
@@ -0,0 +1,233 @@
 2+<?php
 3+/* vim: set expandtab tabstop=4 shiftwidth=4: */
 4+// +----------------------------------------------------------------------------+
 5+// | File_Ogg PEAR Package for Accessing Ogg Bitstreams |
 6+// | Copyright (c) 2005-2007 |
 7+// | David Grant <david@grant.org.uk> |
 8+// | Tim Starling <tstarling@wikimedia.org> |
 9+// +----------------------------------------------------------------------------+
 10+// | This library is free software; you can redistribute it and/or |
 11+// | modify it under the terms of the GNU Lesser General Public |
 12+// | License as published by the Free Software Foundation; either |
 13+// | version 2.1 of the License, or (at your option) any later version. |
 14+// | |
 15+// | This library is distributed in the hope that it will be useful, |
 16+// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
 17+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
 18+// | Lesser General Public License for more details. |
 19+// | |
 20+// | You should have received a copy of the GNU Lesser General Public |
 21+// | License along with this library; if not, write to the Free Software |
 22+// | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
 23+// +----------------------------------------------------------------------------+
 24+
 25+require_once('File/Ogg/Bitstream.php');
 26+
 27+/**
 28+ * Parent class for media bitstreams
 29+ * Contains some functions common to various media formats
 30+ */
 31+abstract class File_Ogg_Media extends File_Ogg_Bitstream
 32+{
 33+ /**
 34+ * Array to hold each of the comments.
 35+ *
 36+ * @access private
 37+ * @var array
 38+ */
 39+ var $_comments = array();
 40+ /**
 41+ * Vendor string for the stream.
 42+ *
 43+ * @access private
 44+ * @var string
 45+ */
 46+ var $_vendor;
 47+
 48+ /**
 49+ * Length of the stream in seconds
 50+ */
 51+ var $_streamLength;
 52+
 53+ /* Start offset of the stream in seconds */
 54+ var $_startOffset = 0;
 55+
 56+ /**
 57+ * Get a short string describing the type of the stream
 58+ * @return string
 59+ */
 60+ abstract function getType();
 61+
 62+ /**
 63+ * Get the 6-byte identification string expected in the common header
 64+ * @return string
 65+ */
 66+ function getIdentificationString()
 67+ {
 68+ return '';
 69+ }
 70+
 71+ /**
 72+ * @access private
 73+ * @param int $packetType
 74+ * @param int $pageOffset
 75+ */
 76+ function _decodeCommonHeader($packetType, $pageOffset)
 77+ {
 78+ fseek($this->_filePointer, $this->_streamData['pages'][$pageOffset]['body_offset'], SEEK_SET);
 79+ if ($packetType !== false) {
 80+ // Check if this is the correct header.
 81+ $packet = unpack("Cdata", fread($this->_filePointer, 1));
 82+ if ($packet['data'] != $packetType)
 83+ throw new PEAR_Exception("Stream Undecodable", OGG_ERROR_UNDECODABLE);
 84+
 85+ // The following six characters should be equal to getIdentificationString()
 86+ $id = $this->getIdentificationString();
 87+ if ($id !== '' && fread($this->_filePointer, strlen($id)) !== $id)
 88+ throw new PEAR_Exception("Stream is undecodable due to a malformed header.", OGG_ERROR_UNDECODABLE);
 89+ } // else seek only, no common header
 90+ }
 91+
 92+ /**
 93+ * Parse a Vorbis-style comments header.
 94+ *
 95+ * This function parses the comments header. The comments header contains a series of
 96+ * UTF-8 comments related to the audio encoded in the stream. This header also contains
 97+ * a string to identify the encoding software. More details on the comments header can
 98+ * be found at the following location: http://xiph.org/vorbis/doc/v-comment.html
 99+ *
 100+ * @access private
 101+ */
 102+ function _decodeBareCommentsHeader()
 103+ {
 104+ // Decode the vendor string length as a 32-bit unsigned integer.
 105+ $vendor_len = unpack("Vdata", fread($this->_filePointer, 4));
 106+ if ( $vendor_len['data'] > 0 ) {
 107+ // Retrieve the vendor string from the stream.
 108+ $this->_vendor = fread($this->_filePointer, $vendor_len['data']);
 109+ } else {
 110+ $this->_vendor = '';
 111+ }
 112+ // Decode the size of the comments list as a 32-bit unsigned integer.
 113+ $comment_list_length = unpack("Vdata", fread($this->_filePointer, 4));
 114+ // Iterate through the comments list.
 115+ for ($i = 0; $i < $comment_list_length['data']; ++$i) {
 116+ // Unpack the length of this comment.
 117+ $comment_length = unpack("Vdata", fread($this->_filePointer, 4));
 118+ // Comments are in the format 'ARTIST=Super Furry Animals', so split it on the equals character.
 119+ // NOTE: Equals characters are strictly prohibited in either the COMMENT or DATA parts.
 120+ $comment = explode("=", fread($this->_filePointer, $comment_length['data']));
 121+ $comment_title = (string) $comment[0];
 122+ $comment_value = (string) utf8_decode($comment[1]);
 123+
 124+ // Check if the comment type (e.g. ARTIST) already exists. If it does,
 125+ // take the new value, and the existing value (or array) and insert it
 126+ // into a new array. This is important, since each comment type may have
 127+ // multiple instances (e.g. ARTIST for a collaboration) and we should not
 128+ // overwrite the previous value.
 129+ if (isset($this->_comments[$comment_title])) {
 130+ if (is_array($this->_comments[$comment_title]))
 131+ $this->_comments[$comment_title][] = $comment_value;
 132+ else
 133+ $this->_comments[$comment_title] = array($this->_comments[$comment_title], $comment_value);
 134+ } else
 135+ $this->_comments[$comment_title] = $comment_value;
 136+ }
 137+ }
 138+
 139+ /**
 140+ * Provides a list of the comments extracted from the Vorbis stream.
 141+ *
 142+ * It is recommended that the user fully inspect the array returned by this function
 143+ * rather than blindly requesting a comment in false belief that it will always
 144+ * be present. Whilst the Vorbis specification dictates a number of popular
 145+ * comments (e.g. TITLE, ARTIST, etc.) for use in Vorbis streams, they are not
 146+ * guaranteed to appear.
 147+ *
 148+ * @access public
 149+ * @return array
 150+ */
 151+ function getCommentList()
 152+ {
 153+ return (array_keys($this->_comments));
 154+ }
 155+
 156+ /**
 157+ * Provides an interface to the numerous comments located with a Vorbis stream.
 158+ *
 159+ * A Vorbis stream may contain one or more instances of each comment, so the user
 160+ * should check the variable type before printing out the result of this method.
 161+ * The situation in which multiple instances of a comment occurring are not as
 162+ * rare as one might think, since they are conceivable at least for ARTIST comments
 163+ * in the situation where a track is a duet.
 164+ *
 165+ * @access public
 166+ * @param string $commentTitle Comment title to search for, e.g. TITLE.
 167+ * @param string $separator String to separate multiple values.
 168+ * @return string
 169+ */
 170+ function getField($commentTitle, $separator = ", ")
 171+ {
 172+ if (isset($this->_comments[$commentTitle])) {
 173+ if (is_array($this->_comments[$commentTitle]))
 174+ return (implode($separator, $this->_comments[$commentTitle]));
 175+ else
 176+ return ($this->_comments[$commentTitle]);
 177+ } else
 178+ // The comment doesn't exist in this file. The user should've called getCommentList first.
 179+ return ("");
 180+ }
 181+
 182+ /**
 183+ * Get the entire comments array.
 184+ * May return an empty array if the bitstream does not support comments.
 185+ *
 186+ * @access public
 187+ * @return array
 188+ */
 189+ function getComments() {
 190+ return $this->_comments;
 191+ }
 192+
 193+ /**
 194+ * Vendor of software used to encode this stream.
 195+ *
 196+ * Gives the vendor string for the software used to encode this stream.
 197+ * It is common to find libVorbis here. The majority of encoders appear
 198+ * to use libvorbis from Xiph.org.
 199+ *
 200+ * @access public
 201+ * @return string
 202+ */
 203+ function getVendor()
 204+ {
 205+ return ($this->_vendor);
 206+ }
 207+
 208+ /**
 209+ * Get an associative array containing header information about the stream
 210+ * @access public
 211+ * @return array
 212+ */
 213+ function getHeader()
 214+ {
 215+ return array();
 216+ }
 217+
 218+ /**
 219+ * Get the length of the stream in seconds
 220+ * @return float
 221+ */
 222+ function getLength()
 223+ {
 224+ return $this->_streamLength;
 225+ }
 226+ /**
 227+ * Get the start offset of the stream in seconds
 228+ *
 229+ * @return float
 230+ */
 231+ function getStartOffset(){
 232+ return $this->_startOffset;
 233+ }
 234+}
Property changes on: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Media.php
___________________________________________________________________
Added: svn:eol-style
1235 + native
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Theora.php
@@ -0,0 +1,241 @@
 2+<?php
 3+/* vim: set expandtab tabstop=4 shiftwidth=4: */
 4+// +----------------------------------------------------------------------------+
 5+// | File_Ogg PEAR Package for Accessing Ogg Bitstreams |
 6+// | Copyright (c) 2005-2007 |
 7+// | David Grant <david@grant.org.uk> |
 8+// | Tim Starling <tstarling@wikimedia.org> |
 9+// +----------------------------------------------------------------------------+
 10+// | This library is free software; you can redistribute it and/or |
 11+// | modify it under the terms of the GNU Lesser General Public |
 12+// | License as published by the Free Software Foundation; either |
 13+// | version 2.1 of the License, or (at your option) any later version. |
 14+// | |
 15+// | This library is distributed in the hope that it will be useful, |
 16+// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
 17+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
 18+// | Lesser General Public License for more details. |
 19+// | |
 20+// | You should have received a copy of the GNU Lesser General Public |
 21+// | License along with this library; if not, write to the Free Software |
 22+// | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
 23+// +----------------------------------------------------------------------------+
 24+
 25+require_once('File/Ogg/Bitstream.php');
 26+
 27+define( 'OGG_THEORA_IDENTIFICATION_HEADER', 0x80 );
 28+define( 'OGG_THEORA_COMMENTS_HEADER', 0x81 );
 29+define( 'OGG_THEORA_IDENTIFICATION_PAGE_OFFSET', 0 );
 30+define( 'OGG_THEORA_COMMENTS_PAGE_OFFSET', 1 );
 31+
 32+
 33+/**
 34+ * @author David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 35+ * @category File
 36+ * @copyright David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 37+ * @license http://www.gnu.org/copyleft/lesser.html GNU LGPL
 38+ * @link http://pear.php.net/package/File_Ogg
 39+ * @link http://www.xiph.org/theora/
 40+ * @package File_Ogg
 41+ * @version CVS: $Id: Theora.php,v 1.9 2005/11/16 20:43:27 djg Exp $
 42+ */
 43+class File_Ogg_Theora extends File_Ogg_Media
 44+{
 45+ /**
 46+ * @access private
 47+ */
 48+ function __construct($streamSerial, $streamData, $filePointer)
 49+ {
 50+ parent::__construct($streamSerial, $streamData, $filePointer);
 51+ $this->_decodeIdentificationHeader();
 52+ $this->_decodeCommentsHeader();
 53+ $endSec = $this->getSecondsFromGranulePos( $this->_lastGranulePos );
 54+
 55+ $startSec = $this->getSecondsFromGranulePos( $this->_firstGranulePos );
 56+
 57+ //make sure the offset is worth taking into account oggz_chop related hack
 58+ if( $startSec > 1){
 59+ $this->_streamLength = $endSec - $startSec;
 60+ $this->_startOffset = $startSec;
 61+ }else{
 62+ $this->_streamLength = $endSec;
 63+ }
 64+
 65+ $this->_avgBitrate = $this->_streamLength ? ($this->_streamSize * 8) / $this->_streamLength : 0;
 66+ }
 67+ function getSecondsFromGranulePos($granulePos){
 68+ // Calculate GranulePos seconds
 69+ // First make some "numeric strings"
 70+ // These might not fit into PHP's integer type, but they will fit into
 71+ // the 53-bit mantissa of a double-precision number
 72+ $topWord = floatval( base_convert( substr( $granulePos, 0, 8 ), 16, 10 ) );
 73+ $bottomWord = floatval( base_convert( substr( $granulePos, 8, 8 ), 16, 10 ) );
 74+ // Calculate the keyframe position by shifting right by KFGSHIFT
 75+ // We don't use PHP's shift operators because they're terribly broken
 76+ // This is made slightly simpler by the fact that KFGSHIFT < 32
 77+ $keyFramePos = $topWord / pow(2, $this->_kfgShift - 32) +
 78+ floor( $bottomWord / pow(2, $this->_kfgShift) );
 79+ // Calculate the frame offset by masking off the top 64-KFGSHIFT bits
 80+ // This requires a bit of floating point trickery
 81+ $offset = fmod( $bottomWord, pow(2, $this->_kfgShift) );
 82+ // They didn't teach you that one at school did they?
 83+ // Now put it together with the frame rate to calculate time in seconds
 84+ return ( $keyFramePos + $offset ) / $this->_frameRate;
 85+ }
 86+ /**
 87+ * Get the 6-byte identification string expected in the common header
 88+ */
 89+ function getIdentificationString()
 90+ {
 91+ return OGG_STREAM_CAPTURE_THEORA;
 92+ }
 93+
 94+ /**
 95+ * Parse the identification header in a Theora stream.
 96+ * @access private
 97+ */
 98+ function _decodeIdentificationHeader()
 99+ {
 100+ $this->_decodeCommonHeader(OGG_THEORA_IDENTIFICATION_HEADER, OGG_THEORA_IDENTIFICATION_PAGE_OFFSET);
 101+ $h = File_Ogg::_readBigEndian( $this->_filePointer, array(
 102+ 'VMAJ' => 8,
 103+ 'VMIN' => 8,
 104+ 'VREV' => 8,
 105+ 'FMBW' => 16,
 106+ 'FMBH' => 16,
 107+ 'PICW' => 24,
 108+ 'PICH' => 24,
 109+ 'PICX' => 8,
 110+ 'PICY' => 8,
 111+ 'FRN' => 32,
 112+ 'FRD' => 32,
 113+ 'PARN' => 24,
 114+ 'PARD' => 24,
 115+ 'CS' => 8,
 116+ 'NOMBR' => 24,
 117+ 'QUAL' => 6,
 118+ 'KFGSHIFT' => 5,
 119+ 'PF' => 2));
 120+ if ( !$h ) {
 121+ throw new PEAR_Exception("Stream is undecodable due to a truncated header.", OGG_ERROR_UNDECODABLE);
 122+ }
 123+
 124+ // Theora version
 125+ // Seems overly strict but this is what the spec says
 126+ // VREV is for backwards-compatible changes, apparently
 127+ if ( $h['VMAJ'] != 3 || $h['VMIN'] != 2 ) {
 128+ throw new PEAR_Exception("Stream is undecodable due to an invalid theora version.", OGG_ERROR_UNDECODABLE);
 129+ }
 130+ $this->_theoraVersion = "{$h['VMAJ']}.{$h['VMIN']}.{$h['VREV']}";
 131+
 132+ // Frame height/width
 133+ if ( !$h['FMBW'] || !$h['FMBH'] ) {
 134+ throw new PEAR_Exception("Stream is undecodable because it has frame size of zero.", OGG_ERROR_UNDECODABLE);
 135+ }
 136+ $this->_frameWidth = $h['FMBW'] * 16;
 137+ $this->_frameHeight = $h['FMBH'] * 16;
 138+
 139+ // Picture height/width
 140+ if ( $h['PICW'] > $this->_frameWidth || $h['PICH'] > $this->_frameHeight ) {
 141+ throw new PEAR_Exception("Stream is undecodable because the picture width is greater than the frame width.", OGG_ERROR_UNDECODABLE);
 142+ }
 143+ $this->_pictureWidth = $h['PICW'];
 144+ $this->_pictureHeight = $h['PICH'];
 145+
 146+ // Picture offset
 147+ $this->_offsetX = $h['PICX'];
 148+ $this->_offsetY = $h['PICY'];
 149+ // Frame rate
 150+ $this->_frameRate = $h['FRD'] == 0 ? 0 : $h['FRN'] / $h['FRD'];
 151+ // Physical aspect ratio
 152+ if ( !$h['PARN'] || !$h['PARD'] ) {
 153+ $this->_physicalAspectRatio = 1;
 154+ } else {
 155+ $this->_physicalAspectRatio = $h['PARN'] / $h['PARD'];
 156+ }
 157+
 158+ // Color space
 159+ $colorSpaces = array(
 160+ 0 => 'Undefined',
 161+ 1 => 'Rec. 470M',
 162+ 2 => 'Rec. 470BG',
 163+ );
 164+ if ( isset( $colorSpaces[$h['CS']] ) ) {
 165+ $this->_colorSpace = $colorSpaces[$h['CS']];
 166+ } else {
 167+ $this->_colorSpace = 'Unknown (reserved)';
 168+ }
 169+
 170+ $this->_nomBitrate = $h['NOMBR'];
 171+
 172+ $this->_quality = $h['QUAL'];
 173+ $this->_kfgShift = $h['KFGSHIFT'];
 174+
 175+ $pixelFormats = array(
 176+ 0 => '4:2:0',
 177+ 1 => 'Unknown (reserved)',
 178+ 2 => '4:2:2',
 179+ 3 => '4:4:4',
 180+ );
 181+ $this->_pixelFormat = $pixelFormats[$h['PF']];
 182+
 183+ switch ( $h['PF'] ) {
 184+ case 0:
 185+ $h['NSBS'] =
 186+ floor( ($h['FMBW'] + 1) / 2 ) *
 187+ floor( ($h['FMBH'] + 1) / 2 ) + 2 *
 188+ floor( ($h['FMBW'] + 3) / 4 ) *
 189+ floor( ($h['FMBH'] + 3) / 4 );
 190+ $h['NBS'] = 6 * $h['FMBW'] * $h['FMBH'];
 191+ break;
 192+ case 2:
 193+ $h['NSBS'] =
 194+ floor( ($h['FMBW'] + 1) / 2 ) *
 195+ floor( ($h['FMBH'] + 1) / 2 ) + 2 *
 196+ floor( ($h['FMBW'] + 3) / 4 ) *
 197+ floor( ($h['FMBH'] + 1) / 2 );
 198+ $h['NBS'] = 8 * $h['FMBW'] * $h['FMBH'];
 199+ break;
 200+ case 3:
 201+ $h['NSBS'] =
 202+ 3 * floor( ($h['FMBW'] + 1) / 2 ) *
 203+ floor( ($h['FMBH'] + 1) / 2 );
 204+ $h['NBS'] = 12 * $h['FMBW'] * $h['FMBH'];
 205+ break;
 206+ default:
 207+ $h['NSBS'] = $h['NBS'] = 0;
 208+ }
 209+ $h['NMBS'] = $h['FMBW'] * $h['FMBH'];
 210+
 211+ $this->_idHeader = $h;
 212+ }
 213+
 214+ /**
 215+ * Get an associative array containing header information about the stream
 216+ * @access public
 217+ * @return array
 218+ */
 219+ function getHeader() {
 220+ return $this->_idHeader;
 221+ }
 222+
 223+ /**
 224+ * Get a short string describing the type of the stream
 225+ * @return string
 226+ */
 227+ function getType() {
 228+ return 'Theora';
 229+ }
 230+
 231+ /**
 232+ * Decode the comments header
 233+ * @access private
 234+ */
 235+ function _decodeCommentsHeader()
 236+ {
 237+ $this->_decodeCommonHeader(OGG_THEORA_COMMENTS_HEADER, OGG_THEORA_COMMENTS_PAGE_OFFSET);
 238+ $this->_decodeBareCommentsHeader();
 239+ }
 240+
 241+}
 242+?>
Property changes on: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Theora.php
___________________________________________________________________
Added: svn:eol-style
1243 + native
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Speex.php
@@ -0,0 +1,123 @@
 2+<?php
 3+/* vim: set expandtab tabstop=4 shiftwidth=4: */
 4+// +----------------------------------------------------------------------------+
 5+// | File_Ogg PEAR Package for Accessing Ogg Bitstreams |
 6+// | Copyright (c) 2005-2007 |
 7+// | David Grant <david@grant.org.uk> |
 8+// | Tim Starling <tstarling@wikimedia.org> |
 9+// +----------------------------------------------------------------------------+
 10+// | This library is free software; you can redistribute it and/or |
 11+// | modify it under the terms of the GNU Lesser General Public |
 12+// | License as published by the Free Software Foundation; either |
 13+// | version 2.1 of the License, or (at your option) any later version. |
 14+// | |
 15+// | This library is distributed in the hope that it will be useful, |
 16+// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
 17+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
 18+// | Lesser General Public License for more details. |
 19+// | |
 20+// | You should have received a copy of the GNU Lesser General Public |
 21+// | License along with this library; if not, write to the Free Software |
 22+// | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
 23+// +----------------------------------------------------------------------------+
 24+
 25+require_once('File/Ogg/Media.php');
 26+
 27+/**
 28+ * @author David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 29+ * @category File
 30+ * @copyright David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 31+ * @license http://www.gnu.org/copyleft/lesser.html GNU LGPL
 32+ * @link http://pear.php.net/package/File_Ogg
 33+ * @link http://www.speex.org/docs.html
 34+ * @package File_Ogg
 35+ * @version CVS: $Id: Speex.php,v 1.10 2005/11/16 20:43:27 djg Exp $
 36+ */
 37+class File_Ogg_Speex extends File_Ogg_Media
 38+{
 39+ /**
 40+ * @access private
 41+ */
 42+ function __construct($streamSerial, $streamData, $filePointer)
 43+ {
 44+ parent::__construct($streamSerial, $streamData, $filePointer);
 45+ $this->_decodeHeader();
 46+ $this->_decodeCommentsHeader();
 47+ $endSec =
 48+ (( '0x' . substr( $this->_lastGranulePos, 0, 8 ) ) * pow(2, 32)
 49+ + ( '0x' . substr( $this->_lastGranulePos, 8, 8 ) ))
 50+ / $this->_header['rate'];
 51+
 52+ $startSec =
 53+ (( '0x' . substr( $this->_firstGranulePos, 0, 8 ) ) * pow(2, 32)
 54+ + ( '0x' . substr( $this->_firstGranulePos, 8, 8 ) ))
 55+ / $this->_header['rate'];
 56+
 57+ //make sure the offset is worth taking into account oggz_chop related hack
 58+ if( $startSec > 1){
 59+ $this->_streamLength = $endSec - $startSec;
 60+ $this->_startOffset = $startSec;
 61+ }else{
 62+ $this->_streamLength = $endSec;
 63+ }
 64+ }
 65+
 66+ /**
 67+ * Get a short string describing the type of the stream
 68+ * @return string
 69+ */
 70+ function getType()
 71+ {
 72+ return 'Speex';
 73+ }
 74+
 75+ /**
 76+ * Decode the stream header
 77+ * @access private
 78+ */
 79+ function _decodeHeader()
 80+ {
 81+ fseek($this->_filePointer, $this->_streamData['pages'][0]['body_offset'], SEEK_SET);
 82+ // The first 8 characters should be "Speex ".
 83+ if (fread($this->_filePointer, 8) != 'Speex ')
 84+ throw new PEAR_Exception("Stream is undecodable due to a malformed header.", OGG_ERROR_UNDECODABLE);
 85+
 86+ $this->_version = fread($this->_filePointer, 20);
 87+ $this->_header = File_Ogg::_readLittleEndian($this->_filePointer, array(
 88+ 'speex_version_id' => 32,
 89+ 'header_size' => 32,
 90+ 'rate' => 32,
 91+ 'mode' => 32,
 92+ 'mode_bitstream_version'=> 32,
 93+ 'nb_channels' => 32,
 94+ 'bitrate' => 32,
 95+ 'frame_size' => 32,
 96+ 'vbr' => 32,
 97+ 'frames_per_packet' => 32,
 98+ 'extra_headers' => 32,
 99+ 'reserved1' => 32,
 100+ 'reserved2' => 32
 101+ ));
 102+ $this->_header['speex_version'] = $this->_version;
 103+ }
 104+
 105+ /**
 106+ * Get an associative array containing header information about the stream
 107+ * @access public
 108+ * @return array
 109+ */
 110+ function getHeader() {
 111+ return $this->_header;
 112+ }
 113+
 114+ /**
 115+ * Decode the comments header
 116+ * @access private
 117+ */
 118+ function _decodeCommentsHeader()
 119+ {
 120+ fseek($this->_filePointer, $this->_streamData['pages'][1]['body_offset'], SEEK_SET);
 121+ $this->_decodeBareCommentsHeader();
 122+ }
 123+}
 124+?>
Property changes on: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Speex.php
___________________________________________________________________
Added: svn:eol-style
1125 + native
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Vorbis.php
@@ -0,0 +1,808 @@
 2+<?php
 3+/* vim: set expandtab tabstop=4 shiftwidth=4: */
 4+// +----------------------------------------------------------------------------+
 5+// | File_Ogg PEAR Package for Accessing Ogg Bitstreams |
 6+// | Copyright (c) 2005-2007 |
 7+// | David Grant <david@grant.org.uk> |
 8+// | Tim Starling <tstarling@wikimedia.org> |
 9+// +----------------------------------------------------------------------------+
 10+// | This library is free software; you can redistribute it and/or |
 11+// | modify it under the terms of the GNU Lesser General Public |
 12+// | License as published by the Free Software Foundation; either |
 13+// | version 2.1 of the License, or (at your option) any later version. |
 14+// | |
 15+// | This library is distributed in the hope that it will be useful, |
 16+// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
 17+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
 18+// | Lesser General Public License for more details. |
 19+// | |
 20+// | You should have received a copy of the GNU Lesser General Public |
 21+// | License along with this library; if not, write to the Free Software |
 22+// | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
 23+// +----------------------------------------------------------------------------+
 24+
 25+require_once('File/Ogg/Bitstream.php');
 26+
 27+/**
 28+ * Check number for the first header in a Vorbis stream.
 29+ *
 30+ * @access private
 31+ */
 32+define("OGG_VORBIS_IDENTIFICATION_HEADER", 1);
 33+/**
 34+ * Check number for the second header in a Vorbis stream.
 35+ *
 36+ * @access private
 37+ */
 38+define("OGG_VORBIS_COMMENTS_HEADER", 3);
 39+/**
 40+ * Check number for the third header in a Vorbis stream.
 41+ *
 42+ * @access private
 43+ */
 44+define("OGG_VORBIS_SETUP_HEADER", 5);
 45+/**
 46+ * Error thrown if the stream appears to be corrupted.
 47+ *
 48+ * @access private
 49+ */
 50+define("OGG_VORBIS_ERROR_UNDECODABLE", OGG_ERROR_UNDECODABLE);
 51+/**
 52+ * Error thrown if the user attempts to extract a comment using a comment key
 53+ * that does not exist.
 54+ *
 55+ * @access private
 56+ */
 57+define("OGG_VORBIS_ERROR_INVALID_COMMENT", 2);
 58+
 59+define("OGG_VORBIS_IDENTIFICATION_PAGE_OFFSET", 0);
 60+define("OGG_VORBIS_COMMENTS_PAGE_OFFSET", 1);
 61+
 62+/**
 63+ * Error thrown if the user attempts to write a comment containing an illegal
 64+ * character
 65+ *
 66+ * @access private
 67+ */
 68+define("OGG_VORBIS_ERROR_ILLEGAL_COMMENT", 3);
 69+
 70+/**
 71+ * Extract the contents of a Vorbis logical stream.
 72+ *
 73+ * This class provides an interface to a Vorbis logical stream found within
 74+ * a Ogg stream. A variety of information may be extracted, including comment
 75+ * tags, running time, and bitrate. For more information, please see the following
 76+ * links.
 77+ *
 78+ * @author David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 79+ * @category File
 80+ * @copyright David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 81+ * @license http://www.gnu.org/copyleft/lesser.html GNU LGPL
 82+ * @link http://pear.php.net/package/File_Ogg
 83+ * @link http://www.xiph.org/vorbis/doc/
 84+ * @package File_Ogg
 85+ * @version CVS: $Id: Vorbis.php,v 1.13 2005/11/19 09:06:32 djg Exp $
 86+ */
 87+class File_Ogg_Vorbis extends File_Ogg_Media
 88+{
 89+
 90+ /**
 91+ * Version of vorbis specification used.
 92+ *
 93+ * @access private
 94+ * @var int
 95+ */
 96+ var $_version;
 97+
 98+ /**
 99+ * Number of channels in the vorbis stream.
 100+ *
 101+ * @access private
 102+ * @var int
 103+ */
 104+ var $_channels;
 105+
 106+ /**
 107+ * Number of samples per second in the vorbis stream.
 108+ *
 109+ * @access private
 110+ * @var int
 111+ */
 112+ var $_sampleRate;
 113+
 114+ /**
 115+ * Minimum bitrate for the vorbis stream.
 116+ *
 117+ * @access private
 118+ * @var int
 119+ */
 120+ var $_minBitrate;
 121+
 122+ /**
 123+ * Maximum bitrate for the vorbis stream.
 124+ *
 125+ * @access private
 126+ * @var int
 127+ */
 128+ var $_maxBitrate;
 129+
 130+ /**
 131+ * Nominal bitrate for the vorbis stream.
 132+ *
 133+ * @access private
 134+ * @var int
 135+ */
 136+ var $_nomBitrate;
 137+
 138+ /**
 139+ * Average bitrate for the vorbis stream.
 140+ *
 141+ * @access private
 142+ * @var float
 143+ */
 144+ var $_avgBitrate;
 145+
 146+ /**
 147+ * The length of this stream in seconds.
 148+ *
 149+ * @access private
 150+ * @var int
 151+ */
 152+ var $_streamLength;
 153+
 154+ /**
 155+ * the start offset of this stream in seconds
 156+ */
 157+ var $_startOffset;
 158+ /**
 159+ * Constructor for accessing a Vorbis logical stream.
 160+ *
 161+ * This method is the constructor for the native-PHP interface to a Vorbis logical
 162+ * stream, embedded within an Ogg physical stream.
 163+ *
 164+ * @param int $streamSerial Serial number of the logical stream.
 165+ * @param array $streamData Data for the requested logical stream.
 166+ * @param string $filePath Location of a file on the filesystem.
 167+ * @param pointer $filePointer File pointer for the current physical stream.
 168+ * @access private
 169+ */
 170+ function __construct($streamSerial, $streamData, $filePointer)
 171+ {
 172+ parent::__construct($streamSerial, $streamData, $filePointer);
 173+ $this->_decodeIdentificationHeader();
 174+ $this->_decodeCommentsHeader(OGG_VORBIS_COMMENTS_HEADER, OGG_VORBIS_COMMENTS_PAGE_OFFSET);
 175+
 176+ $endSec = $this->getSecondsFromGranulePos( $this->_lastGranulePos );
 177+ $startSec = $this->getSecondsFromGranulePos( $this->_firstGranulePos );
 178+
 179+ //make sure the offset is worth taking into account oggz_chop related hack
 180+ if( $startSec > 1){
 181+ $this->_streamLength = $endSec - $startSec;
 182+ $this->_startOffset = $startSec;
 183+ }else{
 184+ $this->_streamLength = $endSec;
 185+ }
 186+
 187+ $this->_avgBitrate = $this->_streamLength ? ($this->_streamSize * 8) / $this->_streamLength : 0;
 188+ }
 189+ function getSecondsFromGranulePos( $granulePos ){
 190+ return (( '0x' . substr( $granulePos, 0, 8 ) ) * pow(2, 32)
 191+ + ( '0x' . substr( $granulePos, 8, 8 ) ))
 192+ / $this->_idHeader['audio_sample_rate'];
 193+ }
 194+ /**
 195+ * Get a short string describing the type of the stream
 196+ */
 197+ function getType()
 198+ {
 199+ return 'Vorbis';
 200+ }
 201+
 202+ /**
 203+ * Parse the identification header (the first of three headers) in a Vorbis stream.
 204+ *
 205+ * This function parses the identification header. The identification header
 206+ * contains simple audio characteristics, such as sample rate and number of
 207+ * channels. There are a number of error-checking provisions laid down in the Vorbis
 208+ * specification to ensure the stream is pure.
 209+ *
 210+ * @access private
 211+ */
 212+ function _decodeIdentificationHeader()
 213+ {
 214+ $this->_decodeCommonHeader(OGG_VORBIS_IDENTIFICATION_HEADER, OGG_VORBIS_IDENTIFICATION_PAGE_OFFSET);
 215+
 216+ $h = File_Ogg::_readLittleEndian($this->_filePointer, array(
 217+ 'vorbis_version' => 32,
 218+ 'audio_channels' => 8,
 219+ 'audio_sample_rate' => 32,
 220+ 'bitrate_maximum' => 32,
 221+ 'bitrate_nominal' => 32,
 222+ 'bitrate_minimum' => 32,
 223+ 'blocksize_0' => 4,
 224+ 'blocksize_1' => 4,
 225+ 'framing_flag' => 1
 226+ ));
 227+
 228+ // The Vorbis stream version must be 0.
 229+ if ($h['vorbis_version'] == 0)
 230+ $this->_version = $h['vorbis_version'];
 231+ else
 232+ throw new PEAR_Exception("Stream is undecodable due to an invalid vorbis stream version.", OGG_VORBIS_ERROR_UNDECODABLE);
 233+
 234+ // The number of channels MUST be greater than 0.
 235+ if ($h['audio_channels'] == 0)
 236+ throw new PEAR_Exception("Stream is undecodable due to zero channels.", OGG_VORBIS_ERROR_UNDECODABLE);
 237+ else
 238+ $this->_channels = $h['audio_channels'];
 239+
 240+ // The sample rate MUST be greater than 0.
 241+ if ($h['audio_sample_rate'] == 0)
 242+ throw new PEAR_Exception("Stream is undecodable due to a zero sample rate.", OGG_VORBIS_ERROR_UNDECODABLE);
 243+ else
 244+ $this->_sampleRate = $h['audio_sample_rate'];
 245+
 246+ // Extract the various bitrates
 247+ $this->_maxBitrate = $h['bitrate_maximum'];
 248+ $this->_nomBitrate = $h['bitrate_nominal'];
 249+ $this->_minBitrate = $h['bitrate_minimum'];
 250+
 251+ // Powers of two between 6 and 13 inclusive.
 252+ $valid_block_sizes = array(64, 128, 256, 512, 1024, 2048, 4096, 8192);
 253+
 254+ // blocksize_0 MUST be a valid blocksize.
 255+ $blocksize_0 = pow(2, $h['blocksize_0']);
 256+ if (FALSE == in_array($blocksize_0, $valid_block_sizes))
 257+ throw new PEAR_Exception("Stream is undecodable because blocksize_0 is $blocksize_0, which is not a valid size.", OGG_VORBIS_ERROR_UNDECODABLE);
 258+
 259+ // Extract bits 5 to 8 from the character data.
 260+ // blocksize_1 MUST be a valid blocksize.
 261+ $blocksize_1 = pow(2, $h['blocksize_1']);
 262+ if (FALSE == in_array($blocksize_1, $valid_block_sizes))
 263+ throw new PEAR_Exception("Stream is undecodable because blocksize_1 is not a valid size.", OGG_VORBIS_ERROR_UNDECODABLE);
 264+
 265+ // blocksize 0 MUST be less than or equal to blocksize 1.
 266+ if ($blocksize_0 > $blocksize_1)
 267+ throw new PEAR_Exception("Stream is undecodable because blocksize_0 is not less than or equal to blocksize_1.", OGG_VORBIS_ERROR_UNDECODABLE);
 268+
 269+ // The framing bit MUST be set to mark the end of the identification header.
 270+ // Some encoders are broken though -- TS
 271+ /*
 272+ if ($h['framing_flag'] == 0)
 273+ throw new PEAR_Exception("Stream in undecodable because the framing bit is not non-zero.", OGG_VORBIS_ERROR_UNDECODABLE);
 274+ */
 275+
 276+ $this->_idHeader = $h;
 277+ }
 278+
 279+ /**
 280+ * Decode the comments header
 281+ * @access private
 282+ * @param int $packetType
 283+ * @param int $pageOffset
 284+ */
 285+ function _decodeCommentsHeader($packetType, $pageOffset)
 286+ {
 287+ $this->_decodeCommonHeader($packetType, $pageOffset);
 288+ $this->_decodeBareCommentsHeader();
 289+ // The framing bit MUST be set to mark the end of the comments header.
 290+ $framing_bit = unpack("Cdata", fread($this->_filePointer, 1));
 291+ if ($framing_bit['data'] != 1)
 292+ throw new PEAR_Exception("Stream Undecodable", OGG_VORBIS_ERROR_UNDECODABLE);
 293+ }
 294+
 295+ /**
 296+ * Get the 6-byte identification string expected in the common header
 297+ */
 298+ function getIdentificationString() {
 299+ return OGG_STREAM_CAPTURE_VORBIS;
 300+ }
 301+
 302+ /**
 303+ * Version of the Vorbis specification referred to in the encoding of this stream.
 304+ *
 305+ * This method returns the version of the Vorbis specification (currently 0 (ZERO))
 306+ * referred to by the encoder of this stream. The Vorbis specification is well-
 307+ * defined, and thus one does not expect this value to change on a frequent basis.
 308+ *
 309+ * @access public
 310+ * @return int
 311+ */
 312+ function getEncoderVersion()
 313+ {
 314+ return ($this->_version);
 315+ }
 316+
 317+ /**
 318+ * Number of channels used in this stream
 319+ *
 320+ * This function returns the number of channels used in this stream. This
 321+ * can range from 1 to 255, but will likely be 2 (stereo) or 1 (mono).
 322+ *
 323+ * @access public
 324+ * @return int
 325+ * @see File_Ogg_Vorbis::isMono()
 326+ * @see File_Ogg_Vorbis::isStereo()
 327+ * @see File_Ogg_Vorbis::isQuadrophonic()
 328+ */
 329+ function getChannels()
 330+ {
 331+ return ($this->_channels);
 332+ }
 333+
 334+ /**
 335+ * Samples per second.
 336+ *
 337+ * This function returns the number of samples used per second in this
 338+ * recording. Probably the most common value here is 44,100.
 339+ *
 340+ * @return int
 341+ * @access public
 342+ */
 343+ function getSampleRate()
 344+ {
 345+ return ($this->_sampleRate);
 346+ }
 347+
 348+ /**
 349+ * Various bitrate measurements
 350+ *
 351+ * Gives an array of the values of four different types of bitrates for this
 352+ * stream. The nominal, maximum and minimum values are found within the file,
 353+ * whereas the average value is computed.
 354+ *
 355+ * @access public
 356+ * @return array
 357+ */
 358+ function getBitrates()
 359+ {
 360+ return (array("nom" => $this->_nomBitrate, "max" => $this->_maxBitrate, "min" => $this->_minBitrate, "avg" => $this->_avgBitrate));
 361+ }
 362+
 363+ /**
 364+ * Gives the most accurate bitrate measurement from this stream.
 365+ *
 366+ * This function returns the most accurate bitrate measurement for this
 367+ * recording, depending on values set in the stream header.
 368+ *
 369+ * @access public
 370+ * @return float
 371+ */
 372+ function getBitrate()
 373+ {
 374+ if ($this->_avgBitrate != 0)
 375+ return ($this->_avgBitrate);
 376+ elseif ($this->_nomBitrate != 0)
 377+ return ($this->_nomBitrate);
 378+ else
 379+ return (($this->_minBitrate + $this->_maxBitrate) / 2);
 380+ }
 381+
 382+ /**
 383+ * Gives the length (in seconds) of this stream.
 384+ *
 385+ * @access public
 386+ * @return int
 387+ */
 388+ function getLength()
 389+ {
 390+ return ($this->_streamLength);
 391+ }
 392+ /**
 393+ * Get the start offset of the stream in seconds
 394+ * @access public
 395+ * @return int
 396+ */
 397+ function getStartOffset(){
 398+ return ($this->_startOffset);
 399+ }
 400+ /**
 401+ * States whether this logical stream was encoded in mono.
 402+ *
 403+ * @access public
 404+ * @return boolean
 405+ */
 406+ function isMono()
 407+ {
 408+ return ($this->_channels == 1);
 409+ }
 410+
 411+ /**
 412+ * States whether this logical stream was encoded in stereo.
 413+ *
 414+ * @access public
 415+ * @return boolean
 416+ */
 417+ function isStereo()
 418+ {
 419+ return ($this->_channels == 2);
 420+ }
 421+
 422+ /**
 423+ * States whether this logical stream was encoded in quadrophonic sound.
 424+ *
 425+ * @access public
 426+ * @return boolean
 427+ */
 428+ function isQuadrophonic()
 429+ {
 430+ return ($this->_channels == 4);
 431+ }
 432+
 433+ /**
 434+ * The title of this track, e.g. "What's Up Pussycat?".
 435+ *
 436+ * @access public
 437+ * @return string
 438+ */
 439+ function getTitle()
 440+ {
 441+ return ($this->getField("TITLE"));
 442+ }
 443+
 444+ /**
 445+ * Set the title of this track.
 446+ *
 447+ * @access public
 448+ * @param string $title
 449+ * @param boolean $replace
 450+ */
 451+ function setTitle($title, $replace = true)
 452+ {
 453+ $this->setField("TITLE", $title, $replace);
 454+ }
 455+
 456+ /**
 457+ * The version of the track, such as a remix.
 458+ *
 459+ * @access public
 460+ * @return string
 461+ */
 462+ function getVersion()
 463+ {
 464+ return $this->getField("VERSION");
 465+ }
 466+
 467+ /**
 468+ * Set the version of this track.
 469+ *
 470+ * @access public
 471+ * @param string $version
 472+ * @param boolean $replace
 473+ */
 474+ function setVersion($version, $replace = true)
 475+ {
 476+ $this->setField("VERSION", $version, $replace);
 477+ }
 478+
 479+ /**
 480+ * The album or collection from which this track comes.
 481+ *
 482+ * @access public
 483+ * @return string
 484+ */
 485+ function getAlbum()
 486+ {
 487+ return ($this->getField("ALBUM"));
 488+ }
 489+
 490+ /**
 491+ * Set the album or collection for this track.
 492+ *
 493+ * @access public
 494+ * @param string $album
 495+ * @param boolean $replace
 496+ */
 497+ function setAlbum($album, $replace = true)
 498+ {
 499+ $this->setField("ALBUM", $album, $replace);
 500+ }
 501+
 502+ /**
 503+ * The number of this track if it is part of a larger collection.
 504+ *
 505+ * @access public
 506+ * @return string
 507+ */
 508+ function getTrackNumber()
 509+ {
 510+ return ($this->getField("TRACKNUMBER"));
 511+ }
 512+
 513+ /**
 514+ * Set the number of this relative to the collection.
 515+ *
 516+ * @access public
 517+ * @param int $number
 518+ * @param boolean $replace
 519+ */
 520+ function setTrackNumber($number, $replace = true)
 521+ {
 522+ $this->setField("TRACKNUMBER", $number, $replace);
 523+ }
 524+
 525+ /**
 526+ * The artist responsible for this track.
 527+ *
 528+ * This function returns the name of the artist responsible for this
 529+ * recording, which may be either a solo-artist, duet or group.
 530+ *
 531+ * @access public
 532+ * @return string
 533+ */
 534+ function getArtist()
 535+ {
 536+ return ($this->getField("ARTIST"));
 537+ }
 538+
 539+ /**
 540+ * Set the artist of this track.
 541+ *
 542+ * @access public
 543+ * @param string $artist
 544+ * @param boolean $replace
 545+ */
 546+ function setArtist($artist, $replace = true)
 547+ {
 548+ $this->setField("ARTIST", $artist, $replace = true);
 549+ }
 550+
 551+ /**
 552+ * The performer of this track, such as an orchestra
 553+ *
 554+ * @access public
 555+ * @return string
 556+ */
 557+ function getPerformer()
 558+ {
 559+ return ($this->getField("PERFORMER"));
 560+ }
 561+
 562+ /**
 563+ * Set the performer of this track.
 564+ *
 565+ * @access public
 566+ * @param string $performer
 567+ * @param boolean $replace
 568+ */
 569+ function setPerformer($performer, $replace = true)
 570+ {
 571+ $this->setField("PERFORMER", $performer, $replace);
 572+ }
 573+
 574+ /**
 575+ * The copyright attribution for this track.
 576+ *
 577+ * @access public
 578+ * @return string
 579+ */
 580+ function getCopyright()
 581+ {
 582+ return ($this->getField("COPYRIGHT"));
 583+ }
 584+
 585+ /**
 586+ * Set the copyright attribution for this track.
 587+ *
 588+ * @access public
 589+ * @param string $copyright
 590+ * @param boolean $replace
 591+ */
 592+ function setCopyright($copyright, $replace = true)
 593+ {
 594+ $this->setField("COPYRIGHT", $copyright, $replace);
 595+ }
 596+
 597+ /**
 598+ * The rights of distribution for this track.
 599+ *
 600+ * This funtion returns the license for this track, and may include
 601+ * copyright information, or a creative commons statement.
 602+ *
 603+ * @access public
 604+ * @return string
 605+ */
 606+ function getLicense()
 607+ {
 608+ return ($this->getField("LICENSE"));
 609+ }
 610+
 611+ /**
 612+ * Set the distribution rights for this track.
 613+ *
 614+ * @access public
 615+ * @param string $license
 616+ * @param boolean $replace
 617+ */
 618+ function setLicense($license, $replace = true)
 619+ {
 620+ $this->setField("LICENSE", $license, $replace);
 621+ }
 622+
 623+ /**
 624+ * The organisation responsible for this track.
 625+ *
 626+ * This function returns the name of the organisation responsible for
 627+ * the production of this track, such as the record label.
 628+ *
 629+ * @access public
 630+ * @return string
 631+ */
 632+ function getOrganization()
 633+ {
 634+ return ($this->getField("ORGANIZATION"));
 635+ }
 636+
 637+ /**
 638+ * Set the organisation responsible for this track.
 639+ *
 640+ * @access public
 641+ * @param string $organization
 642+ * @param boolean $replace
 643+ */
 644+ function setOrganziation($organization, $replace = true)
 645+ {
 646+ $this->setField("ORGANIZATION", $organization, $replace);
 647+ }
 648+
 649+ /**
 650+ * A short description of the contents of this track.
 651+ *
 652+ * This function returns a short description of this track, which might
 653+ * contain extra information that doesn't fit anywhere else.
 654+ *
 655+ * @access public
 656+ * @return string
 657+ */
 658+ function getDescription()
 659+ {
 660+ return ($this->getField("DESCRIPTION"));
 661+ }
 662+
 663+ /**
 664+ * Set the description of this track.
 665+ *
 666+ * @access public
 667+ * @param string $description
 668+ * @param boolean $replace
 669+ */
 670+ function setDescription($description, $replace = true)
 671+ {
 672+ $this->setField("DESCRIPTION", $replace);
 673+ }
 674+
 675+ /**
 676+ * The genre of this recording (e.g. Rock)
 677+ *
 678+ * This function returns the genre of this recording. There are no pre-
 679+ * defined genres, so this is completely up to the tagging software.
 680+ *
 681+ * @access public
 682+ * @return string
 683+ */
 684+ function getGenre()
 685+ {
 686+ return ($this->getField("GENRE"));
 687+ }
 688+
 689+ /**
 690+ * Set the genre of this track.
 691+ *
 692+ * @access public
 693+ * @param string $genre
 694+ * @param boolean $replace
 695+ */
 696+ function setGenre($genre, $replace = true)
 697+ {
 698+ $this->setField("GENRE", $genre, $replace);
 699+ }
 700+
 701+ /**
 702+ * The date of the recording of this track.
 703+ *
 704+ * This function returns the date on which this recording was made. There
 705+ * is no specification for the format of this date.
 706+ *
 707+ * @access public
 708+ * @return string
 709+ */
 710+ function getDate()
 711+ {
 712+ return ($this->getField("DATE"));
 713+ }
 714+
 715+ /**
 716+ * Set the date of recording for this track.
 717+ *
 718+ * @access public
 719+ * @param string $date
 720+ * @param boolean $replace
 721+ */
 722+ function setDate($date, $replace = true)
 723+ {
 724+ $this->setField("DATE", $date, $replace);
 725+ }
 726+
 727+ /**
 728+ * Where this recording was made.
 729+ *
 730+ * This function returns where this recording was made, such as a recording
 731+ * studio, or concert venue.
 732+ *
 733+ * @access public
 734+ * @return string
 735+ */
 736+ function getLocation()
 737+ {
 738+ return ($this->getField("LOCATION"));
 739+ }
 740+
 741+ /**
 742+ * Set the location of the recording of this track.
 743+ *
 744+ * @access public
 745+ * @param string $location
 746+ * @param boolean $replace
 747+ */
 748+ function setLocation($location, $replace = true)
 749+ {
 750+ $this->setField("LOCATION", $location, $replace);
 751+ }
 752+
 753+ /**
 754+ * @access public
 755+ * @return string
 756+ */
 757+ function getContact()
 758+ {
 759+ return ($this->getField("CONTACT"));
 760+ }
 761+
 762+ /**
 763+ * Set the contact information for this track.
 764+ *
 765+ * @access public
 766+ * @param string $contact
 767+ * @param boolean $replace
 768+ */
 769+ function setContact($contact, $replace = true)
 770+ {
 771+ $this->setField("CONTACT", $contact, $replace);
 772+ }
 773+
 774+ /**
 775+ * International Standard Recording Code.
 776+ *
 777+ * Returns the International Standard Recording Code. This code can be
 778+ * validated using the Validate_ISPN package.
 779+ *
 780+ * @access public
 781+ * @return string
 782+ */
 783+ function getIsrc()
 784+ {
 785+ return ($this->getField("ISRC"));
 786+ }
 787+
 788+ /**
 789+ * Set the ISRC for this track.
 790+ *
 791+ * @access public
 792+ * @param string $isrc
 793+ * @param boolean $replace
 794+ */
 795+ function setIsrc($isrc, $replace = true)
 796+ {
 797+ $this->setField("ISRC", $isrc, $replace);
 798+ }
 799+
 800+ /**
 801+ * Get an associative array containing header information about the stream
 802+ * @access public
 803+ * @return array
 804+ */
 805+ function getHeader() {
 806+ return $this->_idHeader;
 807+ }
 808+}
 809+?>
Property changes on: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Vorbis.php
___________________________________________________________________
Added: svn:eol-style
1810 + native
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Flac.php
@@ -0,0 +1,134 @@
 2+<?php
 3+/* vim: set expandtab tabstop=4 shiftwidth=4: */
 4+// +----------------------------------------------------------------------------+
 5+// | File_Ogg PEAR Package for Accessing Ogg Bitstreams |
 6+// | Copyright (c) 2005-2007 |
 7+// | David Grant <david@grant.org.uk> |
 8+// | Tim Starling <tstarling@wikimedia.org> |
 9+// +----------------------------------------------------------------------------+
 10+// | This library is free software; you can redistribute it and/or |
 11+// | modify it under the terms of the GNU Lesser General Public |
 12+// | License as published by the Free Software Foundation; either |
 13+// | version 2.1 of the License, or (at your option) any later version. |
 14+// | |
 15+// | This library is distributed in the hope that it will be useful, |
 16+// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
 17+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
 18+// | Lesser General Public License for more details. |
 19+// | |
 20+// | You should have received a copy of the GNU Lesser General Public |
 21+// | License along with this library; if not, write to the Free Software |
 22+// | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
 23+// +----------------------------------------------------------------------------+
 24+
 25+require_once('File/Ogg/Media.php');
 26+
 27+/**
 28+ * @author David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 29+ * @category File
 30+ * @copyright David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
 31+ * @license http://www.gnu.org/copyleft/lesser.html GNU LGPL
 32+ * @link http://pear.php.net/package/File_Ogg
 33+ * @link http://flac.sourceforge.net/documentation.html
 34+ * @package File_Ogg
 35+ * @version CVS: $Id: Flac.php,v 1.9 2005/11/16 20:43:27 djg Exp $
 36+ */
 37+class File_Ogg_Flac extends File_Ogg_Media
 38+{
 39+ /**
 40+ * @access private
 41+ */
 42+ function __construct($streamSerial, $streamData, $filePointer)
 43+ {
 44+ parent::__construct($streamSerial, $streamData, $filePointer);
 45+ $this->_decodeHeader();
 46+ $this->_decodeCommentsHeader();
 47+ $this->_streamLength = $this->_streamInfo['total_samples']
 48+ / $this->_streamInfo['sample_rate'];
 49+ }
 50+
 51+ /**
 52+ * Get a short string describing the type of the stream
 53+ * @return string
 54+ */
 55+ function getType() {
 56+ return 'FLAC';
 57+ }
 58+
 59+ /**
 60+ * @access private
 61+ * @param int $packetType
 62+ * @param int $pageOffset
 63+ */
 64+ function _decodeHeader()
 65+ {
 66+ fseek($this->_filePointer, $this->_streamData['pages'][0]['body_offset'], SEEK_SET);
 67+ // Check if this is the correct header.
 68+ $packet = unpack("Cdata", fread($this->_filePointer, 1));
 69+ if ($packet['data'] != 0x7f)
 70+ throw new PEAR_Exception("Stream Undecodable", OGG_ERROR_UNDECODABLE);
 71+
 72+ // The following four characters should be "FLAC".
 73+ if (fread($this->_filePointer, 4) != 'FLAC')
 74+ throw new PEAR_Exception("Stream is undecodable due to a malformed header.", OGG_ERROR_UNDECODABLE);
 75+
 76+ $version = unpack("Cmajor/Cminor", fread($this->_filePointer, 2));
 77+ $this->_version = "{$version['major']}.{$version['minor']}";
 78+ if ($version['major'] > 1) {
 79+ throw new PEAR_Exception("Cannot decode a version {$version['major']} FLAC stream", OGG_ERROR_UNDECODABLE);
 80+ }
 81+ $h = File_Ogg::_readBigEndian( $this->_filePointer,
 82+ array(
 83+ // Ogg-specific
 84+ 'num_headers' => 16,
 85+ 'flac_native_sig' => 32,
 86+ // METADATA_BLOCK_HEADER
 87+ 'is_last' => 1,
 88+ 'type' => 7,
 89+ 'length' => 24,
 90+ ));
 91+
 92+ // METADATA_BLOCK_STREAMINFO
 93+ // The variable names are canonical, and come from the FLAC source (format.h)
 94+ $this->_streamInfo = File_Ogg::_readBigEndian( $this->_filePointer,
 95+ array(
 96+ 'min_blocksize' => 16,
 97+ 'max_blocksize' => 16,
 98+ 'min_framesize' => 24,
 99+ 'max_framesize' => 24,
 100+ 'sample_rate' => 20,
 101+ 'channels' => 3,
 102+ 'bits_per_sample' => 5,
 103+ 'total_samples' => 36,
 104+ ));
 105+ $this->_streamInfo['md5sum'] = bin2hex(fread($this->_filePointer, 16));
 106+ }
 107+
 108+ /**
 109+ * Get an associative array containing header information about the stream
 110+ * @access public
 111+ * @return array
 112+ */
 113+ function getHeader()
 114+ {
 115+ return $this->_streamInfo;
 116+ }
 117+
 118+ function _decodeCommentsHeader()
 119+ {
 120+ fseek($this->_filePointer, $this->_streamData['pages'][1]['body_offset'], SEEK_SET);
 121+ $blockHeader = File_Ogg::_readBigEndian( $this->_filePointer,
 122+ array(
 123+ 'last_block' => 1,
 124+ 'block_type' => 7,
 125+ 'length' => 24
 126+ )
 127+ );
 128+ if ($blockHeader['block_type'] != 4) {
 129+ throw new PEAR_Exception("Stream Undecodable", OGG_ERROR_UNDECODABLE);
 130+ }
 131+
 132+ $this->_decodeBareCommentsHeader();
 133+ }
 134+}
 135+?>
Property changes on: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/Ogg/Flac.php
___________________________________________________________________
Added: svn:eol-style
1136 + native
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/README
@@ -0,0 +1,4 @@
 2+This was originally a fork of the File_Ogg package in PEAR. I (TS) have now taken
 3+over maintainership of that package, and have merged my work back to there,
 4+starting with version 0.3.0.
 5+
Property changes on: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/File/README
___________________________________________________________________
Added: svn:eol-style
16 + native
Index: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/package.xml
@@ -0,0 +1,149 @@
 2+<?xml version="1.0" encoding="UTF-8"?>
 3+<package packagerversion="1.6.1" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
 4+ <name>File_Ogg</name>
 5+ <channel>pear.php.net</channel>
 6+ <summary>Retrieves metadata from Ogg files.</summary>
 7+ <description>Decodes Ogg file headers to provide metadata. Supports Vorbis,
 8+Theora, Speex and FLAC stream headers. This package does
 9+NOT provide codecs for these formats.
 10+ </description>
 11+ <lead>
 12+ <name>Tim Starling</name>
 13+ <user>tstarling</user>
 14+ <email>tstarling@wikimedia.org</email>
 15+ <active>yes</active>
 16+ </lead>
 17+ <date>2008-01-31</date>
 18+ <time>15:25:07</time>
 19+ <version>
 20+ <release>0.3.0</release>
 21+ <api>0.3.0</api>
 22+ </version>
 23+ <stability>
 24+ <release>beta</release>
 25+ <api>beta</api>
 26+ </stability>
 27+ <license>GNU LGPL</license>
 28+ <notes>* Change in maintainership
 29+* Added header parsing for Theora, Speex and Flac.
 30+* Removed partially written save/encode code.
 31+* Introduced some PHP 5 features, no longer supports PHP 4.
 32+* Introduced convenience functions getHeader() and getComments() to retrieve metadata in associative array format.
 33+* Fixed the error handling, using exceptions instead of inappropriate continuation.
 34+* Added overall length calculation for multiplexed streams
 35+* Fixed a page splitting bug
 36+
 37+ </notes>
 38+ <contents>
 39+ <dir name="/">
 40+ <dir name="File">
 41+ <dir name="Ogg">
 42+ <file name="Bitstream.php" role="php" />
 43+ <file name="Flac.php" role="php" />
 44+ <file name="Media.php" role="php" />
 45+ <file name="Speex.php" role="php" />
 46+ <file name="Theora.php" role="php" />
 47+ <file name="Vorbis.php" role="php" />
 48+ </dir> <!-- //File/Ogg -->
 49+ <file name="Ogg.php" role="php" />
 50+ </dir> <!-- //File -->
 51+ </dir> <!-- / -->
 52+ </contents>
 53+ <dependencies>
 54+ <required>
 55+ <php>
 56+ <min>5.0.0</min>
 57+ </php>
 58+ <pearinstaller>
 59+ <min>1.4.0b1</min>
 60+ </pearinstaller>
 61+ </required>
 62+ </dependencies>
 63+ <phprelease />
 64+ <changelog>
 65+ <release>
 66+ <version>
 67+ <release>0.2.1</release>
 68+ <api>0.2.1</api>
 69+ </version>
 70+ <stability>
 71+ <release>beta</release>
 72+ <api>beta</api>
 73+ </stability>
 74+ <date>2005-11-03</date>
 75+ <license>GNU LGPL</license>
 76+ <notes>Fixed some basic bugs from the previous release.
 77+ </notes>
 78+ </release>
 79+ <release>
 80+ <version>
 81+ <release>0.2.0</release>
 82+ <api>0.2.0</api>
 83+ </version>
 84+ <stability>
 85+ <release>beta</release>
 86+ <api>beta</api>
 87+ </stability>
 88+ <date>2005-11-03</date>
 89+ <license>GNU LGPL</license>
 90+ <notes>Updated to beta release, changed license to LGPL.
 91+ </notes>
 92+ </release>
 93+ <release>
 94+ <version>
 95+ <release>0.1.3</release>
 96+ <api>0.1.3</api>
 97+ </version>
 98+ <stability>
 99+ <release>alpha</release>
 100+ <api>alpha</api>
 101+ </stability>
 102+ <date>2005-09-21</date>
 103+ <license>GNU LGPL</license>
 104+ <notes>Change to package.xml.
 105+ </notes>
 106+ </release>
 107+ <release>
 108+ <version>
 109+ <release>0.1.2</release>
 110+ <api>0.1.2</api>
 111+ </version>
 112+ <stability>
 113+ <release>alpha</release>
 114+ <api>alpha</api>
 115+ </stability>
 116+ <date>2003-09-09</date>
 117+ <license>GNU LGPL</license>
 118+ <notes>Extensive documentation for the majority of PHP elements.
 119+ </notes>
 120+ </release>
 121+ <release>
 122+ <version>
 123+ <release>0.1.1</release>
 124+ <api>0.1.1</api>
 125+ </version>
 126+ <stability>
 127+ <release>alpha</release>
 128+ <api>alpha</api>
 129+ </stability>
 130+ <date>2003-09-02</date>
 131+ <license>GNU LGPL</license>
 132+ <notes>Bugfix release: package was installing in the wrong location.
 133+ </notes>
 134+ </release>
 135+ <release>
 136+ <version>
 137+ <release>0.1</release>
 138+ <api>0.1</api>
 139+ </version>
 140+ <stability>
 141+ <release>alpha</release>
 142+ <api>alpha</api>
 143+ </stability>
 144+ <date>2003-09-02</date>
 145+ <license>GNU LGPL</license>
 146+ <notes>The first release!
 147+ </notes>
 148+ </release>
 149+ </changelog>
 150+</package>
Property changes on: trunk/extensions/TimedMediaHandler/handlers/OggHandler/PEAR/File_Ogg/package.xml
___________________________________________________________________
Added: svn:eol-style
1151 + native
Index: trunk/extensions/TimedMediaHandler/TimedMediaHandler.php
@@ -32,15 +32,15 @@
3333 $wgAutoloadClasses['TimedMediaThumbnail'] = "$timedMediaDir/TimedMediaThumbnail.php";
3434
3535 // Ogg Handler
36 -$wgAutoloadClasses['OggHandler'] = "$timedMediaDir/OggHandler/OggHandler.php";
 36+$wgAutoloadClasses['OggHandler'] = "$timedMediaDir/handlers/OggHandler/OggHandler.php";
3737 ini_set( 'include_path',
38 - "$timedMediaDir/OggHandler/PEAR/File_Ogg" .
 38+ "$timedMediaDir/handlers/OggHandler/PEAR/File_Ogg" .
3939 PATH_SEPARATOR .
4040 ini_get( 'include_path' ) );
4141
4242 // WebM Handler
43 -$wgAutoloadClasses['WebMHandler'] = "$timedMediaDir/WebMHandler/WebMHandler.php";
44 -$wgAutoloadClasses['getID3' ] = "$timedMediaDir/WebMHandler/getid3.php";
 43+$wgAutoloadClasses['WebMHandler'] = "$timedMediaDir/handlers/WebMHandler/WebMHandler.php";
 44+$wgAutoloadClasses['getID3' ] = "$timedMediaDir/handlers/WebMHandler/getid3.php";
4545
4646 $wgAutoloadClasses['WebVideoTranscode'] = "$timedMediaDir/WebVideoTranscode/WebVideoTranscode.php";
4747 $wgAutoloadClasses['WebVideoTranscodeJob'] = "$timedMediaDir/WebVideoTranscode/WebVideoTranscodeJob.php";

Status & tagging log