T350: Upgraded getID3.

This commit is contained in:
Peter Deltchev 2015-09-12 14:43:01 -07:00
parent 2c95622c1d
commit 7147d06ee7
113 changed files with 21291 additions and 4063 deletions

607
app/Library/getid3/README.md Executable file
View file

@ -0,0 +1,607 @@
getID3() by James Heinrich (<info@getid3.org>)
===
**Available at <http://getid3.sourceforge.net> or <http://www.getid3.org>**
getID3() is released under multiple licenses. You may choose from the following licenses, and use getID3 according to the terms of the license most suitable to your project.
**GNU GPL:**
* [v3](https://gnu.org/licenses/gpl.html)
* [v2](https://gnu.org/licenses/old-licenses/gpl-2.0.html)
* [v1](https://gnu.org/licenses/old-licenses/gpl-1.0.html)
**GNU LGPL:**
* [v3](https://gnu.org/licenses/lgpl.html)
**Mozilla MPL:**
* [v2](http://www.mozilla.org/MPL/2.0/)
**getID3 Commercial License:**
* [gCL](http://getid3.org/#gCL) (payment required)
* * *
Copies of each of the above licenses are included in the `licenses/`
directory of the getID3 distribution.
If you want to donate, there is a link on <http://www.getid3.org> for PayPal donations.
Quick Start
===
**Q:** How can I check that getID3() works on my server/files?
**A:** Unzip getID3() to a directory, then access `/demos/demo.browse.php`
Support
===
**Q:** I have a question, or I found a bug. What do I do?
**A:** The preferred method of support requests and/or bug reports is the forum at <http://support.getid3.org/>
Sourceforge Notification
===
It's highly recommended that you sign up for notification from
Sourceforge for when new versions are released. Please visit:
<http://sourceforge.net/project/showfiles.php?group_id=55859>
and click the little "monitor package" icon/link. If you're
previously signed up for the mailing list, be aware that it has
been discontinued, only the automated Sourceforge notification
will be used from now on.
What does getID3() do?
===
Reads & parses (to varying degrees):
+ tags:
* APE (v1 and v2)
* ID3v1 (& ID3v1.1)
* ID3v2 (v2.4, v2.3, v2.2)
* Lyrics3 (v1 & v2)
+ audio-lossy:
* MP3/MP2/MP1
* MPC / Musepack
* Ogg (Vorbis, OggFLAC, Speex, Opus)
* AAC / MP4
* AC3
* DTS
* RealAudio
* Speex
* DSS
* VQF
+ audio-lossless:
* AIFF
* AU
* Bonk
* CD-audio (*.cda)
* FLAC
* LA (Lossless Audio)
* LiteWave
* LPAC
* MIDI
* Monkey's Audio
* OptimFROG
* RKAU
* Shorten
* TTA
* VOC
* WAV (RIFF)
* WavPack
+ audio-video:
* ASF: ASF, Windows Media Audio (WMA), Windows Media Video (WMV)
* AVI (RIFF)
* Flash
* Matroska (MKV)
* MPEG-1 / MPEG-2
* NSV (Nullsoft Streaming Video)
* Quicktime (including MP4)
* RealVideo
+ still image:
* BMP
* GIF
* JPEG
* PNG
* TIFF
* SWF (Flash)
* PhotoCD
+ data:
* ISO-9660 CD-ROM image (directory structure)
* SZIP (limited support)
* ZIP (directory structure)
* TAR
* CUE
+ Writes:
* ID3v1 (& ID3v1.1)
* ID3v2 (v2.3 & v2.4)
* VorbisComment on OggVorbis
* VorbisComment on FLAC (not OggFLAC)
* APE v2
* Lyrics3 (delete only)
Requirements
===
* PHP 4.2.0 up to 5.2.x for getID3() 1.7.x (and earlier)
* PHP 5.0.5 (or higher) for getID3() 1.8.x (and up)
* PHP 5.0.5 (or higher) for getID3() 2.0.x (and up)
* at least 4MB memory for PHP. 8MB or more is highly recommended.
12MB is required with all modules loaded.
Usage
===
See /demos/demo.basic.php for a very basic use of getID3() with no
fancy output, just scanning one file.
See structure.txt for the returned data structure.
**For an example of a complete directory-browsing, file-scanning implementation of getID3(), please run /demos/demo.browse.php**
See /demos/demo.mysql.php for a sample recursive scanning code that
scans every file in a given directory, and all sub-directories, stores
the results in a database and allows various analysis / maintenance
operations
To analyze remote files over HTTP or FTP you need to copy the file
locally first before running getID3(). Your code would look something
like this:
``` php
<?php
// Copy remote file locally to scan with getID3()
$remotefilename = 'http://www.example.com/filename.mp3';
if ($fp_remote = fopen($remotefilename, 'rb')) {
$localtempfilename = tempnam('/tmp', 'getID3');
if ($fp_local = fopen($localtempfilename, 'wb')) {
while ($buffer = fread($fp_remote, 8192)) {
fwrite($fp_local, $buffer);
}
fclose($fp_local);
// Initialize getID3 engine
$getID3 = new getID3;
$ThisFileInfo = $getID3->analyze($filename);
// Delete temporary file
unlink($localtempfilename);
}
fclose($fp_remote);
}
```
**See /demos/demo.write.php for how to write tags.**
What does the returned data structure look like?
===
See structure.txt
It is recommended that you look at the output of
/demos/demo.browse.php scanning the file(s) you're interested in to
confirm what data is actually returned for any particular filetype in
general, and your files in particular, as the actual data returned
may vary considerably depending on what information is available in
the file itself.
Notes
===
getID3() 1.x:
---
If the format parser encounters a critical problem, it will return
something in `$fileinfo['error']`, describing the encountered error. If
a less critical error or notice is generated it will appear in
`$fileinfo['warning']`. Both keys may contain more than one warning or
error. If something is returned in ['error'] then the file was not
correctly parsed and returned data may or may not be correct and/or
complete. If something is returned in `['warning']` (and not `['error']`)
then the data that is returned is OK - usually getID3() is reporting
errors in the file that have been worked around due to known bugs in
other programs. Some warnings may indicate that the data that is
returned is OK but that some data could not be extracted due to
errors in the file.
getID3() 2.x:
---
See above except errors are thrown (so you will only get one error).
Disclaimer
===
getID3() has been tested on many systems, on many types of files,
under many operating systems, and is generally believe to be stable
and safe. That being said, there is still the chance there is an
undiscovered and/or unfixed bug that may potentially corrupt your
file, especially within the writing functions. By using getID3() you
agree that it's not my fault if any of your files are corrupted.
In fact, I'm not liable for anything :)
License
===
GNU General Public License - see license.txt
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
FAQ:
---
**Q:** Can I use getID3() in my program? Do I need a commercial license?
**A:** You're generally free to use getID3 however you see fit. The only
case in which you would require a commercial license is if you're
selling your closed-source program that integrates getID3. If you
sell your program including a copy of getID3, that's fine as long
as you include a copy of the sourcecode when you sell it. Or you
can distribute your code without getID3 and say "download it from
getid3.sourceforge.net"
Why is it called "getID3()" if it does so much more than just that?
===
v0.1 did in fact just do that. I don't have a copy of code that old, but I
could essentially write it today with a one-line function:
``` php
function getID3($filename) { return unpack('a3TAG/a30title/a30artist/a30album/a4year/a28comment/c1track/c1genreid', substr(file_get_contents($filename), -128)); }
```
Future Plans
===
<http://www.getid3.org/phpBB3/viewforum.php?f=7>
* Better support for MP4 container format
* Scan for appended ID3v2 tag at end of file per ID3v2.4 specs (Section 5.0)
* Support for JPEG-2000 (http://www.morgan-multimedia.com/jpeg2000_overview.htm)
* Support for MOD (mod/stm/s3m/it/xm/mtm/ult/669)
* Support for ACE (thanks Vince)
* Support for Ogg other than Vorbis, Speex and OggFlac (ie. Ogg+Xvid)
* Ability to create Xing/LAME VBR header for VBR MP3s that are missing VBR header
* Ability to "clean" ID3v2 padding (replace invalid padding with valid padding)
* Warn if MP3s change version mid-stream (in full-scan mode)
* check for corrupt/broken mid-file MP3 streams in histogram scan
* Support for lossless-compression formats
(http://www.firstpr.com.au/audiocomp/lossless/#Links)
(http://compression.ca/act-sound.html)
(http://web.inter.nl.net/users/hvdh/lossless/lossless.htm)
* Support for RIFF-INFO chunks
* http://lotto.st-andrews.ac.uk/~njh/tag_interchange.html
(thanks Nick Humfrey <njhØsurgeradio*co*uk>)
* http://abcavi.narod.ru/sof/abcavi/infotags.htm
(thanks Kibi)
* Better support for Bink video
* http://www.hr/josip/DSP/AudioFile2.html
* http://www.pcisys.net/~melanson/codecs/
* Detect mp3PRO
* Support for PSD
* Support for JPC
* Support for JP2
* Support for JPX
* Support for JB2
* Support for IFF
* Support for ICO
* Support for ANI
* Support for EXE (comments, author, etc) (thanks p*quaedackersØplanet*nl)
* Support for DVD-IFO (region, subtitles, aspect ratio, etc)
(thanks p*quaedackersØplanet*nl)
* More complete support for SWF - parsing encapsulated MP3 and/or JPEG content
(thanks n8n8Øyahoo*com)
* Support for a2b
* Optional scan-through-frames for AVI verification
(thanks rockcohenØmassive-interactive*nl)
* Support for TTF (thanks infoØbutterflyx*com)
* Support for DSS (http://www.getid3.org/phpBB3/viewtopic.php?t=171)
* Support for SMAF (http://smaf-yamaha.com/what/demo.html)
http://www.getid3.org/phpBB3/viewtopic.php?t=182
* Support for AMR (http://www.getid3.org/phpBB3/viewtopic.php?t=195)
* Support for 3gpp (http://www.getid3.org/phpBB3/viewtopic.php?t=195)
* Support for ID4 (http://www.wackysoft.cjb.net grizlyY2KØhotmail*com)
* Parse XML data returned in Ogg comments
* Parse XML data from Quicktime SMIL metafiles (klausrathØmac*com)
* ID3v2 genre string creator function
* More complete parsing of JPG
* Support for all old-style ASF packets
* ASF/WMA/WMV tag writing
* Parse declared T??? ID3v2 text information frames, where appropriate
(thanks Christian Fritz for the idea)
* Recognize encoder:
http://www.guerillasoft.com/EncSpot2/index.html
http://ff123.net/identify.html
http://www.hydrogenaudio.org/?act=ST&f=16&t=9414
http://www.hydrogenaudio.org/?showtopic=11785
* Support for other OS/2 bitmap structures: Bitmap Array('BA'),
Color Icon('CI'), Color Pointer('CP'), Icon('IC'), Pointer ('PT')
http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
* Support for WavPack RAW mode
* ASF/WMA/WMV data packet parsing
* ID3v2FrameFlagsLookupTagAlter()
* ID3v2FrameFlagsLookupFileAlter()
* obey ID3v2 tag alter/preserve/discard rules
* http://www.geocities.com/SiliconValley/Sector/9654/Softdoc/Illyrium/Aolyr.htm
* proper checking for LINK/LNK frame validity in ID3v2 writing
* proper checking for ASPI-TLEN frame validity in ID3v2 writing
* proper checking for COMR frame validity in ID3v2 writing
* http://www.geocities.co.jp/SiliconValley-Oakland/3664/index.html
* decode GEOB ID3v2 structure as encoded by RealJukebox,
decode NCON ID3v2 structure as encoded by MusicMatch
(probably won't happen - the formats are proprietary)
Known Bugs/Issues in getID3() that may be fixed eventually
===
<http://www.getid3.org/phpBB3/viewtopic.php?t=25>
* Cannot determine bitrate for MPEG video with VBR video data
(need documentation)
* Interlace/progressive cannot be determined for MPEG video
(need documentation)
* MIDI playtime is sometimes inaccurate
* AAC-RAW mode files cannot be identified
* WavPack-RAW mode files cannot be identified
* mp4 files report lots of "Unknown QuickTime atom type"
(need documentation)
* Encrypted ASF/WMA/WMV files warn about "unhandled GUID
ASF_Content_Encryption_Object"
* Bitrate split between audio and video cannot be calculated for
NSV, only the total bitrate. (need documentation)
* All Ogg formats (Vorbis, OggFLAC, Speex) are affected by the
problem of large VorbisComments spanning multiple Ogg pages, but
but only OggVorbis files can be processed with vorbiscomment.
* The version of "head" supplied with Mac OS 10.2.8 (maybe other
versions too) does only understands a single option (-n) and
therefore fails. getID3 ignores this and returns wrong md5_data.
Known Bugs/Issues in getID3() that cannot be fixed
---
<http://www.getid3.org/phpBB3/viewtopic.php?t=25>
* 32-bit PHP installations only:
Files larger than 2GB cannot always be parsed fully by getID3()
due to limitations in the 32-bit PHP filesystem functions.
NOTE: Since v1.7.8b3 there is partial support for larger-than-
2GB files, most of which will parse OK, as long as no critical
data is located beyond the 2GB offset.
Known will-work:
* all file formats on 64-bit PHP
* ZIP (format doesn't support files >2GB)
* FLAC (current encoders don't support files >2GB)
Known will-not-work:
* ID3v1 tags (always located at end-of-file)
* Lyrics3 tags (always located at end-of-file)
* APE tags (always located at end-of-file)
Maybe-will-work:
* Quicktime (will work if needed metadata is before 2GB offset,
that is if the file has been hinted/optimized for streaming)
* RIFF.WAV (should work fine, but gives warnings about not being
able to parse all chunks)
* RIFF.AVI (playtime will probably be wrong, is only based on
"movi" chunk that fits in the first 2GB, should issue error
to show that playtime is incorrect. Other data should be mostly
correct, assuming that data is constant throughout the file)
Known Bugs/Issues in other programs
---
<http://www.getid3.org/phpBB3/viewtopic.php?t=25>
* Windows Media Player (up to v11) and iTunes (up to v10+) do
not correctly handle ID3v2.3 tags with UTF-16BE+BOM
encoding (they assume the data is UTF-16LE+BOM and either
crash (WMP) or output Asian character set (iTunes)
* Winamp (up to v2.80 at least) does not support ID3v2.4 tags,
only ID3v2.3
see: http://forums.winamp.com/showthread.php?postid=387524
* Some versions of Helium2 (www.helium2.com) do not write
ID3v2.4-compliant Frame Sizes, even though the tag is marked
as ID3v2.4) (detected by getID3())
* MP3ext V3.3.17 places a non-compliant padding string at the end
of the ID3v2 header. This is supposedly fixed in v3.4b21 but
only if you manually add a registry key. This fix is not yet
confirmed. (detected by getID3())
* CDex v1.40 (fixed by v1.50b7) writes non-compliant Ogg comment
strings, supposed to be in the format "NAME=value" but actually
written just "value" (detected by getID3())
* Oggenc 0.9-rc3 flags the encoded file as ABR whether it's
actually ABR or VBR.
* iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably
other versions are too) writes ID3v2.3 comment tags using a
frame name 'COM ' which is not valid for ID3v2.3+ (it's an
ID3v2.2-style frame name) (detected by getID3())
* MP2enc does not encode mono CBR MP2 files properly (half speed
sound and double playtime)
* MP2enc does not encode mono VBR MP2 files properly (actually
encoded as stereo)
* tooLAME does not encode mono VBR MP2 files properly (actually
encoded as stereo)
* AACenc encodes files in VBR mode (actually ABR) even if CBR is
specified
* AAC/ADIF - bitrate_mode = cbr for vbr files
* LAME 3.90-3.92 prepends one frame of null data (space for the
LAME/VBR header, but it never gets written) when encoding in CBR
mode with the DLL
* Ahead Nero encodes TwinVQF with a DSIZ value (which is supposed
to be the filesize in bytes) of "0" for TwinVQF v1.0 and "1" for
TwinVQF v2.0 (detected by getID3())
* Ahead Nero encodes TwinVQF files 1 second shorter than they
should be
* AAC-ADTS files are always actually encoded VBR, even if CBR mode
is specified (the CBR-mode switches on the encoder enable ABR
mode, not CBR as such, but it's not possible to tell the
difference between such ABR files and true VBR)
* STREAMINFO.audio_signature in OggFLAC is always null. "The reason
it's like that is because there is no seeking support in
libOggFLAC yet, so it has no way to go back and write the
computed sum after encoding. Seeking support in Ogg FLAC is the
#1 item for the next release." - Josh Coalson (FLAC developer)
NOTE: getID3() will calculate md5_data in a method similar to
other file formats, but that value cannot be compared to the
md5_data value from FLAC data in a FLAC file format.
* STREAMINFO.audio_signature is not calculated in FLAC v0.3.0 &
v0.4.0 - getID3() will calculate md5_data in a method similar to
other file formats, but that value cannot be compared to the
md5_data value from FLAC v0.5.0+
* RioPort (various versions including 2.0 and 3.11) tags ID3v2 with
a WCOM frame that has no data portion
* Earlier versions of Coolplayer adds illegal ID3 tags to Ogg Vorbis
files, thus making them corrupt.
* Meracl ID3 Tag Writer v1.3.4 (and older) incorrectly truncates the
last byte of data from an MP3 file when appending a new ID3v1 tag.
(detected by getID3())
* Lossless-Audio files encoded with and without the -noseek switch
do actually differ internally and therefore cannot match md5_data
* iTunes has been known to append a new ID3v1 tag on the end of an
existing ID3v1 tag when ID3v2 tag is also present
(detected by getID3())
* MediaMonkey may write a blank RGAD ID3v2 frame but put actual
replay gain adjustments in a series of user-defined TXXX frames
(detected and handled by getID3() since v1.9.2)
Reference material:
===
[www.id3.org](http://www.id3.org) material now mirrored at <http://id3lib.sourceforge.net/id3/>
* http://www.id3.org/id3v2.4.0-structure.txt
* http://www.id3.org/id3v2.4.0-frames.txt
* http://www.id3.org/id3v2.4.0-changes.txt
* http://www.id3.org/id3v2.3.0.txt
* http://www.id3.org/id3v2-00.txt
* http://www.id3.org/mp3frame.html
* http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html <mathewhendry@hotmail.com>
* http://www.dv.co.yu/mpgscript/mpeghdr.htm
* http://www.mp3-tech.org/programmer/frame_header.html
* http://users.belgacom.net/gc247244/extra/tag.html
* http://gabriel.mp3-tech.org/mp3infotag.html
* http://www.id3.org/iso4217.html
* http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT
* http://www.xiph.org/ogg/vorbis/doc/framing.html
* http://www.xiph.org/ogg/vorbis/doc/v-comment.html
* http://leknor.com/code/php/class.ogg.php.txt
* http://www.id3.org/iso639-2.html
* http://www.id3.org/lyrics3.html
* http://www.id3.org/lyrics3200.html
* http://www.psc.edu/general/software/packages/ieee/ieee.html
* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
* http://www.jmcgowan.com/avi.html
* http://www.wotsit.org/
* http://www.herdsoft.com/ti/davincie/davp3xo2.htm
* http://www.mathdogs.com/vorbis-illuminated/bitstream-appendix.html
* "Standard MIDI File Format" by Dustin Caldwell (from www.wotsit.org)
* http://midistudio.com/Help/GMSpecs_Patches.htm
* http://www.xiph.org/archives/vorbis/200109/0459.html
* http://www.replaygain.org/
* http://www.lossless-audio.com/
* http://download.microsoft.com/download/winmediatech40/Doc/1.0/WIN98MeXP/EN-US/ASF_Specification_v.1.0.exe
* http://mediaxw.sourceforge.net/files/doc/Active%20Streaming%20Format%20(ASF)%201.0%20Specification.pdf
* http://www.uni-jena.de/~pfk/mpp/sv8/ (archived at http://www.hydrogenaudio.org/musepack/klemm/www.personal.uni-jena.de/~pfk/mpp/sv8/)
* http://jfaul.de/atl/
* http://www.uni-jena.de/~pfk/mpp/ (archived at http://www.hydrogenaudio.org/musepack/klemm/www.personal.uni-jena.de/~pfk/mpp/)
* http://www.libpng.org/pub/png/spec/png-1.2-pdg.html
* http://www.real.com/devzone/library/creating/rmsdk/doc/rmff.htm
* http://www.fastgraph.com/help/bmp_os2_header_format.html
* http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
* http://flac.sourceforge.net/format.html
* http://www.research.att.com/projects/mpegaudio/mpeg2.html
* http://www.audiocoding.com/wiki/index.php?page=AAC
* http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
* http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
* http://developer.apple.com/techpubs/quicktime/qtdevdocs/RM/frameset.htm
* http://www.nullsoft.com/nsv/
* http://www.wotsit.org/download.asp?f=iso9660
* http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
* http://www.cdroller.com/htm/readdata.html
* http://www.speex.org/manual/node10.html
* http://www.harmony-central.com/Computer/Programming/aiff-file-format.doc
* http://www.faqs.org/rfcs/rfc2361.html
* http://ghido.shelter.ro/
* http://www.ebu.ch/tech_t3285.pdf
* http://www.sr.se/utveckling/tu/bwf
* http://ftp.aessc.org/pub/aes46-2002.pdf
* http://cartchunk.org:8080/
* http://www.broadcastpapers.com/radio/cartchunk01.htm
* http://www.hr/josip/DSP/AudioFile2.html
* http://home.attbi.com/~chris.bagwell/AudioFormats-11.html
* http://www.pure-mac.com/extkey.html
* http://cesnet.dl.sourceforge.net/sourceforge/bonkenc/bonk-binary-format-0.9.txt
* http://www.headbands.com/gspot/
* http://www.openswf.org/spec/SWFfileformat.html
* http://j-faul.virtualave.net/
* http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
* http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
* http://sswf.sourceforge.net/SWFalexref.html
* http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
* http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
* http://developer.apple.com/quicktime/icefloe/dispatch012.html
* http://www.csdn.net/Dev/Format/graphics/PCD.htm
* http://tta.iszf.irk.ru/
* http://www.atsc.org/standards/a_52a.pdf
* http://www.alanwood.net/unicode/
* http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
* http://www.its.msstate.edu/net/real/reports/config/tags.stats
* http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
* http://brennan.young.net/Comp/LiveStage/things.html
* http://www.multiweb.cz/twoinches/MP3inside.htm
* http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
* http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
* http://www.unicode.org/unicode/faq/utf_bom.html
* http://tta.corecodec.org/?menu=format
* http://www.scvi.net/nsvformat.htm
* http://pda.etsi.org/pda/queryform.asp
* http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
* http://trac.musepack.net/trac/wiki/SV8Specification
* http://wyday.com/cuesharp/specification.php
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html

2807
app/Library/getid3/changelog.txt Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,15 @@
{
"name": "james-heinrich/getid3",
"description": "PHP script that extracts useful information from popular multimedia file formats",
"homepage": "http://www.getid3.org/",
"keywords": ["php","tags","codecs"],
"type": "library",
"license": "GPL",
"require":
{
"php": ">=5.3.0"
},
"autoload": {
"classmap": ["getid3/getid3.php"]
}
}

View file

@ -0,0 +1,316 @@
<?php
// +----------------------------------------------------------------------+
// | PHP version 4.1.0 |
// +----------------------------------------------------------------------+
// | Placed in public domain by Allan Hansen, 2002. Share and enjoy! |
// +----------------------------------------------------------------------+
// | /demo/demo.audioinfo.class.php |
// | |
// | Example wrapper class to extract information from audio files |
// | through getID3(). |
// | |
// | getID3() returns a lot of information. Much of this information is |
// | not needed for the end-application. It is also possible that some |
// | users want to extract specific info. Modifying getID3() files is a |
// | bad idea, as modifications needs to be done to future versions of |
// | getID3(). |
// | |
// | Modify this wrapper class instead. This example extracts certain |
// | fields only and adds a new root value - encoder_options if possible. |
// | It also checks for mp3 files with wave headers. |
// +----------------------------------------------------------------------+
// | Example code: |
// | $au = new AudioInfo(); |
// | print_r($au->Info('file.flac'); |
// +----------------------------------------------------------------------+
// | Authors: Allan Hansen <ahØartemis*dk> |
// +----------------------------------------------------------------------+
//
/**
* getID3() settings
*/
require_once('../getid3/getid3.php');
/**
* Class for extracting information from audio files with getID3().
*/
class AudioInfo {
/**
* Private variables
*/
var $result = NULL;
var $info = NULL;
/**
* Constructor
*/
function AudioInfo() {
// Initialize getID3 engine
$this->getID3 = new getID3;
$this->getID3->option_md5_data = true;
$this->getID3->option_md5_data_source = true;
$this->getID3->encoding = 'UTF-8';
}
/**
* Extract information - only public function
*
* @access public
* @param string file Audio file to extract info from.
*/
function Info($file) {
// Analyze file
$this->info = $this->getID3->analyze($file);
// Exit here on error
if (isset($this->info['error'])) {
return array ('error' => $this->info['error']);
}
// Init wrapper object
$this->result = array();
$this->result['format_name'] = (isset($this->info['fileformat']) ? $this->info['fileformat'] : '').'/'.(isset($this->info['audio']['dataformat']) ? $this->info['audio']['dataformat'] : '').(isset($this->info['video']['dataformat']) ? '/'.$this->info['video']['dataformat'] : '');
$this->result['encoder_version'] = (isset($this->info['audio']['encoder']) ? $this->info['audio']['encoder'] : '');
$this->result['encoder_options'] = (isset($this->info['audio']['encoder_options']) ? $this->info['audio']['encoder_options'] : '');
$this->result['bitrate_mode'] = (isset($this->info['audio']['bitrate_mode']) ? $this->info['audio']['bitrate_mode'] : '');
$this->result['channels'] = (isset($this->info['audio']['channels']) ? $this->info['audio']['channels'] : '');
$this->result['sample_rate'] = (isset($this->info['audio']['sample_rate']) ? $this->info['audio']['sample_rate'] : '');
$this->result['bits_per_sample'] = (isset($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : '');
$this->result['playing_time'] = (isset($this->info['playtime_seconds']) ? $this->info['playtime_seconds'] : '');
$this->result['avg_bit_rate'] = (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : '');
$this->result['tags'] = (isset($this->info['tags']) ? $this->info['tags'] : '');
$this->result['comments'] = (isset($this->info['comments']) ? $this->info['comments'] : '');
$this->result['warning'] = (isset($this->info['warning']) ? $this->info['warning'] : '');
$this->result['md5'] = (isset($this->info['md5_data']) ? $this->info['md5_data'] : '');
// Post getID3() data handling based on file format
$method = (isset($this->info['fileformat']) ? $this->info['fileformat'] : '').'Info';
if ($method && method_exists($this, $method)) {
$this->$method();
}
return $this->result;
}
/**
* post-getID3() data handling for AAC files.
*
* @access private
*/
function aacInfo() {
$this->result['format_name'] = 'AAC';
}
/**
* post-getID3() data handling for Wave files.
*
* @access private
*/
function riffInfo() {
if ($this->info['audio']['dataformat'] == 'wav') {
$this->result['format_name'] = 'Wave';
} elseif (preg_match('#^mp[1-3]$#', $this->info['audio']['dataformat'])) {
$this->result['format_name'] = strtoupper($this->info['audio']['dataformat']);
} else {
$this->result['format_name'] = 'riff/'.$this->info['audio']['dataformat'];
}
}
/**
* * post-getID3() data handling for FLAC files.
*
* @access private
*/
function flacInfo() {
$this->result['format_name'] = 'FLAC';
}
/**
* post-getID3() data handling for Monkey's Audio files.
*
* @access private
*/
function macInfo() {
$this->result['format_name'] = 'Monkey\'s Audio';
}
/**
* post-getID3() data handling for Lossless Audio files.
*
* @access private
*/
function laInfo() {
$this->result['format_name'] = 'La';
}
/**
* post-getID3() data handling for Ogg Vorbis files.
*
* @access private
*/
function oggInfo() {
if ($this->info['audio']['dataformat'] == 'vorbis') {
$this->result['format_name'] = 'Ogg Vorbis';
} else if ($this->info['audio']['dataformat'] == 'flac') {
$this->result['format_name'] = 'Ogg FLAC';
} else if ($this->info['audio']['dataformat'] == 'speex') {
$this->result['format_name'] = 'Ogg Speex';
} else {
$this->result['format_name'] = 'Ogg '.$this->info['audio']['dataformat'];
}
}
/**
* post-getID3() data handling for Musepack files.
*
* @access private
*/
function mpcInfo() {
$this->result['format_name'] = 'Musepack';
}
/**
* post-getID3() data handling for MPEG files.
*
* @access private
*/
function mp3Info() {
$this->result['format_name'] = 'MP3';
}
/**
* post-getID3() data handling for MPEG files.
*
* @access private
*/
function mp2Info() {
$this->result['format_name'] = 'MP2';
}
/**
* post-getID3() data handling for MPEG files.
*
* @access private
*/
function mp1Info() {
$this->result['format_name'] = 'MP1';
}
/**
* post-getID3() data handling for WMA files.
*
* @access private
*/
function asfInfo() {
$this->result['format_name'] = strtoupper($this->info['audio']['dataformat']);
}
/**
* post-getID3() data handling for Real files.
*
* @access private
*/
function realInfo() {
$this->result['format_name'] = 'Real';
}
/**
* post-getID3() data handling for VQF files.
*
* @access private
*/
function vqfInfo() {
$this->result['format_name'] = 'VQF';
}
}

View file

@ -0,0 +1,52 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.basic.php - part of getID3() //
// Sample script showing most basic use of getID3() //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
// include getID3() library (can be in a different directory if full path is specified)
require_once('../getid3/getid3.php');
// Initialize getID3 engine
$getID3 = new getID3;
// Analyze file and store returned data in $ThisFileInfo
$ThisFileInfo = $getID3->analyze($filename);
/*
Optional: copies data from all subarrays of [tags] into [comments] so
metadata is all available in one location for all tag formats
metainformation is always available under [tags] even if this is not called
*/
getid3_lib::CopyTagsToComments($ThisFileInfo);
/*
Output desired information in whatever format you want
Note: all entries in [comments] or [tags] are arrays of strings
See structure.txt for information on what information is available where
or check out the output of /demos/demo.browse.php for a particular file
to see the full detail of what information is returned where in the array
Note: all array keys may not always exist, you may want to check with isset()
or empty() before deciding what to output
*/
//echo $ThisFileInfo['comments_html']['artist'][0]; // artist from any/all available tag formats
//echo $ThisFileInfo['tags']['id3v2']['title'][0]; // title from ID3v2
//echo $ThisFileInfo['audio']['bitrate']; // audio bitrate
//echo $ThisFileInfo['playtime_string']; // playtime in minutes:seconds, formatted string
/*
if you want to see ALL the output, uncomment this line:
*/
//echo '<pre>'.htmlentities(print_r($ThisFileInfo, true)).'</pre>';

View file

@ -0,0 +1,611 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.browse.php - part of getID3() //
// Sample script for browsing/scanning files and displaying //
// information returned by getID3() //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
die('For security reasons, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in demos/'.basename(__FILE__));
define('GETID3_DEMO_BROWSE_ALLOW_EDIT_LINK', false);
define('GETID3_DEMO_BROWSE_ALLOW_DELETE_LINK', false);
define('GETID3_DEMO_BROWSE_ALLOW_MD5_LINK', false);
/////////////////////////////////////////////////////////////////
// die if magic_quotes_runtime or magic_quotes_gpc are set
if (function_exists('get_magic_quotes_runtime') && get_magic_quotes_runtime()) {
die('magic_quotes_runtime is enabled, getID3 will not run.');
}
if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) {
die('magic_quotes_gpc is enabled, getID3 will not run.');
}
if (!defined('ENT_SUBSTITUTE')) { // defined in PHP v5.4.0
define('ENT_SUBSTITUTE', ENT_QUOTES);
}
/////////////////////////////////////////////////////////////////
$PageEncoding = 'UTF-8';
$writescriptfilename = 'demo.write.php';
require_once('../getid3/getid3.php');
// Needed for windows only. Leave commented-out to auto-detect, only define here if auto-detection does not work properly
//define('GETID3_HELPERAPPSDIR', 'C:\\helperapps\\');
// Initialize getID3 engine
$getID3 = new getID3;
$getID3->setOption(array('encoding' => $PageEncoding));
$getID3checkColor_Head = 'CCCCDD';
$getID3checkColor_DirectoryLight = 'FFCCCC';
$getID3checkColor_DirectoryDark = 'EEBBBB';
$getID3checkColor_FileLight = 'EEEEEE';
$getID3checkColor_FileDark = 'DDDDDD';
$getID3checkColor_UnknownLight = 'CCCCFF';
$getID3checkColor_UnknownDark = 'BBBBDD';
///////////////////////////////////////////////////////////////////////////////
header('Content-Type: text/html; charset='.$PageEncoding);
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
echo '<html><head>';
echo '<title>getID3() - /demo/demo.browse.php (sample script)</title>';
echo '<link rel="stylesheet" href="getid3.css" type="text/css">';
echo '<meta http-equiv="Content-Type" content="text/html;charset='.$PageEncoding.'" />';
echo '</head><body>';
if (isset($_REQUEST['deletefile'])) {
if (file_exists($_REQUEST['deletefile'])) {
if (unlink($_REQUEST['deletefile'])) {
$deletefilemessage = 'Successfully deleted '.addslashes($_REQUEST['deletefile']);
} else {
$deletefilemessage = 'FAILED to delete '.addslashes($_REQUEST['deletefile']).' - error deleting file';
}
} else {
$deletefilemessage = 'FAILED to delete '.addslashes($_REQUEST['deletefile']).' - file does not exist';
}
if (isset($_REQUEST['noalert'])) {
echo '<b><font color="'.(($deletefilemessage{0} == 'F') ? '#FF0000' : '#008000').'">'.$deletefilemessage.'</font></b><hr>';
} else {
echo '<script type="text/javascript">alert("'.$deletefilemessage.'");</script>';
}
}
if (isset($_REQUEST['filename'])) {
if (!file_exists($_REQUEST['filename']) || !is_file($_REQUEST['filename'])) {
die(getid3_lib::iconv_fallback('ISO-8859-1', $PageEncoding, $_REQUEST['filename'].' does not exist'));
}
$starttime = microtime(true);
//$getID3->setOption(array(
// 'option_md5_data' => $AutoGetHashes,
// 'option_sha1_data' => $AutoGetHashes,
//));
$ThisFileInfo = $getID3->analyze($_REQUEST['filename']);
$AutoGetHashes = (bool) (isset($ThisFileInfo['filesize']) && ($ThisFileInfo['filesize'] > 0) && ($ThisFileInfo['filesize'] < (50 * 1048576))); // auto-get md5_data, md5_file, sha1_data, sha1_file if filesize < 50MB, and NOT zero (which may indicate a file>2GB)
$AutoGetHashes = ($AutoGetHashes && GETID3_DEMO_BROWSE_ALLOW_MD5_LINK);
if ($AutoGetHashes) {
$ThisFileInfo['md5_file'] = md5_file($_REQUEST['filename']);
$ThisFileInfo['sha1_file'] = sha1_file($_REQUEST['filename']);
}
getid3_lib::CopyTagsToComments($ThisFileInfo);
$listdirectory = dirname($_REQUEST['filename']);
$listdirectory = realpath($listdirectory); // get rid of /../../ references
if (GETID3_OS_ISWINDOWS) {
// this mostly just gives a consistant look to Windows and *nix filesystems
// (windows uses \ as directory seperator, *nix uses /)
$listdirectory = str_replace(DIRECTORY_SEPARATOR, '/', $listdirectory.'/');
}
if (strstr($_REQUEST['filename'], 'http://') || strstr($_REQUEST['filename'], 'ftp://')) {
echo '<i>Cannot browse remote filesystems</i><br>';
} else {
echo 'Browse: <a href="'.htmlentities($_SERVER['PHP_SELF'].'?listdirectory='.urlencode($listdirectory), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'">'.getid3_lib::iconv_fallback('ISO-8859-1', $PageEncoding, $listdirectory).'</a><br>';
}
getid3_lib::ksort_recursive($ThisFileInfo);
echo table_var_dump($ThisFileInfo, false, $PageEncoding);
$endtime = microtime(true);
echo 'File parsed in '.number_format($endtime - $starttime, 3).' seconds.<br>';
} else {
$listdirectory = (isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.');
$listdirectory = realpath($listdirectory); // get rid of /../../ references
$currentfulldir = $listdirectory.'/';
if (GETID3_OS_ISWINDOWS) {
// this mostly just gives a consistant look to Windows and *nix filesystems
// (windows uses \ as directory seperator, *nix uses /)
$currentfulldir = str_replace(DIRECTORY_SEPARATOR, '/', $listdirectory.'/');
}
ob_start();
if ($handle = opendir($listdirectory)) {
ob_end_clean();
echo str_repeat(' ', 300); // IE buffers the first 300 or so chars, making this progressive display useless - fill the buffer with spaces
echo 'Processing';
$starttime = microtime(true);
$TotalScannedUnknownFiles = 0;
$TotalScannedKnownFiles = 0;
$TotalScannedPlaytimeFiles = 0;
$TotalScannedBitrateFiles = 0;
$TotalScannedFilesize = 0;
$TotalScannedPlaytime = 0;
$TotalScannedBitrate = 0;
$FilesWithWarnings = 0;
$FilesWithErrors = 0;
while ($file = readdir($handle)) {
$currentfilename = $listdirectory.'/'.$file;
set_time_limit(30); // allocate another 30 seconds to process this file - should go much quicker than this unless intense processing (like bitrate histogram analysis) is enabled
echo ' .'; // progress indicator dot
flush(); // make sure the dot is shown, otherwise it's useless
switch ($file) {
case '..':
$ParentDir = realpath($file.'/..').'/';
if (GETID3_OS_ISWINDOWS) {
$ParentDir = str_replace(DIRECTORY_SEPARATOR, '/', $ParentDir);
}
$DirectoryContents[$currentfulldir]['dir'][$file]['filename'] = $ParentDir;
continue 2;
break;
case '.':
// ignore
continue 2;
break;
}
// symbolic-link-resolution enhancements by davidbullock״ech-center*com
$TargetObject = realpath($currentfilename); // Find actual file path, resolve if it's a symbolic link
$TargetObjectType = filetype($TargetObject); // Check file type without examining extension
if ($TargetObjectType == 'dir') {
$DirectoryContents[$currentfulldir]['dir'][$file]['filename'] = $file;
} elseif ($TargetObjectType == 'file') {
$getID3->setOption(array('option_md5_data' => (isset($_REQUEST['ShowMD5']) && GETID3_DEMO_BROWSE_ALLOW_MD5_LINK)));
$fileinformation = $getID3->analyze($currentfilename);
getid3_lib::CopyTagsToComments($fileinformation);
$TotalScannedFilesize += (isset($fileinformation['filesize']) ? $fileinformation['filesize'] : 0);
if (isset($_REQUEST['ShowMD5']) && GETID3_DEMO_BROWSE_ALLOW_MD5_LINK) {
$fileinformation['md5_file'] = md5_file($currentfilename);
}
if (!empty($fileinformation['fileformat'])) {
$DirectoryContents[$currentfulldir]['known'][$file] = $fileinformation;
$TotalScannedPlaytime += (isset($fileinformation['playtime_seconds']) ? $fileinformation['playtime_seconds'] : 0);
$TotalScannedBitrate += (isset($fileinformation['bitrate']) ? $fileinformation['bitrate'] : 0);
$TotalScannedKnownFiles++;
} else {
$DirectoryContents[$currentfulldir]['other'][$file] = $fileinformation;
$DirectoryContents[$currentfulldir]['other'][$file]['playtime_string'] = '-';
$TotalScannedUnknownFiles++;
}
if (isset($fileinformation['playtime_seconds']) && ($fileinformation['playtime_seconds'] > 0)) {
$TotalScannedPlaytimeFiles++;
}
if (isset($fileinformation['bitrate']) && ($fileinformation['bitrate'] > 0)) {
$TotalScannedBitrateFiles++;
}
}
}
$endtime = microtime(true);
closedir($handle);
echo 'done<br>';
echo 'Directory scanned in '.number_format($endtime - $starttime, 2).' seconds.<br>';
flush();
$columnsintable = 14;
echo '<table class="table" cellspacing="0" cellpadding="3">';
echo '<tr bgcolor="#'.$getID3checkColor_Head.'"><th colspan="'.$columnsintable.'">Files in '.getid3_lib::iconv_fallback('ISO-8859-1', $PageEncoding, $currentfulldir).'</th></tr>';
$rowcounter = 0;
foreach ($DirectoryContents as $dirname => $val) {
if (isset($DirectoryContents[$dirname]['dir']) && is_array($DirectoryContents[$dirname]['dir'])) {
uksort($DirectoryContents[$dirname]['dir'], 'MoreNaturalSort');
foreach ($DirectoryContents[$dirname]['dir'] as $filename => $fileinfo) {
echo '<tr bgcolor="#'.(($rowcounter++ % 2) ? $getID3checkColor_DirectoryLight : $getID3checkColor_DirectoryDark).'">';
if ($filename == '..') {
echo '<td colspan="'.$columnsintable.'">';
echo '<form action="'.htmlentities($_SERVER['PHP_SELF'], ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'" method="get">';
echo 'Parent directory: ';
echo '<input type="text" name="listdirectory" size="50" style="background-color: '.$getID3checkColor_DirectoryDark.';" value="';
if (GETID3_OS_ISWINDOWS) {
echo htmlentities(str_replace(DIRECTORY_SEPARATOR, '/', realpath($dirname.$filename)), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding);
} else {
echo htmlentities(realpath($dirname.$filename), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding);
}
echo '"> <input type="submit" value="Go">';
echo '</form></td>';
} else {
$escaped_filename = htmlentities($filename, ENT_SUBSTITUTE, $PageEncoding); // do filesystems always return filenames in ISO-8859-1?
$escaped_filename = ($escaped_filename ? $escaped_filename : rawurlencode($filename));
echo '<td colspan="'.$columnsintable.'"><a href="'.htmlentities($_SERVER['PHP_SELF'].'?listdirectory='.urlencode($dirname.$filename), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'"><b>'.$escaped_filename.'</b></a></td>';
}
echo '</tr>';
}
}
echo '<tr bgcolor="#'.$getID3checkColor_Head.'">';
echo '<th>Filename</th>';
echo '<th>File Size</th>';
echo '<th>Format</th>';
echo '<th>Playtime</th>';
echo '<th>Bitrate</th>';
echo '<th>Artist</th>';
echo '<th>Title</th>';
if (isset($_REQUEST['ShowMD5']) && GETID3_DEMO_BROWSE_ALLOW_MD5_LINK) {
echo '<th>MD5&nbsp;File (File) (<a href="'.htmlentities($_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.'), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'">disable</a>)</th>';
echo '<th>MD5&nbsp;Data (File) (<a href="'.htmlentities($_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.'), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'">disable</a>)</th>';
echo '<th>MD5&nbsp;Data (Source) (<a href="'.htmlentities($_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.'), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'">disable</a>)</th>';
} else {
echo '<th colspan="3">MD5&nbsp;Data'.(GETID3_DEMO_BROWSE_ALLOW_MD5_LINK ?' (<a href="'.htmlentities($_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.').'&ShowMD5=1', ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'">enable</a>)' : '').'</th>';
}
echo '<th>Tags</th>';
echo '<th>Errors &amp; Warnings</th>';
echo (GETID3_DEMO_BROWSE_ALLOW_EDIT_LINK ? '<th>Edit</th>' : '');
echo (GETID3_DEMO_BROWSE_ALLOW_DELETE_LINK ? '<th>Delete</th>' : '');
echo '</tr>';
if (isset($DirectoryContents[$dirname]['known']) && is_array($DirectoryContents[$dirname]['known'])) {
uksort($DirectoryContents[$dirname]['known'], 'MoreNaturalSort');
foreach ($DirectoryContents[$dirname]['known'] as $filename => $fileinfo) {
echo '<tr bgcolor="#'.(($rowcounter++ % 2) ? $getID3checkColor_FileDark : $getID3checkColor_FileLight).'">';
$escaped_filename = htmlentities($filename, ENT_SUBSTITUTE, $PageEncoding);
$escaped_filename = ($escaped_filename ? $escaped_filename : rawurlencode($filename));
echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?filename='.urlencode($dirname.$filename), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'" title="View detailed analysis">'.$escaped_filename.'</a></td>';
echo '<td align="right">&nbsp;'.number_format($fileinfo['filesize']).'</td>';
echo '<td align="right">&nbsp;'.NiceDisplayFiletypeFormat($fileinfo).'</td>';
echo '<td align="right">&nbsp;'.(isset($fileinfo['playtime_string']) ? $fileinfo['playtime_string'] : '-').'</td>';
echo '<td align="right">&nbsp;'.(isset($fileinfo['bitrate']) ? BitrateText($fileinfo['bitrate'] / 1000, 0, ((isset($fileinfo['audio']['bitrate_mode']) && ($fileinfo['audio']['bitrate_mode'] == 'vbr')) ? true : false)) : '-').'</td>';
echo '<td align="left">&nbsp;'.(isset($fileinfo['comments_html']['artist']) ? implode('<br>', $fileinfo['comments_html']['artist']) : ((isset($fileinfo['video']['resolution_x']) && isset($fileinfo['video']['resolution_y'])) ? $fileinfo['video']['resolution_x'].'x'.$fileinfo['video']['resolution_y'] : '')).'</td>';
echo '<td align="left">&nbsp;'.(isset($fileinfo['comments_html']['title']) ? implode('<br>', $fileinfo['comments_html']['title']) : (isset($fileinfo['video']['frame_rate']) ? number_format($fileinfo['video']['frame_rate'], 3).'fps' : '')).'</td>';
if (isset($_REQUEST['ShowMD5']) && GETID3_DEMO_BROWSE_ALLOW_MD5_LINK) {
echo '<td align="left"><tt>'.(isset($fileinfo['md5_file']) ? $fileinfo['md5_file'] : '&nbsp;').'</tt></td>';
echo '<td align="left"><tt>'.(isset($fileinfo['md5_data']) ? $fileinfo['md5_data'] : '&nbsp;').'</tt></td>';
echo '<td align="left"><tt>'.(isset($fileinfo['md5_data_source']) ? $fileinfo['md5_data_source'] : '&nbsp;').'</tt></td>';
} else {
echo '<td align="center" colspan="3">-</td>';
}
echo '<td align="left">&nbsp;'.(!empty($fileinfo['tags']) ? implode(', ', array_keys($fileinfo['tags'])) : '').'</td>';
echo '<td align="left">&nbsp;';
if (!empty($fileinfo['warning'])) {
$FilesWithWarnings++;
echo '<a href="#" onClick="alert(\''.htmlentities(str_replace("'", "\\'", preg_replace('#[\r\n\t]+#', ' ', implode('\\n', $fileinfo['warning']))), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'\'); return false;" title="'.htmlentities(implode("; \n", $fileinfo['warning']), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'">warning</a><br>';
}
if (!empty($fileinfo['error'])) {
$FilesWithErrors++;
echo '<a href="#" onClick="alert(\''.htmlentities(str_replace("'", "\\'", preg_replace('#[\r\n\t]+#', ' ', implode('\\n', $fileinfo['error']))), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'\'); return false;" title="'.htmlentities(implode("; \n", $fileinfo['error']), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'">error</a><br>';
}
echo '</td>';
if (GETID3_DEMO_BROWSE_ALLOW_EDIT_LINK) {
echo '<td align="left">&nbsp;';
$fileinfo['fileformat'] = (isset($fileinfo['fileformat']) ? $fileinfo['fileformat'] : '');
switch ($fileinfo['fileformat']) {
case 'mp3':
case 'mp2':
case 'mp1':
case 'flac':
case 'mpc':
case 'real':
echo '<a href="'.htmlentities($writescriptfilename.'?Filename='.urlencode($dirname.$filename), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'" title="Edit tags">edit&nbsp;tags</a>';
break;
case 'ogg':
if (isset($fileinfo['audio']['dataformat']) && ($fileinfo['audio']['dataformat'] == 'vorbis')) {
echo '<a href="'.htmlentities($writescriptfilename.'?Filename='.urlencode($dirname.$filename), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'" title="Edit tags">edit&nbsp;tags</a>';
}
break;
default:
break;
}
echo '</td>';
}
if (GETID3_DEMO_BROWSE_ALLOW_DELETE_LINK) {
echo '<td align="left">&nbsp;<a href="'.htmlentities($_SERVER['PHP_SELF'].'?listdirectory='.urlencode($listdirectory).'&deletefile='.urlencode($dirname.$filename), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'" onClick="return confirm(\'Are you sure you want to delete '.addslashes(htmlentities($dirname.$filename)).'? \n(this action cannot be un-done)\');" title="'.htmlentities('Permanently delete '."\n".$filename."\n".' from'."\n".' '.$dirname, ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'">delete</a></td>';
}
echo '</tr>';
}
}
if (isset($DirectoryContents[$dirname]['other']) && is_array($DirectoryContents[$dirname]['other'])) {
uksort($DirectoryContents[$dirname]['other'], 'MoreNaturalSort');
foreach ($DirectoryContents[$dirname]['other'] as $filename => $fileinfo) {
echo '<tr bgcolor="#'.(($rowcounter++ % 2) ? $getID3checkColor_UnknownDark : $getID3checkColor_UnknownLight).'">';
$escaped_filename = htmlentities($filename, ENT_SUBSTITUTE, $PageEncoding);
$escaped_filename = ($escaped_filename ? $escaped_filename : rawurlencode($filename));
echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?filename='.urlencode($dirname.$filename), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'"><i>'.$escaped_filename.'</i></a></td>';
echo '<td align="right">&nbsp;'.(isset($fileinfo['filesize']) ? number_format($fileinfo['filesize']) : '-').'</td>';
echo '<td align="right">&nbsp;'.NiceDisplayFiletypeFormat($fileinfo).'</td>';
echo '<td align="right">&nbsp;'.(isset($fileinfo['playtime_string']) ? $fileinfo['playtime_string'] : '-').'</td>';
echo '<td align="right">&nbsp;'.(isset($fileinfo['bitrate']) ? BitrateText($fileinfo['bitrate'] / 1000) : '-').'</td>';
echo '<td align="left">&nbsp;</td>'; // Artist
echo '<td align="left">&nbsp;</td>'; // Title
echo '<td align="left" colspan="3">&nbsp;</td>'; // MD5_data
echo '<td align="left">&nbsp;</td>'; // Tags
//echo '<td align="left">&nbsp;</td>'; // Warning/Error
echo '<td align="left">&nbsp;';
if (!empty($fileinfo['warning'])) {
$FilesWithWarnings++;
echo '<a href="#" onClick="alert(\''.htmlentities(implode('\\n', $fileinfo['warning']), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'\'); return false;" title="'.htmlentities(implode("\n", $fileinfo['warning']), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'">warning</a><br>';
}
if (!empty($fileinfo['error'])) {
if ($fileinfo['error'][0] != 'unable to determine file format') {
$FilesWithErrors++;
echo '<a href="#" onClick="alert(\''.htmlentities(implode('\\n', $fileinfo['error']), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'\'); return false;" title="'.htmlentities(implode("\n", $fileinfo['error']), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'">error</a><br>';
}
}
echo '</td>';
if (GETID3_DEMO_BROWSE_ALLOW_EDIT_LINK) {
echo '<td align="left">&nbsp;</td>'; // Edit
}
if (GETID3_DEMO_BROWSE_ALLOW_DELETE_LINK) {
echo '<td align="left">&nbsp;<a href="'.htmlentities($_SERVER['PHP_SELF'].'?listdirectory='.urlencode($listdirectory).'&deletefile='.urlencode($dirname.$filename), ENT_QUOTES | ENT_SUBSTITUTE, $PageEncoding).'" onClick="return confirm(\'Are you sure you want to delete '.addslashes($dirname.$filename).'? \n(this action cannot be un-done)\');" title="Permanently delete '.addslashes($dirname.$filename).'">delete</a></td>';
}
echo '</tr>';
}
}
echo '<tr bgcolor="#'.$getID3checkColor_Head.'">';
echo '<td><b>Average:</b></td>';
echo '<td align="right">'.number_format($TotalScannedFilesize / max($TotalScannedKnownFiles, 1)).'</td>';
echo '<td>&nbsp;</td>';
echo '<td align="right">'.getid3_lib::PlaytimeString($TotalScannedPlaytime / max($TotalScannedPlaytimeFiles, 1)).'</td>';
echo '<td align="right">'.BitrateText(round(($TotalScannedBitrate / 1000) / max($TotalScannedBitrateFiles, 1))).'</td>';
echo '<td rowspan="2" colspan="'.($columnsintable - 5).'"><table class="table" border="0" cellspacing="0" cellpadding="2"><tr><th align="right">Identified Files:</th><td align="right">'.number_format($TotalScannedKnownFiles).'</td><td>&nbsp;&nbsp;&nbsp;</td><th align="right">Errors:</th><td align="right">'.number_format($FilesWithErrors).'</td></tr><tr><th align="right">Unknown Files:</th><td align="right">'.number_format($TotalScannedUnknownFiles).'</td><td>&nbsp;&nbsp;&nbsp;</td><th align="right">Warnings:</th><td align="right">'.number_format($FilesWithWarnings).'</td></tr></table>';
echo '</tr>';
echo '<tr bgcolor="#'.$getID3checkColor_Head.'">';
echo '<td><b>Total:</b></td>';
echo '<td align="right">'.number_format($TotalScannedFilesize).'</td>';
echo '<td>&nbsp;</td>';
echo '<td align="right">'.getid3_lib::PlaytimeString($TotalScannedPlaytime).'</td>';
echo '<td>&nbsp;</td>';
echo '</tr>';
}
echo '</table>';
} else {
$errormessage = ob_get_contents();
ob_end_clean();
echo '<b>ERROR: Could not open directory: <u>'.$currentfulldir.'</u></b><br>';
}
}
echo PoweredBygetID3().'<br clear="all">';
echo '</body></html>';
/////////////////////////////////////////////////////////////////
function RemoveAccents($string) {
// Revised version by markstewardרotmail*com
// Again revised by James Heinrich (19-June-2006)
return strtr(
strtr(
$string,
"\x8A\x8E\x9A\x9E\x9F\xC0\xC1\xC2\xC3\xC4\xC5\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFF",
'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'
),
array(
"\xDE" => 'TH',
"\xFE" => 'th',
"\xD0" => 'DH',
"\xF0" => 'dh',
"\xDF" => 'ss',
"\x8C" => 'OE',
"\x9C" => 'oe',
"\xC6" => 'AE',
"\xE6" => 'ae',
"\xB5" => 'u'
)
);
}
function BitrateColor($bitrate, $BitrateMaxScale=768) {
// $BitrateMaxScale is bitrate of maximum-quality color (bright green)
// below this is gradient, above is solid green
$bitrate *= (256 / $BitrateMaxScale); // scale from 1-[768]kbps to 1-256
$bitrate = round(min(max($bitrate, 1), 256));
$bitrate--; // scale from 1-256kbps to 0-255kbps
$Rcomponent = max(255 - ($bitrate * 2), 0);
$Gcomponent = max(($bitrate * 2) - 255, 0);
if ($bitrate > 127) {
$Bcomponent = max((255 - $bitrate) * 2, 0);
} else {
$Bcomponent = max($bitrate * 2, 0);
}
return str_pad(dechex($Rcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Gcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Bcomponent), 2, '0', STR_PAD_LEFT);
}
function BitrateText($bitrate, $decimals=0, $vbr=false) {
return '<span style="color: #'.BitrateColor($bitrate).($vbr ? '; font-weight: bold;' : '').'">'.number_format($bitrate, $decimals).' kbps</span>';
}
function string_var_dump($variable) {
if (version_compare(PHP_VERSION, '4.3.0', '>=')) {
return print_r($variable, true);
}
ob_start();
var_dump($variable);
$dumpedvariable = ob_get_contents();
ob_end_clean();
return $dumpedvariable;
}
function table_var_dump($variable, $wrap_in_td=false, $encoding='ISO-8859-1') {
$returnstring = '';
switch (gettype($variable)) {
case 'array':
$returnstring .= ($wrap_in_td ? '<td>' : '');
$returnstring .= '<table class="dump" cellspacing="0" cellpadding="2">';
foreach ($variable as $key => $value) {
$returnstring .= '<tr><td valign="top"><b>'.str_replace("\x00", ' ', $key).'</b></td>'."\n";
$returnstring .= '<td valign="top">'.gettype($value);
if (is_array($value)) {
$returnstring .= '&nbsp;('.count($value).')';
} elseif (is_string($value)) {
$returnstring .= '&nbsp;('.strlen($value).')';
}
//if (($key == 'data') && isset($variable['image_mime']) && isset($variable['dataoffset'])) {
if (($key == 'data') && isset($variable['image_mime'])) {
$imageinfo = array();
$imagechunkcheck = getid3_lib::GetDataImageSize($value, $imageinfo);
$returnstring .= '</td>'."\n".'<td><img src="data:'.$variable['image_mime'].';base64,'.base64_encode($value).'" width="'.$imagechunkcheck[0].'" height="'.$imagechunkcheck[1].'"></td></tr>'."\n";
} else {
$returnstring .= '</td>'."\n".table_var_dump($value, true, $encoding).'</tr>'."\n";
}
}
$returnstring .= '</table>'."\n";
$returnstring .= ($wrap_in_td ? '</td>'."\n" : '');
break;
case 'boolean':
$returnstring .= ($wrap_in_td ? '<td class="dump_boolean">' : '').($variable ? 'TRUE' : 'FALSE').($wrap_in_td ? '</td>'."\n" : '');
break;
case 'integer':
$returnstring .= ($wrap_in_td ? '<td class="dump_integer">' : '').$variable.($wrap_in_td ? '</td>'."\n" : '');
break;
case 'double':
case 'float':
$returnstring .= ($wrap_in_td ? '<td class="dump_double">' : '').$variable.($wrap_in_td ? '</td>'."\n" : '');
break;
case 'object':
case 'null':
$returnstring .= ($wrap_in_td ? '<td>' : '').string_var_dump($variable).($wrap_in_td ? '</td>'."\n" : '');
break;
case 'string':
$returnstring = htmlentities($variable, ENT_QUOTES | ENT_SUBSTITUTE, $encoding);
$returnstring = ($wrap_in_td ? '<td class="dump_string">' : '').nl2br($returnstring).($wrap_in_td ? '</td>'."\n" : '');
break;
default:
$imageinfo = array();
$imagechunkcheck = getid3_lib::GetDataImageSize($variable, $imageinfo);
if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
$returnstring .= ($wrap_in_td ? '<td>' : '');
$returnstring .= '<table class="dump" cellspacing="0" cellpadding="2">';
$returnstring .= '<tr><td><b>type</b></td><td>'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]).'</td></tr>'."\n";
$returnstring .= '<tr><td><b>width</b></td><td>'.number_format($imagechunkcheck[0]).' px</td></tr>'."\n";
$returnstring .= '<tr><td><b>height</b></td><td>'.number_format($imagechunkcheck[1]).' px</td></tr>'."\n";
$returnstring .= '<tr><td><b>size</b></td><td>'.number_format(strlen($variable)).' bytes</td></tr></table>'."\n";
$returnstring .= ($wrap_in_td ? '</td>'."\n" : '');
} else {
$returnstring .= ($wrap_in_td ? '<td>' : '').nl2br(htmlspecialchars(str_replace("\x00", ' ', $variable))).($wrap_in_td ? '</td>'."\n" : '');
}
break;
}
return $returnstring;
}
function NiceDisplayFiletypeFormat(&$fileinfo) {
if (empty($fileinfo['fileformat'])) {
return '-';
}
$output = $fileinfo['fileformat'];
if (empty($fileinfo['video']['dataformat']) && empty($fileinfo['audio']['dataformat'])) {
return $output; // 'gif'
}
if (empty($fileinfo['video']['dataformat']) && !empty($fileinfo['audio']['dataformat'])) {
if ($fileinfo['fileformat'] == $fileinfo['audio']['dataformat']) {
return $output; // 'mp3'
}
$output .= '.'.$fileinfo['audio']['dataformat']; // 'ogg.flac'
return $output;
}
if (!empty($fileinfo['video']['dataformat']) && empty($fileinfo['audio']['dataformat'])) {
if ($fileinfo['fileformat'] == $fileinfo['video']['dataformat']) {
return $output; // 'mpeg'
}
$output .= '.'.$fileinfo['video']['dataformat']; // 'riff.avi'
return $output;
}
if ($fileinfo['video']['dataformat'] == $fileinfo['audio']['dataformat']) {
if ($fileinfo['fileformat'] == $fileinfo['video']['dataformat']) {
return $output; // 'real'
}
$output .= '.'.$fileinfo['video']['dataformat']; // any examples?
return $output;
}
$output .= '.'.$fileinfo['video']['dataformat'];
$output .= '.'.$fileinfo['audio']['dataformat']; // asf.wmv.wma
return $output;
}
function MoreNaturalSort($ar1, $ar2) {
if ($ar1 === $ar2) {
return 0;
}
$len1 = strlen($ar1);
$len2 = strlen($ar2);
$shortest = min($len1, $len2);
if (substr($ar1, 0, $shortest) === substr($ar2, 0, $shortest)) {
// the shorter argument is the beginning of the longer one, like "str" and "string"
if ($len1 < $len2) {
return -1;
} elseif ($len1 > $len2) {
return 1;
}
return 0;
}
$ar1 = RemoveAccents(strtolower(trim($ar1)));
$ar2 = RemoveAccents(strtolower(trim($ar2)));
$translatearray = array('\''=>'', '"'=>'', '_'=>' ', '('=>'', ')'=>'', '-'=>' ', ' '=>' ', '.'=>'', ','=>'');
foreach ($translatearray as $key => $val) {
$ar1 = str_replace($key, $val, $ar1);
$ar2 = str_replace($key, $val, $ar2);
}
if ($ar1 < $ar2) {
return -1;
} elseif ($ar1 > $ar2) {
return 1;
}
return 0;
}
function PoweredBygetID3($string='') {
global $getID3;
if (!$string) {
$string = '<div style="border: 1px #CCCCCC solid; padding: 5px; margin: 5px 0px; float: left; background-color: #EEEEEE; font-size: 8pt; font-face: sans-serif;">Powered by <a href="http://getid3.sourceforge.net"><b>getID3() v<!--GETID3VER--></b><br>http://getid3.sourceforge.net</a><br>Running on PHP v'.phpversion().' ('.(ceil(log(PHP_INT_MAX, 2)) + 1).'-bit)</div>';
}
return str_replace('<!--GETID3VER-->', $getID3->version(), $string);
}

View file

@ -0,0 +1,31 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.cache.dbm.php - part of getID3() //
// Sample script demonstrating the use of the DBM caching //
// extension for getID3() //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
require_once('../getid3/getid3.php');
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'extension.cache.dbm.php', __FILE__, true);
$getID3 = new getID3_cached_dbm('db3', '/zimweb/test/test.dbm', '/zimweb/test/test.lock');
$r = $getID3->analyze('/path/to/files/filename.mp3');
echo '<pre>';
var_dump($r);
echo '</pre>';
// uncomment to clear cache
// $getID3->clear_cache();

View file

@ -0,0 +1,31 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.cache.mysql.php - part of getID3() //
// Sample script demonstrating the use of the DBM caching //
// extension for getID3() //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
require_once('../getid3/getid3.php');
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'extension.cache.mysql.php', __FILE__, true);
$getID3 = new getID3_cached_mysql('localhost', 'database', 'username', 'password');
$r = $getID3->analyze('/path/to/files/filename.mp3');
echo '<pre>';
var_dump($r);
echo '</pre>';
// uncomment to clear cache
//$getID3->clear_cache();

View file

@ -0,0 +1,103 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.joinmp3.php - part of getID3() //
// Sample script for splicing two or more MP3s together into //
// one file. Does not attempt to fix VBR header frames. //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
// sample usage:
// $FilenameOut = 'combined.mp3';
// $FilenamesIn[] = 'file1.mp3';
// $FilenamesIn[] = 'file2.mp3';
// $FilenamesIn[] = 'file3.mp3';
//
// if (CombineMultipleMP3sTo($FilenameOut, $FilenamesIn)) {
// echo 'Successfully copied '.implode(' + ', $FilenamesIn).' to '.$FilenameOut;
// } else {
// echo 'Failed to copy '.implode(' + ', $FilenamesIn).' to '.$FilenameOut;
// }
function CombineMultipleMP3sTo($FilenameOut, $FilenamesIn) {
foreach ($FilenamesIn as $nextinputfilename) {
if (!is_readable($nextinputfilename)) {
echo 'Cannot read "'.$nextinputfilename.'"<BR>';
return false;
}
}
if (!is_writeable($FilenameOut)) {
echo 'Cannot write "'.$FilenameOut.'"<BR>';
return false;
}
require_once('../getid3/getid3.php');
ob_start();
if ($fp_output = fopen($FilenameOut, 'wb')) {
ob_end_clean();
// Initialize getID3 engine
$getID3 = new getID3;
foreach ($FilenamesIn as $nextinputfilename) {
$CurrentFileInfo = $getID3->analyze($nextinputfilename);
if ($CurrentFileInfo['fileformat'] == 'mp3') {
ob_start();
if ($fp_source = fopen($nextinputfilename, 'rb')) {
ob_end_clean();
$CurrentOutputPosition = ftell($fp_output);
// copy audio data from first file
fseek($fp_source, $CurrentFileInfo['avdataoffset'], SEEK_SET);
while (!feof($fp_source) && (ftell($fp_source) < $CurrentFileInfo['avdataend'])) {
fwrite($fp_output, fread($fp_source, 32768));
}
fclose($fp_source);
// trim post-audio data (if any) copied from first file that we don't need or want
$EndOfFileOffset = $CurrentOutputPosition + ($CurrentFileInfo['avdataend'] - $CurrentFileInfo['avdataoffset']);
fseek($fp_output, $EndOfFileOffset, SEEK_SET);
ftruncate($fp_output, $EndOfFileOffset);
} else {
$errormessage = ob_get_contents();
ob_end_clean();
echo 'failed to open '.$nextinputfilename.' for reading';
fclose($fp_output);
return false;
}
} else {
echo $nextinputfilename.' is not MP3 format';
fclose($fp_output);
return false;
}
}
} else {
$errormessage = ob_get_contents();
ob_end_clean();
echo 'failed to open '.$FilenameOut.' for writing';
return false;
}
fclose($fp_output);
return true;
}

View file

@ -0,0 +1,74 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.mimeonly.php - part of getID3() //
// Sample script for scanning a single file and returning only //
// the MIME information //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
echo '<html><head><title>getID3 demos - MIME type only</title><style type="text/css">BODY, TD, TH { font-family: sans-serif; font-size: 10pt; }</style></head><body>';
if (!empty($_REQUEST['filename'])) {
echo 'The file "'.htmlentities($_REQUEST['filename']).'" has a MIME type of "'.htmlentities(GetMIMEtype($_REQUEST['filename'])).'"';
} else {
echo 'Usage: <span style="font-family: monospace;">'.htmlentities($_SERVER['PHP_SELF']).'?filename=<i>filename.ext</i></span>';
}
function GetMIMEtype($filename) {
$filename = realpath($filename);
if (!file_exists($filename)) {
echo 'File does not exist: "'.htmlentities($filename).'"<br>';
return '';
} elseif (!is_readable($filename)) {
echo 'File is not readable: "'.htmlentities($filename).'"<br>';
return '';
}
// include getID3() library (can be in a different directory if full path is specified)
require_once('../getid3/getid3.php');
// Initialize getID3 engine
$getID3 = new getID3;
$DeterminedMIMEtype = '';
if ($fp = fopen($filename, 'rb')) {
$getID3->openfile($filename);
if (empty($getID3->info['error'])) {
// ID3v2 is the only tag format that might be prepended in front of files, and it's non-trivial to skip, easier just to parse it and know where to skip to
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
$getid3_id3v2 = new getid3_id3v2($getID3);
$getid3_id3v2->Analyze();
fseek($fp, $getID3->info['avdataoffset'], SEEK_SET);
$formattest = fread($fp, 16); // 16 bytes is sufficient for any format except ISO CD-image
fclose($fp);
$DeterminedFormatInfo = $getID3->GetFileFormat($formattest);
$DeterminedMIMEtype = $DeterminedFormatInfo['mime_type'];
} else {
echo 'Failed to getID3->openfile "'.htmlentities($filename).'"<br>';
}
} else {
echo 'Failed to fopen "'.htmlentities($filename).'"<br>';
}
return $DeterminedMIMEtype;
}
echo '</body></html>';

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.simple.php - part of getID3() //
// Sample script for scanning a single directory and //
// displaying a few pieces of information for each file //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
echo '<html><head>';
echo '<title>getID3() - /demo/demo.simple.php (sample script)</title>';
echo '<style type="text/css">BODY,TD,TH { font-family: sans-serif; font-size: 9pt; }</style>';
echo '</head><body>';
// include getID3() library (can be in a different directory if full path is specified)
require_once('../getid3/getid3.php');
// Initialize getID3 engine
$getID3 = new getID3;
$DirectoryToScan = '/change/to/directory/you/want/to/scan'; // change to whatever directory you want to scan
$dir = opendir($DirectoryToScan);
echo '<table border="1" cellspacing="0" cellpadding="3">';
echo '<tr><th>Filename</th><th>Artist</th><th>Title</th><th>Bitrate</th><th>Playtime</th></tr>';
while (($file = readdir($dir)) !== false) {
$FullFileName = realpath($DirectoryToScan.'/'.$file);
if ((substr($file, 0, 1) != '.') && is_file($FullFileName)) {
set_time_limit(30);
$ThisFileInfo = $getID3->analyze($FullFileName);
getid3_lib::CopyTagsToComments($ThisFileInfo);
// output desired information in whatever format you want
echo '<tr>';
echo '<td>'.htmlentities($ThisFileInfo['filenamepath']).'</td>';
echo '<td>' .htmlentities(!empty($ThisFileInfo['comments_html']['artist']) ? implode('<br>', $ThisFileInfo['comments_html']['artist']) : chr(160)).'</td>';
echo '<td>' .htmlentities(!empty($ThisFileInfo['comments_html']['title']) ? implode('<br>', $ThisFileInfo['comments_html']['title']) : chr(160)).'</td>';
echo '<td align="right">'.htmlentities(!empty($ThisFileInfo['audio']['bitrate']) ? round($ThisFileInfo['audio']['bitrate'] / 1000).' kbps' : chr(160)).'</td>';
echo '<td align="right">'.htmlentities(!empty($ThisFileInfo['playtime_string']) ? $ThisFileInfo['playtime_string'] : chr(160)).'</td>';
echo '</tr>';
}
}
echo '</table>';
echo '</body></html>';

View file

@ -0,0 +1,60 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.simple.write.php - part of getID3() //
// Sample script showing basic syntax for writing tags //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
//die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
$TextEncoding = 'UTF-8';
require_once('../getid3/getid3.php');
// Initialize getID3 engine
$getID3 = new getID3;
$getID3->setOption(array('encoding'=>$TextEncoding));
require_once('../getid3/write.php');
// Initialize getID3 tag-writing module
$tagwriter = new getid3_writetags;
//$tagwriter->filename = '/path/to/file.mp3';
$tagwriter->filename = 'c:/file.mp3';
//$tagwriter->tagformats = array('id3v1', 'id3v2.3');
$tagwriter->tagformats = array('id3v2.3');
// set various options (optional)
$tagwriter->overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data (experimental)
$tagwriter->remove_other_tags = false; // if true removes other tag formats (e.g. ID3v1, ID3v2, APE, Lyrics3, etc) that may be present in the file and only write the specified tag format(s). If false leaves any unspecified tag formats as-is.
$tagwriter->tag_encoding = $TextEncoding;
$tagwriter->remove_other_tags = true;
// populate data array
$TagData = array(
'title' => array('My Song'),
'artist' => array('The Artist'),
'album' => array('Greatest Hits'),
'year' => array('2004'),
'genre' => array('Rock'),
'comment' => array('excellent!'),
'track' => array('04/16'),
'popularimeter' => array('email'=>'user@example.net', 'rating'=>128, 'data'=>0),
);
$tagwriter->tag_data = $TagData;
// write tags
if ($tagwriter->WriteTags()) {
echo 'Successfully wrote tags<br>';
if (!empty($tagwriter->warnings)) {
echo 'There were some warnings:<br>'.implode('<br><br>', $tagwriter->warnings);
}
} else {
echo 'Failed to write tags!<br>'.implode('<br><br>', $tagwriter->errors);
}

View file

@ -0,0 +1,271 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.write.php - part of getID3() //
// sample script for demonstrating writing ID3v1 and ID3v2 //
// tags for MP3, or Ogg comment tags for Ogg Vorbis //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
$TaggingFormat = 'UTF-8';
header('Content-Type: text/html; charset='.$TaggingFormat);
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
echo '<html><head><title>getID3() - Sample tag writer</title></head><style type="text/css">BODY,TD,TH { font-family: sans-serif; font-size: 9pt;" }</style><body>';
require_once('../getid3/getid3.php');
// Initialize getID3 engine
$getID3 = new getID3;
$getID3->setOption(array('encoding'=>$TaggingFormat));
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.php', __FILE__, true);
$browsescriptfilename = 'demo.browse.php';
$Filename = (isset($_REQUEST['Filename']) ? $_REQUEST['Filename'] : '');
if (isset($_POST['WriteTags'])) {
$TagFormatsToWrite = (isset($_POST['TagFormatsToWrite']) ? $_POST['TagFormatsToWrite'] : array());
if (!empty($TagFormatsToWrite)) {
echo 'starting to write tag(s)<BR>';
$tagwriter = new getid3_writetags;
$tagwriter->filename = $Filename;
$tagwriter->tagformats = $TagFormatsToWrite;
$tagwriter->overwrite_tags = false;
$tagwriter->tag_encoding = $TaggingFormat;
if (!empty($_POST['remove_other_tags'])) {
$tagwriter->remove_other_tags = true;
}
$commonkeysarray = array('Title', 'Artist', 'Album', 'Year', 'Comment');
foreach ($commonkeysarray as $key) {
if (!empty($_POST[$key])) {
$TagData[strtolower($key)][] = $_POST[$key];
}
}
if (!empty($_POST['Genre'])) {
$TagData['genre'][] = $_POST['Genre'];
}
if (!empty($_POST['GenreOther'])) {
$TagData['genre'][] = $_POST['GenreOther'];
}
if (!empty($_POST['Track'])) {
$TagData['track'][] = $_POST['Track'].(!empty($_POST['TracksTotal']) ? '/'.$_POST['TracksTotal'] : '');
}
if (!empty($_FILES['userfile']['tmp_name'])) {
if (in_array('id3v2.4', $tagwriter->tagformats) || in_array('id3v2.3', $tagwriter->tagformats) || in_array('id3v2.2', $tagwriter->tagformats)) {
if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
ob_start();
if ($fd = fopen($_FILES['userfile']['tmp_name'], 'rb')) {
ob_end_clean();
$APICdata = fread($fd, filesize($_FILES['userfile']['tmp_name']));
fclose ($fd);
list($APIC_width, $APIC_height, $APIC_imageTypeID) = GetImageSize($_FILES['userfile']['tmp_name']);
$imagetypes = array(1=>'gif', 2=>'jpeg', 3=>'png');
if (isset($imagetypes[$APIC_imageTypeID])) {
$TagData['attached_picture'][0]['data'] = $APICdata;
$TagData['attached_picture'][0]['picturetypeid'] = $_POST['APICpictureType'];
$TagData['attached_picture'][0]['description'] = $_FILES['userfile']['name'];
$TagData['attached_picture'][0]['mime'] = 'image/'.$imagetypes[$APIC_imageTypeID];
} else {
echo '<b>invalid image format (only GIF, JPEG, PNG)</b><br>';
}
} else {
$errormessage = ob_get_contents();
ob_end_clean();
echo '<b>cannot open '.$_FILES['userfile']['tmp_name'].'</b><br>';
}
} else {
echo '<b>!is_uploaded_file('.$_FILES['userfile']['tmp_name'].')</b><br>';
}
} else {
echo '<b>WARNING:</b> Can only embed images for ID3v2<br>';
}
}
$tagwriter->tag_data = $TagData;
if ($tagwriter->WriteTags()) {
echo 'Successfully wrote tags<BR>';
if (!empty($tagwriter->warnings)) {
echo 'There were some warnings:<BLOCKQUOTE STYLE="background-color:#FFCC33; padding: 10px;">'.implode('<br><br>', $tagwriter->warnings).'</BLOCKQUOTE>';
}
} else {
echo 'Failed to write tags!<BLOCKQUOTE STYLE="background-color:#FF9999; padding: 10px;">'.implode('<br><br>', $tagwriter->errors).'</BLOCKQUOTE>';
}
} else {
echo 'WARNING: no tag formats selected for writing - nothing written';
}
echo '<HR>';
}
echo '<div style="font-size: 1.2em; font-weight: bold;">Sample tag editor/writer</div>';
echo '<a href="'.htmlentities($browsescriptfilename.'?listdirectory='.rawurlencode(realpath(dirname($Filename))), ENT_QUOTES).'">Browse current directory</a><br>';
if (!empty($Filename)) {
echo '<a href="'.htmlentities($_SERVER['PHP_SELF'], ENT_QUOTES).'">Start Over</a><br><br>';
echo '<form action="'.htmlentities($_SERVER['PHP_SELF'], ENT_QUOTES).'" method="post" enctype="multipart/form-data">';
echo '<table border="3" cellspacing="0" cellpadding="4">';
echo '<tr><th align="right">Filename:</th><td><input type="hidden" name="Filename" value="'.htmlentities($Filename, ENT_QUOTES).'"><a href="'.htmlentities($browsescriptfilename.'?filename='.rawurlencode($Filename), ENT_QUOTES).'" target="_blank">'.$Filename.'</a></td></tr>';
if (file_exists($Filename)) {
// Initialize getID3 engine
$getID3 = new getID3;
$OldThisFileInfo = $getID3->analyze($Filename);
getid3_lib::CopyTagsToComments($OldThisFileInfo);
switch ($OldThisFileInfo['fileformat']) {
case 'mp3':
case 'mp2':
case 'mp1':
$ValidTagTypes = array('id3v1', 'id3v2.3', 'ape');
break;
case 'mpc':
$ValidTagTypes = array('ape');
break;
case 'ogg':
if (!empty($OldThisFileInfo['audio']['dataformat']) && ($OldThisFileInfo['audio']['dataformat'] == 'flac')) {
//$ValidTagTypes = array('metaflac');
// metaflac doesn't (yet) work with OggFLAC files
$ValidTagTypes = array();
} else {
$ValidTagTypes = array('vorbiscomment');
}
break;
case 'flac':
$ValidTagTypes = array('metaflac');
break;
case 'real':
$ValidTagTypes = array('real');
break;
default:
$ValidTagTypes = array();
break;
}
echo '<tr><td align="right"><b>Title</b></td> <td><input type="text" size="40" name="Title" value="'.htmlentities((!empty($OldThisFileInfo['comments']['title']) ? implode(', ', $OldThisFileInfo['comments']['title'] ) : ''), ENT_QUOTES).'"></td></tr>';
echo '<tr><td align="right"><b>Artist</b></td><td><input type="text" size="40" name="Artist" value="'.htmlentities((!empty($OldThisFileInfo['comments']['artist']) ? implode(', ', $OldThisFileInfo['comments']['artist']) : ''), ENT_QUOTES).'"></td></tr>';
echo '<tr><td align="right"><b>Album</b></td> <td><input type="text" size="40" name="Album" value="'.htmlentities((!empty($OldThisFileInfo['comments']['album']) ? implode(', ', $OldThisFileInfo['comments']['album'] ) : ''), ENT_QUOTES).'"></td></tr>';
echo '<tr><td align="right"><b>Year</b></td> <td><input type="text" size="4" name="Year" value="'.htmlentities((!empty($OldThisFileInfo['comments']['year']) ? implode(', ', $OldThisFileInfo['comments']['year'] ) : ''), ENT_QUOTES).'"></td></tr>';
$TracksTotal = '';
$TrackNumber = '';
if (!empty($OldThisFileInfo['comments']['track_number']) && is_array($OldThisFileInfo['comments']['track_number'])) {
$RawTrackNumberArray = $OldThisFileInfo['comments']['track_number'];
} elseif (!empty($OldThisFileInfo['comments']['track']) && is_array($OldThisFileInfo['comments']['track'])) {
$RawTrackNumberArray = $OldThisFileInfo['comments']['track'];
} else {
$RawTrackNumberArray = array();
}
foreach ($RawTrackNumberArray as $key => $value) {
if (strlen($value) > strlen($TrackNumber)) {
// ID3v1 may store track as "3" but ID3v2/APE would store as "03/16"
$TrackNumber = $value;
}
}
if (strstr($TrackNumber, '/')) {
list($TrackNumber, $TracksTotal) = explode('/', $TrackNumber);
}
echo '<tr><td align="right"><b>Track</b></td><td><input type="text" size="2" name="Track" value="'.htmlentities($TrackNumber, ENT_QUOTES).'"> of <input type="text" size="2" name="TracksTotal" value="'.htmlentities($TracksTotal, ENT_QUOTES).'"></TD></TR>';
$ArrayOfGenresTemp = getid3_id3v1::ArrayOfGenres(); // get the array of genres
foreach ($ArrayOfGenresTemp as $key => $value) { // change keys to match displayed value
$ArrayOfGenres[$value] = $value;
}
unset($ArrayOfGenresTemp); // remove temporary array
unset($ArrayOfGenres['Cover']); // take off these special cases
unset($ArrayOfGenres['Remix']);
unset($ArrayOfGenres['Unknown']);
$ArrayOfGenres[''] = '- Unknown -'; // Add special cases back in with renamed key/value
$ArrayOfGenres['Cover'] = '-Cover-';
$ArrayOfGenres['Remix'] = '-Remix-';
asort($ArrayOfGenres); // sort into alphabetical order
echo '<tr><th align="right">Genre</th><td><select name="Genre">';
$AllGenresArray = (!empty($OldThisFileInfo['comments']['genre']) ? $OldThisFileInfo['comments']['genre'] : array());
foreach ($ArrayOfGenres as $key => $value) {
echo '<option value="'.htmlentities($key, ENT_QUOTES).'"';
if (in_array($key, $AllGenresArray)) {
echo ' selected="selected"';
unset($AllGenresArray[array_search($key, $AllGenresArray)]);
sort($AllGenresArray);
}
echo '>'.htmlentities($value).'</option>';
}
echo '</select><input type="text" name="GenreOther" size="10" value="'.htmlentities((!empty($AllGenresArray[0]) ? $AllGenresArray[0] : ''), ENT_QUOTES).'"></td></tr>';
echo '<tr><td align="right"><b>Write Tags</b></td><td>';
foreach ($ValidTagTypes as $ValidTagType) {
echo '<input type="checkbox" name="TagFormatsToWrite[]" value="'.$ValidTagType.'"';
if (count($ValidTagTypes) == 1) {
echo ' checked="checked"';
} else {
switch ($ValidTagType) {
case 'id3v2.2':
case 'id3v2.3':
case 'id3v2.4':
if (isset($OldThisFileInfo['tags']['id3v2'])) {
echo ' checked="checked"';
}
break;
default:
if (isset($OldThisFileInfo['tags'][$ValidTagType])) {
echo ' checked="checked"';
}
break;
}
}
echo '>'.$ValidTagType.'<br>';
}
if (count($ValidTagTypes) > 1) {
echo '<hr><input type="checkbox" name="remove_other_tags" value="1"> Remove non-selected tag formats when writing new tag<br>';
}
echo '</td></tr>';
echo '<tr><td align="right"><b>Comment</b></td><td><textarea cols="30" rows="3" name="Comment" wrap="virtual">'.((isset($OldThisFileInfo['comments']['comment']) && is_array($OldThisFileInfo['comments']['comment'])) ? implode("\n", $OldThisFileInfo['comments']['comment']) : '').'</textarea></td></tr>';
echo '<tr><td align="right"><b>Picture</b><br>(ID3v2 only)</td><td><input type="file" name="userfile" accept="image/jpeg, image/gif, image/png"><br>';
echo '<select name="APICpictureType">';
$APICtypes = getid3_id3v2::APICPictureTypeLookup('', true);
foreach ($APICtypes as $key => $value) {
echo '<option value="'.htmlentities($key, ENT_QUOTES).'">'.htmlentities($value).'</option>';
}
echo '</select></td></tr>';
echo '<tr><td align="center" colspan="2"><input type="submit" name="WriteTags" value="Save Changes"> ';
echo '<input type="reset" value="Reset"></td></tr>';
} else {
echo '<tr><td align="right"><b>Error</b></td><td>'.htmlentities($Filename).' does not exist</td></tr>';
}
echo '</table>';
echo '</form>';
}
echo '</body></html>';

View file

@ -0,0 +1,101 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.zip.php - part of getID3() //
// Sample script how to use getID3() to decompress zip files //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
function UnzipFileContents($filename, &$errors) {
$errors = array();
$DecompressedFileContents = array();
if (!class_exists('getID3')) {
$errors[] = 'class getID3 not defined, please include getid3.php';
} elseif (include_once('module.archive.zip.php')) {
$getid3 = new getID3();
$getid3->info['filesize'] = filesize($filename);
ob_start();
if ($getid3->fp = fopen($filename, 'rb')) {
ob_end_clean();
$getid3_zip = new getid3_zip($getid3);
$getid3_zip->analyze($filename);
if (($getid3->info['fileformat'] == 'zip') && !empty($getid3->info['zip']['files'])) {
if (!empty($getid3->info['zip']['central_directory'])) {
$ZipDirectoryToWalk = $getid3->info['zip']['central_directory'];
} elseif (!empty($getid3->info['zip']['entries'])) {
$ZipDirectoryToWalk = $getid3->info['zip']['entries'];
} else {
$errors[] = 'failed to parse ZIP attachment "'.$piece_filename.'" (no central directory)<br>';
fclose($getid3->fp);
return false;
}
foreach ($ZipDirectoryToWalk as $key => $valuearray) {
fseek($getid3->fp, $valuearray['entry_offset'], SEEK_SET);
$LocalFileHeader = $getid3_zip->ZIPparseLocalFileHeader($getid3->fp);
if ($LocalFileHeader['flags']['encrypted']) {
// password-protected
$DecompressedFileContents[$valuearray['filename']] = '';
} else {
fseek($getid3->fp, $LocalFileHeader['data_offset'], SEEK_SET);
$compressedFileData = '';
while ((strlen($compressedFileData) < $LocalFileHeader['compressed_size']) && !feof($getid3->fp)) {
$compressedFileData .= fread($getid3->fp, 32768);
}
switch ($LocalFileHeader['raw']['compression_method']) {
case 0: // store - great, just copy data unchanged
$uncompressedFileData = $compressedFileData;
break;
case 8: // deflate
ob_start();
$uncompressedFileData = gzinflate($compressedFileData);
$gzinflate_errors = trim(strip_tags(ob_get_contents()));
ob_end_clean();
if ($gzinflate_errors) {
$errors[] = 'gzinflate() failed for file ['.$LocalFileHeader['filename'].']: "'.$gzinflate_errors.'"';
continue 2;
}
break;
case 1: // shrink
case 2: // reduce-1
case 3: // reduce-2
case 4: // reduce-3
case 5: // reduce-4
case 6: // implode
case 7: // tokenize
case 9: // deflate64
case 10: // PKWARE Date Compression Library Imploding
$DecompressedFileContents[$valuearray['filename']] = '';
$errors[] = 'unsupported ZIP compression method ('.$LocalFileHeader['raw']['compression_method'].' = '.$getid3_zip->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']).')';
continue 2;
default:
$DecompressedFileContents[$valuearray['filename']] = '';
$errors[] = 'unknown ZIP compression method ('.$LocalFileHeader['raw']['compression_method'].')';
continue 2;
}
$DecompressedFileContents[$valuearray['filename']] = $uncompressedFileData;
unset($compressedFileData);
}
}
} else {
$errors[] = $filename.' does not appear to be a zip file';
}
} else {
$error_message = ob_get_contents();
ob_end_clean();
$errors[] = 'failed to fopen('.$filename.', rb): '.$error_message;
}
} else {
$errors[] = 'failed to include_once(module.archive.zip.php)';
}
return $DecompressedFileContents;
}

171
app/Library/getid3/demos/getid3.css vendored Executable file
View file

@ -0,0 +1,171 @@
/**
* Common elements
*/
body {
font: 12px Verdana, sans-serif;
background-color: white;
color: black;
margin-top: 6px;
margin-bottom: 30px;
margin-left: 12px;
margin-right: 12px;
}
h1 {
font: bold 18px Verdana, sans-serif;
line-height: 26px;
margin-top: 12px;
margin-bottom: 15px;
margin-left: 0px;
margin-right: 7px;
background-color: #e6eaf6;
padding-left: 10px;
padding-top: 2px;
padding-bottom: 4px;
}
h3 {
font: bold 13px Verdana, sans-serif;
line-height: 26px;
margin-top: 12px;
margin-bottom: 0px;
margin-left: 0px;
margin-right: 7px;
padding-left: 4px;
}
ul {
margin-top: 0px;
}
p, li {
font: 9pt/135% sans-serif;
margin-top: 1x;
margin-bottom: 0x;
}
a, a:link, a:visited {
color: #0000cc;
}
hr {
height: 0;
border: solid gray 0;
border-top-width: thin;
width: 700px;
}
table.table td {
font: 9pt sans-serif;
padding-top: 1px;
padding-bottom: 1px;
padding-left: 5px;
padding-right: 5px;
}
table.table td.header {
background-color: #cccccc;
padding-top: 2px;
padding-bottom: 2px;
font-weight: bold;
}
table.table tr.even_files {
background-color: #fefefe;
}
table.table tr.odd_files {
background-color: #e9e9e9;
}
table.dump {
border-top: solid 1px #cccccc;
border-left: solid 1px #cccccc;
margin: 2px;
}
table.dump td {
font: 9pt sans-serif;
padding-top: 1px;
padding-bottom: 1px;
padding-left: 5px;
padding-right: 5px;
border-right: solid 1px #cccccc;
border-bottom: solid 1px #cccccc;
}
td.dump_string {
font-weight: bold;
color: #006600;
font-family: Zawgyi-One,sans-serif;
}
td.dump_integer {
color: #CC0000;
font-weight: bold;
}
td.dump_double {
color: #FF9900;
font-weight: bold;
}
td.dump_boolean {
color: #0000FF;
font-weight: bold;
}
.error {
color: red
}
/**
* Tool Tips
*/
.tooltip {
font: 9pt sans-serif;
background: #ffffe1;
color: black;
border: black 1px solid;
margin: 2px;
padding: 7px;
position: absolute;
top: 10px;
left: 10px;
z-index: 10000;
visibility: hidden;
}
.tooltip p {
margin-top: -2px;
margin-bottom: 4px;
}
/**
* Forms
*/
table.form td {
font: 9pt/135% sans-serif;
padding: 2px;
}
select, input {
font: 9pt/135% sans-serif;
}
.select, .field {
width: 260px;
}
#sel_field {
width: 85px;
}
.button {
margin-top: 10px;
}

View file

@ -0,0 +1,258 @@
<?php
/////////////////////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////////////////////
/// //
// getid3.dirscan.php - tool for batch media file processing with getID3() //
// ///
/////////////////////////////////////////////////////////////////////////////////
/// //
// Directory Scanning and Caching CLI tool by Karl G. Holz <newaeonØmac*com> //
// ///
/////////////////////////////////////////////////////////////////////////////////
/**
* This is a directory scanning and caching cli tool for getID3().
*
* use like so for the default sqlite3 database, which is hidden:
*
* cd <path you want to start scanning from>
* php <path to getid3 files>/getid3.dirscan.php
*
* or
*
* php <path to getid3 files>/getid3.dirscan.php <dir to scan> <file ext in csv list>
*
* Supported Cache Types (this extension)
*
* SQL Databases:
*
* cache_type
* -------------------------------------------------------------------
* mysql
$cache='mysql';
$database['host']='';
$database['database']='';
$database['username']='';
$database['password']='';
$database['table']='';
* sqlite3
$cache='sqlite3';
$database['table']='getid3_cache';
$database['hide']=true;
*/
$dir = $_SERVER['PWD'];
$media = array('mp4', 'm4v', 'mov', 'mp3', 'm4a', 'jpg', 'png', 'gif');
$database = array();
/**
* configure the database bellow
*/
// sqlite3
$cache = 'sqlite3';
$database['table'] = 'getid3_cache';
$database['hide'] = true;
/**
* mysql
$cache = 'mysql';
$database['host'] = '';
$database['database'] = '';
$database['username'] = '';
$database['password'] = '';
$database['table'] = '';
*/
/**
* id3 tags class file
*/
require_once(dirname(__FILE__).'/getid3.php');
/**
* dirscan scans all directories for files that match your selected filetypes into the cache database
* this is useful for a lot of media files
*
*
* @package dirscan
* @author Karl Holz
*
*/
class dirscan {
/**
* type_brace() * Might not work on Solaris and other non GNU systems *
*
* Configures a filetype list for use with glob searches,
* will match uppercase or lowercase extensions only, no mixing
* @param string $dir directory to use
* @param mixed cvs list of extentions or an array
* @return string or null if checks fail
*/
private function type_brace($dir, $search=array()) {
$dir = str_replace(array('///', '//'), array('/', '/'), $dir);
if (!is_dir($dir)) {
return null;
}
if (!is_array($search)) {
$e = explode(',', $search);
} elseif (count($search) < 1) {
return null;
} else {
$e = $search;
}
$ext = array();
foreach ($e as $new) {
$ext[] = strtolower(trim($new));
$ext[] = strtoupper(trim($new));
}
$b = $dir.'/*.{'.implode(',', $ext).'}';
return $b;
}
/**
* this function will search 4 levels deep for directories
* will return null on failure
* @param string $root
* @return array return an array of dirs under root
* @todo figure out how to block tabo directories with ease
*/
private function getDirs($root) {
switch ($root) { // return null on tabo directories, add as needed -> case {dir to block }: this is not perfect yet
case '/':
case '/var':
case '/etc':
case '/home':
case '/usr':
case '/root':
case '/private/etc':
case '/private/var':
case '/etc/apache2':
case '/home':
case '/tmp':
case '/var/log':
return null;
break;
default: // scan 4 directories deep
if (!is_dir($root)) {
return null;
}
$dirs = array_merge(glob($root.'/*', GLOB_ONLYDIR), glob($root.'/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*/*/*', GLOB_ONLYDIR));
break;
}
if (count($dirs) < 1) {
$dirs = array($root);
}
return $dirs;
}
/**
* file_check() check the number of file that are found that match the brace search
*
* @param string $search
* @return mixed
*/
private function file_check($search) {
$t = array();
$s = glob($search, GLOB_BRACE);
foreach ($s as $file) {
$t[] = str_replace(array('///', '//'), array('/', '/'), $file);
}
if (count($t) > 0) {
return $t;
}
return null;
}
function getTime() {
return microtime(true);
// old method for PHP < 5
//$a = explode(' ', microtime());
//return (double) $a[0] + $a[1];
}
/**
*
* @param type $dir
* @param type $match search type name extentions, can be an array or csv list
* @param type $cache caching extention, select one of sqlite3, mysql, dbm
* @param array $opt database options,
*/
function scan_files($dir, $match, $cache='sqlite3', $opt=array('table'=>'getid3_cache', 'hide'=>true)) {
$Start = self::getTime();
switch ($cache) { // load the caching module
case 'sqlite3':
if (!class_exists('getID3_cached_sqlite3')) {
require_once(dirname(__FILE__)).'/extension.cache.sqlite3.php';
}
$id3 = new getID3_cached_sqlite3($opt['table'], $opt['hide']);
break;
case 'mysql':
if (!class_exists('getID3_cached_mysql')) {
require_once(dirname(__FILE__)).'/extension.cache.mysql.php';
}
$id3 = new getID3_cached_mysql($opt['host'], $opt['database'], $opt['username'], $opt['password'], $opt['table']);
break;
// I'll leave this for some one else
//case 'dbm':
// if (!class_exists('getID3_cached_dbm')) {
// require_once(dirname(__FILE__)).'/extension.cache.dbm.php';
// }
// die(' This has not be implemented, sorry for the inconvenience');
// break;
default:
die(' You have selected an Invalid cache type, only "sqlite3" and "mysql" are valid'."\n");
break;
}
$count = array('dir'=>0, 'file'=>0);
$dirs = self::getDirs($dir);
if ($dirs !== null) {
foreach ($dirs as $d) {
echo ' Scanning: '.$d."\n";
$search = self::type_brace($d, $match);
if ($search !== null) {
$files = self::file_check($search);
if ($files !== null) {
foreach ($files as $f) {
echo ' * Analyzing '.$f.' '."\n";
$id3->analyze($f);
$count['file']++;
}
$count['dir']++;
} else {
echo 'Failed to get files '."\n";
}
} else {
echo 'Failed to create match string '."\n";
}
}
echo '**************************************'."\n";
echo '* Finished Scanning your directories '."\n*\n";
echo '* Directories '.$count['dir']."\n";
echo '* Files '.$count['file']."\n";
$End = self::getTime();
$t = number_format(($End - $Start) / 60, 2);
echo '* Time taken to scan '.$dir.' '.$t.' min '."\n";
echo '**************************************'."\n";
} else {
echo ' failed to get directories '."\n";
}
}
}
if (PHP_SAPI === 'cli') {
if (count($argv) == 2) {
if (is_dir($argv[1])) {
$dir = $argv[1];
}
if (count(explode(',', $argv[2])) > 0) {
$media = $arg[2];
}
}
echo ' * Starting to scan directory: '.$dir."\n";
echo ' * Using default media types: '.implode(',', $media)."\n";
dirscan::scan_files($dir, $media, $cache, $database);
}

View file

@ -0,0 +1,19 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><title>getID3 demos</title><style type="text/css">BODY, TD, TH { font-family: sans-serif; font-size: 10pt; }</style></head><body>
In this directory are a number of examples of how to use <a href="http://www.getid3.org/">getID3()</a>.<br>
If you don't know what to run, take a look at <a href="demo.browse.php"><b>demo.browse.php</b></a>
<hr>
Other demos:<ul>
<?php
if ($dh = opendir('.')) {
while ($file = readdir($dh)) {
if (preg_match('#^demo\\..+\\.php$#', $file)) {
echo '<li><a href="'.htmlentities($file, ENT_QUOTES).'">'.htmlentities($file).'</a></li>';
}
}
}
?>
</ul>
</body>
</html>

View file

@ -0,0 +1,25 @@
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// dependencies.txt - part of getID3() //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
lyrics3 depends on apetag (optional)
ogg depends on flac
id3v2 depends on id3v1
apetag depends on id3v1 (optional, writing only)
bonk depends on id3v2 (optional)
riff depends on mp3
mpeg depends on mp3
quicktime depends on mp3
flac depends on ogg
optimfrog depends on riff
la depends on riff
lpac depends on riff
asf depends on riff, id3v1 (optional)

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// extension.cache.dbm.php - part of getID3() // // extension.cache.dbm.php - part of getID3() //
@ -10,7 +11,7 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// This extension written by Allan Hansen <ahØartemis*dk> // // This extension written by Allan Hansen <ahØartemis*dk> //
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -73,7 +74,7 @@ class getID3_cached_dbm extends getID3
{ {
// public: constructor - see top of this file for cache type and cache_options // public: constructor - see top of this file for cache type and cache_options
function getID3_cached_dbm($cache_type, $dbm_filename, $lock_filename) { public function getID3_cached_dbm($cache_type, $dbm_filename, $lock_filename) {
// Check for dba extension // Check for dba extension
if (!extension_loaded('dba')) { if (!extension_loaded('dba')) {
@ -135,13 +136,13 @@ class getID3_cached_dbm extends getID3
$this->clear_cache(); $this->clear_cache();
} }
parent::getID3(); parent::__construct();
} }
// public: destuctor // public: destructor
function __destruct() { public function __destruct() {
// Close dbm file // Close dbm file
dba_close($this->dba); dba_close($this->dba);
@ -156,7 +157,7 @@ class getID3_cached_dbm extends getID3
// public: clear cache // public: clear cache
function clear_cache() { public function clear_cache() {
// Close dbm file // Close dbm file
dba_close($this->dba); dba_close($this->dba);
@ -178,7 +179,7 @@ class getID3_cached_dbm extends getID3
// public: analyze file // public: analyze file
function analyze($filename) { public function analyze($filename) {
if (file_exists($filename)) { if (file_exists($filename)) {
@ -206,6 +207,3 @@ class getID3_cached_dbm extends getID3
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// extension.cache.mysql.php - part of getID3() // // extension.cache.mysql.php - part of getID3() //
@ -10,8 +11,8 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// This extension written by Allan Hansen <ahØartemis*dk> // // This extension written by Allan Hansen <ahØartemis*dk> //
// Table name mod by Carlo Capocasa <calroØcarlocapocasa*com> // // Table name mod by Carlo Capocasa <calroØcarlocapocasa*com> //
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -74,12 +75,12 @@ class getID3_cached_mysql extends getID3
{ {
// private vars // private vars
var $cursor; private $cursor;
var $connection; private $connection;
// public: constructor - see top of this file for cache type and cache_options // public: constructor - see top of this file for cache type and cache_options
function getID3_cached_mysql($host, $database, $username, $password, $table='getid3_cache') { public function getID3_cached_mysql($host, $database, $username, $password, $table='getid3_cache') {
// Check for mysql support // Check for mysql support
if (!function_exists('mysql_pconnect')) { if (!function_exists('mysql_pconnect')) {
@ -105,38 +106,49 @@ class getID3_cached_mysql extends getID3
// Check version number and clear cache if changed // Check version number and clear cache if changed
$version = ''; $version = '';
if ($this->cursor = mysql_query("SELECT `value` FROM `".mysql_real_escape_string($this->table)."` WHERE (`filename` = '".mysql_real_escape_string(getID3::VERSION)."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection)) { $SQLquery = 'SELECT `value`';
$SQLquery .= ' FROM `'.mysql_real_escape_string($this->table).'`';
$SQLquery .= ' WHERE (`filename` = \''.mysql_real_escape_string(getID3::VERSION).'\')';
$SQLquery .= ' AND (`filesize` = -1)';
$SQLquery .= ' AND (`filetime` = -1)';
$SQLquery .= ' AND (`analyzetime` = -1)';
if ($this->cursor = mysql_query($SQLquery, $this->connection)) {
list($version) = mysql_fetch_array($this->cursor); list($version) = mysql_fetch_array($this->cursor);
} }
if ($version != getID3::VERSION) { if ($version != getID3::VERSION) {
$this->clear_cache(); $this->clear_cache();
} }
parent::getID3(); parent::__construct();
} }
// public: clear cache // public: clear cache
function clear_cache() { public function clear_cache() {
$this->cursor = mysql_query("DELETE FROM `".mysql_real_escape_string($this->table)."`", $this->connection); $this->cursor = mysql_query('DELETE FROM `'.mysql_real_escape_string($this->table).'`', $this->connection);
$this->cursor = mysql_query("INSERT INTO `".mysql_real_escape_string($this->table)."` VALUES ('".getID3::VERSION."', -1, -1, -1, '".getID3::VERSION."')", $this->connection); $this->cursor = mysql_query('INSERT INTO `'.mysql_real_escape_string($this->table).'` VALUES (\''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')', $this->connection);
} }
// public: analyze file // public: analyze file
function analyze($filename) { public function analyze($filename) {
if (file_exists($filename)) { if (file_exists($filename)) {
// Short-hands // Short-hands
$filetime = filemtime($filename); $filetime = filemtime($filename);
$filesize = filesize($filename); $filesize = filesize($filename);
// Lookup file // Lookup file
$this->cursor = mysql_query("SELECT `value` FROM `".mysql_real_escape_string($this->table)."` WHERE (`filename` = '".mysql_real_escape_string($filename)."') AND (`filesize` = '".mysql_real_escape_string($filesize)."') AND (`filetime` = '".mysql_real_escape_string($filetime)."')", $this->connection); $SQLquery = 'SELECT `value`';
$SQLquery .= ' FROM `'.mysql_real_escape_string($this->table).'`';
$SQLquery .= ' WHERE (`filename` = \''.mysql_real_escape_string($filename).'\')';
$SQLquery .= ' AND (`filesize` = \''.mysql_real_escape_string($filesize).'\')';
$SQLquery .= ' AND (`filetime` = \''.mysql_real_escape_string($filetime).'\')';
$this->cursor = mysql_query($SQLquery, $this->connection);
if (mysql_num_rows($this->cursor) > 0) { if (mysql_num_rows($this->cursor) > 0) {
// Hit // Hit
list($result) = mysql_fetch_array($this->cursor); list($result) = mysql_fetch_array($this->cursor);
@ -149,7 +161,13 @@ class getID3_cached_mysql extends getID3
// Save result // Save result
if (file_exists($filename)) { if (file_exists($filename)) {
$this->cursor = mysql_query("INSERT INTO `".mysql_real_escape_string($this->table)."` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".mysql_real_escape_string($filename)."', '".mysql_real_escape_string($filesize)."', '".mysql_real_escape_string($filetime)."', '".mysql_real_escape_string(time())."', '".mysql_real_escape_string(base64_encode(serialize($analysis)))."')", $this->connection); $SQLquery = 'INSERT INTO `'.mysql_real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (';
$SQLquery .= '\''.mysql_real_escape_string($filename).'\'';
$SQLquery .= ', \''.mysql_real_escape_string($filesize).'\'';
$SQLquery .= ', \''.mysql_real_escape_string($filetime).'\'';
$SQLquery .= ', \''.mysql_real_escape_string(time() ).'\'';
$SQLquery .= ', \''.mysql_real_escape_string(base64_encode(serialize($analysis))).'\')';
$this->cursor = mysql_query($SQLquery, $this->connection);
} }
return $analysis; return $analysis;
} }
@ -157,17 +175,16 @@ class getID3_cached_mysql extends getID3
// private: (re)create sql table // private: (re)create sql table
function create_table($drop=false) { private function create_table($drop=false) {
$this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `".mysql_real_escape_string($this->table)."` ( $SQLquery = 'CREATE TABLE IF NOT EXISTS `'.mysql_real_escape_string($this->table).'` (';
`filename` VARCHAR(255) NOT NULL DEFAULT '', $SQLquery .= '`filename` VARCHAR(255) NOT NULL DEFAULT \'\'';
`filesize` INT(11) NOT NULL DEFAULT '0', $SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\'';
`filetime` INT(11) NOT NULL DEFAULT '0', $SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\'';
`analyzetime` INT(11) NOT NULL DEFAULT '0', $SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\'';
`value` TEXT NOT NULL, $SQLquery .= ', `value` TEXT NOT NULL';
PRIMARY KEY (`filename`,`filesize`,`filetime`)) TYPE=MyISAM", $this->connection); $SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM';
$this->cursor = mysql_query($SQLquery, $this->connection);
echo mysql_error($this->connection); echo mysql_error($this->connection);
} }
} }
?>

View file

@ -0,0 +1,265 @@
<?php
/////////////////////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////////////////////
/// //
// extension.cache.sqlite3.php - part of getID3() //
// Please see readme.txt for more information //
// ///
/////////////////////////////////////////////////////////////////////////////////
/// //
// MySQL extension written by Allan Hansen <ahØartemis*dk> //
// Table name mod by Carlo Capocasa <calroØcarlocapocasa*com> //
// MySQL extension was reworked for SQLite3 by Karl G. Holz <newaeonØmac*com> //
// ///
/////////////////////////////////////////////////////////////////////////////////
/**
* This is a caching extension for getID3(). It works the exact same
* way as the getID3 class, but return cached information much faster
*
* Normal getID3 usage (example):
*
* require_once 'getid3/getid3.php';
* $getID3 = new getID3;
* $getID3->encoding = 'UTF-8';
* $info1 = $getID3->analyze('file1.flac');
* $info2 = $getID3->analyze('file2.wv');
*
* getID3_cached usage:
*
* require_once 'getid3/getid3.php';
* require_once 'getid3/extension.cache.sqlite3.php';
* // all parameters are optional, defaults are:
* $getID3 = new getID3_cached_sqlite3($table='getid3_cache', $hide=FALSE);
* $getID3->encoding = 'UTF-8';
* $info1 = $getID3->analyze('file1.flac');
* $info2 = $getID3->analyze('file2.wv');
*
*
* Supported Cache Types (this extension)
*
* SQL Databases:
*
* cache_type cache_options
* -------------------------------------------------------------------
* mysql host, database, username, password
*
* sqlite3 table='getid3_cache', hide=false (PHP5)
*
*** database file will be stored in the same directory as this script,
*** webserver must have write access to that directory!
*** set $hide to TRUE to prefix db file with .ht to pervent access from web client
*** this is a default setting in the Apache configuration:
# The following lines prevent .htaccess and .htpasswd files from being viewed by Web clients.
<Files ~ "^\.ht">
Order allow,deny
Deny from all
Satisfy all
</Files>
********************************************************************************
*
* -------------------------------------------------------------------
* DBM-Style Databases: (use extension.cache.dbm)
*
* cache_type cache_options
* -------------------------------------------------------------------
* gdbm dbm_filename, lock_filename
* ndbm dbm_filename, lock_filename
* db2 dbm_filename, lock_filename
* db3 dbm_filename, lock_filename
* db4 dbm_filename, lock_filename (PHP5 required)
*
* PHP must have write access to both dbm_filename and lock_filename.
*
* Recommended Cache Types
*
* Infrequent updates, many reads any DBM
* Frequent updates mysql
********************************************************************************
*
* IMHO this is still a bit slow, I'm using this with MP4/MOV/ M4v files
* there is a plan to add directory scanning and analyzing to make things work much faster
*
*
*/
class getID3_cached_sqlite3 extends getID3 {
/**
* __construct()
* @param string $table holds name of sqlite table
* @return type
*/
public function __construct($table='getid3_cache', $hide=false) {
$this->table = $table; // Set table
$file = dirname(__FILE__).'/'.basename(__FILE__, 'php').'sqlite';
if ($hide) {
$file = dirname(__FILE__).'/.ht.'.basename(__FILE__, 'php').'sqlite';
}
$this->db = new SQLite3($file);
$db = $this->db;
$this->create_table(); // Create cache table if not exists
$version = '';
$sql = $this->version_check;
$stmt = $db->prepare($sql);
$stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT);
$result = $stmt->execute();
list($version) = $result->fetchArray();
if ($version != getID3::VERSION) { // Check version number and clear cache if changed
$this->clear_cache();
}
return parent::__construct();
}
/**
* close the database connection
*/
public function __destruct() {
$db=$this->db;
$db->close();
}
/**
* hold the sqlite db
* @var SQLite Resource
*/
private $db;
/**
* table to use for caching
* @var string $table
*/
private $table;
/**
* clear the cache
* @access private
* @return type
*/
private function clear_cache() {
$db = $this->db;
$sql = $this->delete_cache;
$db->exec($sql);
$sql = $this->set_version;
$stmt = $db->prepare($sql);
$stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT);
$stmt->bindValue(':dirname', getID3::VERSION, SQLITE3_TEXT);
$stmt->bindValue(':val', getID3::VERSION, SQLITE3_TEXT);
return $stmt->execute();
}
/**
* analyze file and cache them, if cached pull from the db
* @param type $filename
* @return boolean
*/
public function analyze($filename) {
if (!file_exists($filename)) {
return false;
}
// items to track for caching
$filetime = filemtime($filename);
$filesize = filesize($filename);
// this will be saved for a quick directory lookup of analized files
// ... why do 50 seperate sql quries when you can do 1 for the same result
$dirname = dirname($filename);
// Lookup file
$db = $this->db;
$sql = $this->get_id3_data;
$stmt = $db->prepare($sql);
$stmt->bindValue(':filename', $filename, SQLITE3_TEXT);
$stmt->bindValue(':filesize', $filesize, SQLITE3_INTEGER);
$stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER);
$res = $stmt->execute();
list($result) = $res->fetchArray();
if (count($result) > 0 ) {
return unserialize(base64_decode($result));
}
// if it hasn't been analyzed before, then do it now
$analysis = parent::analyze($filename);
// Save result
$sql = $this->cache_file;
$stmt = $db->prepare($sql);
$stmt->bindValue(':filename', $filename, SQLITE3_TEXT);
$stmt->bindValue(':dirname', $dirname, SQLITE3_TEXT);
$stmt->bindValue(':filesize', $filesize, SQLITE3_INTEGER);
$stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER);
$stmt->bindValue(':atime', time(), SQLITE3_INTEGER);
$stmt->bindValue(':val', base64_encode(serialize($analysis)), SQLITE3_TEXT);
$res = $stmt->execute();
return $analysis;
}
/**
* create data base table
* this is almost the same as MySQL, with the exception of the dirname being added
* @return type
*/
private function create_table() {
$db = $this->db;
$sql = $this->make_table;
return $db->exec($sql);
}
/**
* get cached directory
*
* This function is not in the MySQL extention, it's ment to speed up requesting multiple files
* which is ideal for podcasting, playlists, etc.
*
* @access public
* @param string $dir directory to search the cache database for
* @return array return an array of matching id3 data
*/
public function get_cached_dir($dir) {
$db = $this->db;
$rows = array();
$sql = $this->get_cached_dir;
$stmt = $db->prepare($sql);
$stmt->bindValue(':dirname', $dir, SQLITE3_TEXT);
$res = $stmt->execute();
while ($row=$res->fetchArray()) {
$rows[] = unserialize(base64_decode($row));
}
return $rows;
}
/**
* use the magical __get() for sql queries
*
* access as easy as $this->{case name}, returns NULL if query is not found
*/
public function __get($name) {
switch($name) {
case 'version_check':
return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = '-1' AND filetime = '-1' AND analyzetime = '-1'";
break;
case 'delete_cache':
return "DELETE FROM $this->table";
break;
case 'set_version':
return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, -1, -1, -1, :val)";
break;
case 'get_id3_data':
return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = :filesize AND filetime = :filetime";
break;
case 'cache_file':
return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, :filesize, :filetime, :atime, :val)";
break;
case 'make_table':
return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) NOT NULL DEFAULT '', dirname VARCHAR(255) NOT NULL DEFAULT '', filesize INT(11) NOT NULL DEFAULT '0', filetime INT(11) NOT NULL DEFAULT '0', analyzetime INT(11) NOT NULL DEFAULT '0', val text not null, PRIMARY KEY (filename, filesize, filetime))";
break;
case 'get_cached_dir':
return "SELECT val FROM $this->table WHERE dirname = :dirname";
break;
}
return null;
}
}

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// getid3.lib.php - part of getID3() // // getid3.lib.php - part of getID3() //
@ -14,13 +15,13 @@
class getid3_lib class getid3_lib
{ {
static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') { public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') {
$returnstring = ''; $returnstring = '';
for ($i = 0; $i < strlen($string); $i++) { for ($i = 0; $i < strlen($string); $i++) {
if ($hex) { if ($hex) {
$returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT); $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
} else { } else {
$returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '<EFBFBD>'); $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '¤');
} }
if ($spaces) { if ($spaces) {
$returnstring .= ' '; $returnstring .= ' ';
@ -35,7 +36,7 @@ class getid3_lib
return $returnstring; return $returnstring;
} }
static function trunc($floatnumber) { public static function trunc($floatnumber) {
// truncates a floating-point number at the decimal point // truncates a floating-point number at the decimal point
// returns int (if possible, otherwise float) // returns int (if possible, otherwise float)
if ($floatnumber >= 1) { if ($floatnumber >= 1) {
@ -45,14 +46,14 @@ class getid3_lib
} else { } else {
$truncatednumber = 0; $truncatednumber = 0;
} }
if (getid3_lib::intValueSupported($truncatednumber)) { if (self::intValueSupported($truncatednumber)) {
$truncatednumber = (int) $truncatednumber; $truncatednumber = (int) $truncatednumber;
} }
return $truncatednumber; return $truncatednumber;
} }
static function safe_inc(&$variable, $increment=1) { public static function safe_inc(&$variable, $increment=1) {
if (isset($variable)) { if (isset($variable)) {
$variable += $increment; $variable += $increment;
} else { } else {
@ -61,14 +62,14 @@ class getid3_lib
return true; return true;
} }
static function CastAsInt($floatnum) { public static function CastAsInt($floatnum) {
// convert to float if not already // convert to float if not already
$floatnum = (float) $floatnum; $floatnum = (float) $floatnum;
// convert a float to type int, only if possible // convert a float to type int, only if possible
if (getid3_lib::trunc($floatnum) == $floatnum) { if (self::trunc($floatnum) == $floatnum) {
// it's not floating point // it's not floating point
if (getid3_lib::intValueSupported($floatnum)) { if (self::intValueSupported($floatnum)) {
// it's within int range // it's within int range
$floatnum = (int) $floatnum; $floatnum = (int) $floatnum;
} }
@ -92,20 +93,20 @@ class getid3_lib
return false; return false;
} }
static function DecimalizeFraction($fraction) { public static function DecimalizeFraction($fraction) {
list($numerator, $denominator) = explode('/', $fraction); list($numerator, $denominator) = explode('/', $fraction);
return $numerator / ($denominator ? $denominator : 1); return $numerator / ($denominator ? $denominator : 1);
} }
static function DecimalBinary2Float($binarynumerator) { public static function DecimalBinary2Float($binarynumerator) {
$numerator = getid3_lib::Bin2Dec($binarynumerator); $numerator = self::Bin2Dec($binarynumerator);
$denominator = getid3_lib::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator))); $denominator = self::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
return ($numerator / $denominator); return ($numerator / $denominator);
} }
static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) { public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
if (strpos($binarypointnumber, '.') === false) { if (strpos($binarypointnumber, '.') === false) {
$binarypointnumber = '0.'.$binarypointnumber; $binarypointnumber = '0.'.$binarypointnumber;
@ -129,23 +130,23 @@ class getid3_lib
} }
static function Float2BinaryDecimal($floatvalue) { public static function Float2BinaryDecimal($floatvalue) {
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
$maxbits = 128; // to how many bits of precision should the calculations be taken? $maxbits = 128; // to how many bits of precision should the calculations be taken?
$intpart = getid3_lib::trunc($floatvalue); $intpart = self::trunc($floatvalue);
$floatpart = abs($floatvalue - $intpart); $floatpart = abs($floatvalue - $intpart);
$pointbitstring = ''; $pointbitstring = '';
while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) { while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
$floatpart *= 2; $floatpart *= 2;
$pointbitstring .= (string) getid3_lib::trunc($floatpart); $pointbitstring .= (string) self::trunc($floatpart);
$floatpart -= getid3_lib::trunc($floatpart); $floatpart -= self::trunc($floatpart);
} }
$binarypointnumber = decbin($intpart).'.'.$pointbitstring; $binarypointnumber = decbin($intpart).'.'.$pointbitstring;
return $binarypointnumber; return $binarypointnumber;
} }
static function Float2String($floatvalue, $bits) { public static function Float2String($floatvalue, $bits) {
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
switch ($bits) { switch ($bits) {
case 32: case 32:
@ -167,26 +168,26 @@ class getid3_lib
} else { } else {
$signbit = '1'; $signbit = '1';
} }
$normalizedbinary = getid3_lib::NormalizeBinaryPoint(getid3_lib::Float2BinaryDecimal($floatvalue), $fractionbits); $normalizedbinary = self::NormalizeBinaryPoint(self::Float2BinaryDecimal($floatvalue), $fractionbits);
$biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent $biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
$exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT); $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
$fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT); $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
return getid3_lib::BigEndian2String(getid3_lib::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false); return self::BigEndian2String(self::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
} }
static function LittleEndian2Float($byteword) { public static function LittleEndian2Float($byteword) {
return getid3_lib::BigEndian2Float(strrev($byteword)); return self::BigEndian2Float(strrev($byteword));
} }
static function BigEndian2Float($byteword) { public static function BigEndian2Float($byteword) {
// ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
// http://www.psc.edu/general/software/packages/ieee/ieee.html // http://www.psc.edu/general/software/packages/ieee/ieee.html
// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
$bitword = getid3_lib::BigEndian2Bin($byteword); $bitword = self::BigEndian2Bin($byteword);
if (!$bitword) { if (!$bitword) {
return 0; return 0;
} }
@ -209,8 +210,8 @@ class getid3_lib
$exponentstring = substr($bitword, 1, 15); $exponentstring = substr($bitword, 1, 15);
$isnormalized = intval($bitword{16}); $isnormalized = intval($bitword{16});
$fractionstring = substr($bitword, 17, 63); $fractionstring = substr($bitword, 17, 63);
$exponent = pow(2, getid3_lib::Bin2Dec($exponentstring) - 16383); $exponent = pow(2, self::Bin2Dec($exponentstring) - 16383);
$fraction = $isnormalized + getid3_lib::DecimalBinary2Float($fractionstring); $fraction = $isnormalized + self::DecimalBinary2Float($fractionstring);
$floatvalue = $exponent * $fraction; $floatvalue = $exponent * $fraction;
if ($signbit == '1') { if ($signbit == '1') {
$floatvalue *= -1; $floatvalue *= -1;
@ -224,8 +225,8 @@ class getid3_lib
} }
$exponentstring = substr($bitword, 1, $exponentbits); $exponentstring = substr($bitword, 1, $exponentbits);
$fractionstring = substr($bitword, $exponentbits + 1, $fractionbits); $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
$exponent = getid3_lib::Bin2Dec($exponentstring); $exponent = self::Bin2Dec($exponentstring);
$fraction = getid3_lib::Bin2Dec($fractionstring); $fraction = self::Bin2Dec($fractionstring);
if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) { if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
// Not a Number // Not a Number
@ -245,12 +246,12 @@ class getid3_lib
$floatvalue = ($signbit ? 0 : -0); $floatvalue = ($signbit ? 0 : -0);
} elseif (($exponent == 0) && ($fraction != 0)) { } elseif (($exponent == 0) && ($fraction != 0)) {
// These are 'unnormalized' values // These are 'unnormalized' values
$floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fractionstring); $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * self::DecimalBinary2Float($fractionstring);
if ($signbit == '1') { if ($signbit == '1') {
$floatvalue *= -1; $floatvalue *= -1;
} }
} elseif ($exponent != 0) { } elseif ($exponent != 0) {
$floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fractionstring)); $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + self::DecimalBinary2Float($fractionstring));
if ($signbit == '1') { if ($signbit == '1') {
$floatvalue *= -1; $floatvalue *= -1;
} }
@ -259,7 +260,7 @@ class getid3_lib
} }
static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) { public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
$intvalue = 0; $intvalue = 0;
$bytewordlen = strlen($byteword); $bytewordlen = strlen($byteword);
if ($bytewordlen == 0) { if ($bytewordlen == 0) {
@ -281,19 +282,19 @@ class getid3_lib
$intvalue = 0 - ($intvalue & ($signMaskBit - 1)); $intvalue = 0 - ($intvalue & ($signMaskBit - 1));
} }
} else { } else {
throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in getid3_lib::BigEndian2Int()'); throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()');
} }
} }
return getid3_lib::CastAsInt($intvalue); return self::CastAsInt($intvalue);
} }
static function LittleEndian2Int($byteword, $signed=false) { public static function LittleEndian2Int($byteword, $signed=false) {
return getid3_lib::BigEndian2Int(strrev($byteword), false, $signed); return self::BigEndian2Int(strrev($byteword), false, $signed);
} }
static function BigEndian2Bin($byteword) { public static function BigEndian2Bin($byteword) {
$binvalue = ''; $binvalue = '';
$bytewordlen = strlen($byteword); $bytewordlen = strlen($byteword);
for ($i = 0; $i < $bytewordlen; $i++) { for ($i = 0; $i < $bytewordlen; $i++) {
@ -303,15 +304,15 @@ class getid3_lib
} }
static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) { public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
if ($number < 0) { if ($number < 0) {
throw new Exception('ERROR: getid3_lib::BigEndian2String() does not support negative numbers'); throw new Exception('ERROR: self::BigEndian2String() does not support negative numbers');
} }
$maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF); $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
$intstring = ''; $intstring = '';
if ($signed) { if ($signed) {
if ($minbytes > PHP_INT_SIZE) { if ($minbytes > PHP_INT_SIZE) {
throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits in getid3_lib::BigEndian2String()'); throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits in self::BigEndian2String()');
} }
$number = $number & (0x80 << (8 * ($minbytes - 1))); $number = $number & (0x80 << (8 * ($minbytes - 1)));
} }
@ -324,7 +325,7 @@ class getid3_lib
} }
static function Dec2Bin($number) { public static function Dec2Bin($number) {
while ($number >= 256) { while ($number >= 256) {
$bytes[] = (($number / 256) - (floor($number / 256))) * 256; $bytes[] = (($number / 256) - (floor($number / 256))) * 256;
$number = floor($number / 256); $number = floor($number / 256);
@ -338,7 +339,7 @@ class getid3_lib
} }
static function Bin2Dec($binstring, $signed=false) { public static function Bin2Dec($binstring, $signed=false) {
$signmult = 1; $signmult = 1;
if ($signed) { if ($signed) {
if ($binstring{0} == '1') { if ($binstring{0} == '1') {
@ -350,22 +351,22 @@ class getid3_lib
for ($i = 0; $i < strlen($binstring); $i++) { for ($i = 0; $i < strlen($binstring); $i++) {
$decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i); $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
} }
return getid3_lib::CastAsInt($decvalue * $signmult); return self::CastAsInt($decvalue * $signmult);
} }
static function Bin2String($binstring) { public static function Bin2String($binstring) {
// return 'hi' for input of '0110100001101001' // return 'hi' for input of '0110100001101001'
$string = ''; $string = '';
$binstringreversed = strrev($binstring); $binstringreversed = strrev($binstring);
for ($i = 0; $i < strlen($binstringreversed); $i += 8) { for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
$string = chr(getid3_lib::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string; $string = chr(self::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
} }
return $string; return $string;
} }
static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) { public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
$intstring = ''; $intstring = '';
while ($number > 0) { while ($number > 0) {
if ($synchsafe) { if ($synchsafe) {
@ -380,8 +381,8 @@ class getid3_lib
} }
static function array_merge_clobber($array1, $array2) { public static function array_merge_clobber($array1, $array2) {
// written by kc<EFBFBD>hireability*com // written by kcØhireability*com
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
if (!is_array($array1) || !is_array($array2)) { if (!is_array($array1) || !is_array($array2)) {
return false; return false;
@ -389,7 +390,7 @@ class getid3_lib
$newarray = $array1; $newarray = $array1;
foreach ($array2 as $key => $val) { foreach ($array2 as $key => $val) {
if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
$newarray[$key] = getid3_lib::array_merge_clobber($newarray[$key], $val); $newarray[$key] = self::array_merge_clobber($newarray[$key], $val);
} else { } else {
$newarray[$key] = $val; $newarray[$key] = $val;
} }
@ -398,14 +399,14 @@ class getid3_lib
} }
static function array_merge_noclobber($array1, $array2) { public static function array_merge_noclobber($array1, $array2) {
if (!is_array($array1) || !is_array($array2)) { if (!is_array($array1) || !is_array($array2)) {
return false; return false;
} }
$newarray = $array1; $newarray = $array1;
foreach ($array2 as $key => $val) { foreach ($array2 as $key => $val) {
if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
$newarray[$key] = getid3_lib::array_merge_noclobber($newarray[$key], $val); $newarray[$key] = self::array_merge_noclobber($newarray[$key], $val);
} elseif (!isset($newarray[$key])) { } elseif (!isset($newarray[$key])) {
$newarray[$key] = $val; $newarray[$key] = $val;
} }
@ -414,7 +415,7 @@ class getid3_lib
} }
static function ksort_recursive(&$theArray) { public static function ksort_recursive(&$theArray) {
ksort($theArray); ksort($theArray);
foreach ($theArray as $key => $value) { foreach ($theArray as $key => $value) {
if (is_array($value)) { if (is_array($value)) {
@ -424,7 +425,7 @@ class getid3_lib
return true; return true;
} }
static function fileextension($filename, $numextensions=1) { public static function fileextension($filename, $numextensions=1) {
if (strstr($filename, '.')) { if (strstr($filename, '.')) {
$reversedfilename = strrev($filename); $reversedfilename = strrev($filename);
$offset = 0; $offset = 0;
@ -440,58 +441,56 @@ class getid3_lib
} }
static function PlaytimeString($seconds) { public static function PlaytimeString($seconds) {
$sign = (($seconds < 0) ? '-' : ''); $sign = (($seconds < 0) ? '-' : '');
$seconds = abs($seconds); $seconds = round(abs($seconds));
$H = floor( $seconds / 3600); $H = (int) floor( $seconds / 3600);
$M = floor(($seconds - (3600 * $H) ) / 60); $M = (int) floor(($seconds - (3600 * $H) ) / 60);
$S = round( $seconds - (3600 * $H) - (60 * $M) ); $S = (int) round( $seconds - (3600 * $H) - (60 * $M) );
return $sign.($H ? $H.':' : '').($H ? str_pad($M, 2, '0', STR_PAD_LEFT) : intval($M)).':'.str_pad($S, 2, 0, STR_PAD_LEFT); return $sign.($H ? $H.':' : '').($H ? str_pad($M, 2, '0', STR_PAD_LEFT) : intval($M)).':'.str_pad($S, 2, 0, STR_PAD_LEFT);
} }
static function DateMac2Unix($macdate) { public static function DateMac2Unix($macdate) {
// Macintosh timestamp: seconds since 00:00h January 1, 1904 // Macintosh timestamp: seconds since 00:00h January 1, 1904
// UNIX timestamp: seconds since 00:00h January 1, 1970 // UNIX timestamp: seconds since 00:00h January 1, 1970
return getid3_lib::CastAsInt($macdate - 2082844800); return self::CastAsInt($macdate - 2082844800);
} }
static function FixedPoint8_8($rawdata) { public static function FixedPoint8_8($rawdata) {
return getid3_lib::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8)); return self::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (self::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
} }
static function FixedPoint16_16($rawdata) { public static function FixedPoint16_16($rawdata) {
return getid3_lib::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16)); return self::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (self::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
} }
static function FixedPoint2_30($rawdata) { public static function FixedPoint2_30($rawdata) {
$binarystring = getid3_lib::BigEndian2Bin($rawdata); $binarystring = self::BigEndian2Bin($rawdata);
return getid3_lib::Bin2Dec(substr($binarystring, 0, 2)) + (float) (getid3_lib::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30)); return self::Bin2Dec(substr($binarystring, 0, 2)) + (float) (self::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30));
} }
static function CreateDeepArray($ArrayPath, $Separator, $Value) { public static function CreateDeepArray($ArrayPath, $Separator, $Value) {
// assigns $Value to a nested array path: // assigns $Value to a nested array path:
// $foo = getid3_lib::CreateDeepArray('/path/to/my', '/', 'file.txt') // $foo = self::CreateDeepArray('/path/to/my', '/', 'file.txt')
// is the same as: // is the same as:
// $foo = array('path'=>array('to'=>'array('my'=>array('file.txt')))); // $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
// or // or
// $foo['path']['to']['my'] = 'file.txt'; // $foo['path']['to']['my'] = 'file.txt';
while ($ArrayPath && ($ArrayPath{0} == $Separator)) { $ArrayPath = ltrim($ArrayPath, $Separator);
$ArrayPath = substr($ArrayPath, 1);
}
if (($pos = strpos($ArrayPath, $Separator)) !== false) { if (($pos = strpos($ArrayPath, $Separator)) !== false) {
$ReturnedArray[substr($ArrayPath, 0, $pos)] = getid3_lib::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value); $ReturnedArray[substr($ArrayPath, 0, $pos)] = self::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
} else { } else {
$ReturnedArray[$ArrayPath] = $Value; $ReturnedArray[$ArrayPath] = $Value;
} }
return $ReturnedArray; return $ReturnedArray;
} }
static function array_max($arraydata, $returnkey=false) { public static function array_max($arraydata, $returnkey=false) {
$maxvalue = false; $maxvalue = false;
$maxkey = false; $maxkey = false;
foreach ($arraydata as $key => $value) { foreach ($arraydata as $key => $value) {
@ -505,7 +504,7 @@ class getid3_lib
return ($returnkey ? $maxkey : $maxvalue); return ($returnkey ? $maxkey : $maxvalue);
} }
static function array_min($arraydata, $returnkey=false) { public static function array_min($arraydata, $returnkey=false) {
$minvalue = false; $minvalue = false;
$minkey = false; $minkey = false;
foreach ($arraydata as $key => $value) { foreach ($arraydata as $key => $value) {
@ -519,17 +518,20 @@ class getid3_lib
return ($returnkey ? $minkey : $minvalue); return ($returnkey ? $minkey : $minvalue);
} }
static function XML2array($XMLstring) { public static function XML2array($XMLstring) {
if (function_exists('simplexml_load_string')) { if (function_exists('simplexml_load_string') && function_exists('libxml_disable_entity_loader')) {
if (function_exists('get_object_vars')) { // http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html
$XMLobject = simplexml_load_string($XMLstring); // https://core.trac.wordpress.org/changeset/29378
return self::SimpleXMLelement2array($XMLobject); $loader = libxml_disable_entity_loader(true);
} $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT);
} $return = self::SimpleXMLelement2array($XMLobject);
libxml_disable_entity_loader($loader);
return $return;
}
return false; return false;
} }
static function SimpleXMLelement2array($XMLobject) { public static function SimpleXMLelement2array($XMLobject) {
if (!is_object($XMLobject) && !is_array($XMLobject)) { if (!is_object($XMLobject) && !is_array($XMLobject)) {
return $XMLobject; return $XMLobject;
} }
@ -541,11 +543,11 @@ class getid3_lib
} }
// Allan Hansen <ah<EFBFBD>artemis*dk> // Allan Hansen <ahØartemis*dk>
// getid3_lib::md5_data() - returns md5sum for a file from startuing position to absolute end position // self::md5_data() - returns md5sum for a file from startuing position to absolute end position
static function hash_data($file, $offset, $end, $algorithm) { public static function hash_data($file, $offset, $end, $algorithm) {
static $tempdir = ''; static $tempdir = '';
if (!getid3_lib::intValueSupported($end)) { if (!self::intValueSupported($end)) {
return false; return false;
} }
switch ($algorithm) { switch ($algorithm) {
@ -564,7 +566,7 @@ class getid3_lib
break; break;
default: default:
throw new Exception('Invalid algorithm ('.$algorithm.') in getid3_lib::hash_data()'); throw new Exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
break; break;
} }
$size = $end - $offset; $size = $end - $offset;
@ -581,10 +583,10 @@ class getid3_lib
foreach ($RequiredFiles as $required_file) { foreach ($RequiredFiles as $required_file) {
if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
// helper apps not available - fall back to old method // helper apps not available - fall back to old method
break; break 2;
} }
} }
$commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' "'.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).'" | '; $commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' '.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).' | ';
$commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | '; $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
$commandline .= GETID3_HELPERAPPSDIR.$windows_call; $commandline .= GETID3_HELPERAPPSDIR.$windows_call;
@ -604,7 +606,7 @@ class getid3_lib
if (empty($tempdir)) { if (empty($tempdir)) {
// yes this is ugly, feel free to suggest a better way // yes this is ugly, feel free to suggest a better way
require_once(dirname(__FILE__).'/getid3.php'); require_once(dirname(__FILE__) . '/getid3.php');
$getid3_temp = new getID3(); $getid3_temp = new getID3();
$tempdir = $getid3_temp->tempdir; $tempdir = $getid3_temp->tempdir;
unset($getid3_temp); unset($getid3_temp);
@ -620,22 +622,22 @@ class getid3_lib
// copy parts of file // copy parts of file
try { try {
getid3_lib::CopyFileParts($file, $data_filename, $offset, $end - $offset); self::CopyFileParts($file, $data_filename, $offset, $end - $offset);
$result = $hash_function($data_filename); $result = $hash_function($data_filename);
} catch (Exception $e) { } catch (Exception $e) {
throw new Exception('getid3_lib::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage()); throw new Exception('self::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage());
} }
unlink($data_filename); unlink($data_filename);
return $result; return $result;
} }
static function CopyFileParts($filename_source, $filename_dest, $offset, $length) { public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) {
if (!getid3_lib::intValueSupported($offset + $length)) { if (!self::intValueSupported($offset + $length)) {
throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit'); throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit');
} }
if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) { if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) {
if (($fp_dest = fopen($filename_dest, 'wb'))) { if (($fp_dest = fopen($filename_dest, 'wb'))) {
if (fseek($fp_src, $offset, SEEK_SET) == 0) { if (fseek($fp_src, $offset) == 0) {
$byteslefttowrite = $length; $byteslefttowrite = $length;
while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) { while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) {
$byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite); $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite);
@ -656,7 +658,7 @@ class getid3_lib
return false; return false;
} }
static function iconv_fallback_int_utf8($charval) { public static function iconv_fallback_int_utf8($charval) {
if ($charval < 128) { if ($charval < 128) {
// 0bbbbbbb // 0bbbbbbb
$newcharstring = chr($charval); $newcharstring = chr($charval);
@ -680,7 +682,7 @@ class getid3_lib
} }
// ISO-8859-1 => UTF-8 // ISO-8859-1 => UTF-8
static function iconv_fallback_iso88591_utf8($string, $bom=false) { public static function iconv_fallback_iso88591_utf8($string, $bom=false) {
if (function_exists('utf8_encode')) { if (function_exists('utf8_encode')) {
return utf8_encode($string); return utf8_encode($string);
} }
@ -691,13 +693,13 @@ class getid3_lib
} }
for ($i = 0; $i < strlen($string); $i++) { for ($i = 0; $i < strlen($string); $i++) {
$charval = ord($string{$i}); $charval = ord($string{$i});
$newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval); $newcharstring .= self::iconv_fallback_int_utf8($charval);
} }
return $newcharstring; return $newcharstring;
} }
// ISO-8859-1 => UTF-16BE // ISO-8859-1 => UTF-16BE
static function iconv_fallback_iso88591_utf16be($string, $bom=false) { public static function iconv_fallback_iso88591_utf16be($string, $bom=false) {
$newcharstring = ''; $newcharstring = '';
if ($bom) { if ($bom) {
$newcharstring .= "\xFE\xFF"; $newcharstring .= "\xFE\xFF";
@ -709,7 +711,7 @@ class getid3_lib
} }
// ISO-8859-1 => UTF-16LE // ISO-8859-1 => UTF-16LE
static function iconv_fallback_iso88591_utf16le($string, $bom=false) { public static function iconv_fallback_iso88591_utf16le($string, $bom=false) {
$newcharstring = ''; $newcharstring = '';
if ($bom) { if ($bom) {
$newcharstring .= "\xFF\xFE"; $newcharstring .= "\xFF\xFE";
@ -721,12 +723,12 @@ class getid3_lib
} }
// ISO-8859-1 => UTF-16LE (BOM) // ISO-8859-1 => UTF-16LE (BOM)
static function iconv_fallback_iso88591_utf16($string) { public static function iconv_fallback_iso88591_utf16($string) {
return getid3_lib::iconv_fallback_iso88591_utf16le($string, true); return self::iconv_fallback_iso88591_utf16le($string, true);
} }
// UTF-8 => ISO-8859-1 // UTF-8 => ISO-8859-1
static function iconv_fallback_utf8_iso88591($string) { public static function iconv_fallback_utf8_iso88591($string) {
if (function_exists('utf8_decode')) { if (function_exists('utf8_decode')) {
return utf8_decode($string); return utf8_decode($string);
} }
@ -770,7 +772,7 @@ class getid3_lib
} }
// UTF-8 => UTF-16BE // UTF-8 => UTF-16BE
static function iconv_fallback_utf8_utf16be($string, $bom=false) { public static function iconv_fallback_utf8_utf16be($string, $bom=false) {
$newcharstring = ''; $newcharstring = '';
if ($bom) { if ($bom) {
$newcharstring .= "\xFE\xFF"; $newcharstring .= "\xFE\xFF";
@ -806,14 +808,14 @@ class getid3_lib
$offset += 1; $offset += 1;
} }
if ($charval !== false) { if ($charval !== false) {
$newcharstring .= (($charval < 65536) ? getid3_lib::BigEndian2String($charval, 2) : "\x00".'?'); $newcharstring .= (($charval < 65536) ? self::BigEndian2String($charval, 2) : "\x00".'?');
} }
} }
return $newcharstring; return $newcharstring;
} }
// UTF-8 => UTF-16LE // UTF-8 => UTF-16LE
static function iconv_fallback_utf8_utf16le($string, $bom=false) { public static function iconv_fallback_utf8_utf16le($string, $bom=false) {
$newcharstring = ''; $newcharstring = '';
if ($bom) { if ($bom) {
$newcharstring .= "\xFF\xFE"; $newcharstring .= "\xFF\xFE";
@ -849,96 +851,96 @@ class getid3_lib
$offset += 1; $offset += 1;
} }
if ($charval !== false) { if ($charval !== false) {
$newcharstring .= (($charval < 65536) ? getid3_lib::LittleEndian2String($charval, 2) : '?'."\x00"); $newcharstring .= (($charval < 65536) ? self::LittleEndian2String($charval, 2) : '?'."\x00");
} }
} }
return $newcharstring; return $newcharstring;
} }
// UTF-8 => UTF-16LE (BOM) // UTF-8 => UTF-16LE (BOM)
static function iconv_fallback_utf8_utf16($string) { public static function iconv_fallback_utf8_utf16($string) {
return getid3_lib::iconv_fallback_utf8_utf16le($string, true); return self::iconv_fallback_utf8_utf16le($string, true);
} }
// UTF-16BE => UTF-8 // UTF-16BE => UTF-8
static function iconv_fallback_utf16be_utf8($string) { public static function iconv_fallback_utf16be_utf8($string) {
if (substr($string, 0, 2) == "\xFE\xFF") { if (substr($string, 0, 2) == "\xFE\xFF") {
// strip BOM // strip BOM
$string = substr($string, 2); $string = substr($string, 2);
} }
$newcharstring = ''; $newcharstring = '';
for ($i = 0; $i < strlen($string); $i += 2) { for ($i = 0; $i < strlen($string); $i += 2) {
$charval = getid3_lib::BigEndian2Int(substr($string, $i, 2)); $charval = self::BigEndian2Int(substr($string, $i, 2));
$newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval); $newcharstring .= self::iconv_fallback_int_utf8($charval);
} }
return $newcharstring; return $newcharstring;
} }
// UTF-16LE => UTF-8 // UTF-16LE => UTF-8
static function iconv_fallback_utf16le_utf8($string) { public static function iconv_fallback_utf16le_utf8($string) {
if (substr($string, 0, 2) == "\xFF\xFE") { if (substr($string, 0, 2) == "\xFF\xFE") {
// strip BOM // strip BOM
$string = substr($string, 2); $string = substr($string, 2);
} }
$newcharstring = ''; $newcharstring = '';
for ($i = 0; $i < strlen($string); $i += 2) { for ($i = 0; $i < strlen($string); $i += 2) {
$charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2)); $charval = self::LittleEndian2Int(substr($string, $i, 2));
$newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval); $newcharstring .= self::iconv_fallback_int_utf8($charval);
} }
return $newcharstring; return $newcharstring;
} }
// UTF-16BE => ISO-8859-1 // UTF-16BE => ISO-8859-1
static function iconv_fallback_utf16be_iso88591($string) { public static function iconv_fallback_utf16be_iso88591($string) {
if (substr($string, 0, 2) == "\xFE\xFF") { if (substr($string, 0, 2) == "\xFE\xFF") {
// strip BOM // strip BOM
$string = substr($string, 2); $string = substr($string, 2);
} }
$newcharstring = ''; $newcharstring = '';
for ($i = 0; $i < strlen($string); $i += 2) { for ($i = 0; $i < strlen($string); $i += 2) {
$charval = getid3_lib::BigEndian2Int(substr($string, $i, 2)); $charval = self::BigEndian2Int(substr($string, $i, 2));
$newcharstring .= (($charval < 256) ? chr($charval) : '?'); $newcharstring .= (($charval < 256) ? chr($charval) : '?');
} }
return $newcharstring; return $newcharstring;
} }
// UTF-16LE => ISO-8859-1 // UTF-16LE => ISO-8859-1
static function iconv_fallback_utf16le_iso88591($string) { public static function iconv_fallback_utf16le_iso88591($string) {
if (substr($string, 0, 2) == "\xFF\xFE") { if (substr($string, 0, 2) == "\xFF\xFE") {
// strip BOM // strip BOM
$string = substr($string, 2); $string = substr($string, 2);
} }
$newcharstring = ''; $newcharstring = '';
for ($i = 0; $i < strlen($string); $i += 2) { for ($i = 0; $i < strlen($string); $i += 2) {
$charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2)); $charval = self::LittleEndian2Int(substr($string, $i, 2));
$newcharstring .= (($charval < 256) ? chr($charval) : '?'); $newcharstring .= (($charval < 256) ? chr($charval) : '?');
} }
return $newcharstring; return $newcharstring;
} }
// UTF-16 (BOM) => ISO-8859-1 // UTF-16 (BOM) => ISO-8859-1
static function iconv_fallback_utf16_iso88591($string) { public static function iconv_fallback_utf16_iso88591($string) {
$bom = substr($string, 0, 2); $bom = substr($string, 0, 2);
if ($bom == "\xFE\xFF") { if ($bom == "\xFE\xFF") {
return getid3_lib::iconv_fallback_utf16be_iso88591(substr($string, 2)); return self::iconv_fallback_utf16be_iso88591(substr($string, 2));
} elseif ($bom == "\xFF\xFE") { } elseif ($bom == "\xFF\xFE") {
return getid3_lib::iconv_fallback_utf16le_iso88591(substr($string, 2)); return self::iconv_fallback_utf16le_iso88591(substr($string, 2));
} }
return $string; return $string;
} }
// UTF-16 (BOM) => UTF-8 // UTF-16 (BOM) => UTF-8
static function iconv_fallback_utf16_utf8($string) { public static function iconv_fallback_utf16_utf8($string) {
$bom = substr($string, 0, 2); $bom = substr($string, 0, 2);
if ($bom == "\xFE\xFF") { if ($bom == "\xFE\xFF") {
return getid3_lib::iconv_fallback_utf16be_utf8(substr($string, 2)); return self::iconv_fallback_utf16be_utf8(substr($string, 2));
} elseif ($bom == "\xFF\xFE") { } elseif ($bom == "\xFF\xFE") {
return getid3_lib::iconv_fallback_utf16le_utf8(substr($string, 2)); return self::iconv_fallback_utf16le_utf8(substr($string, 2));
} }
return $string; return $string;
} }
static function iconv_fallback($in_charset, $out_charset, $string) { public static function iconv_fallback($in_charset, $out_charset, $string) {
if ($in_charset == $out_charset) { if ($in_charset == $out_charset) {
return $string; return $string;
@ -981,13 +983,26 @@ class getid3_lib
} }
if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) { if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) {
$ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)]; $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
return getid3_lib::$ConversionFunction($string); return self::$ConversionFunction($string);
} }
throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset); throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
} }
public static function recursiveMultiByteCharString2HTML($data, $charset='ISO-8859-1') {
if (is_string($data)) {
return self::MultiByteCharString2HTML($data, $charset);
} elseif (is_array($data)) {
$return_data = array();
foreach ($data as $key => $value) {
$return_data[$key] = self::recursiveMultiByteCharString2HTML($value, $charset);
}
return $return_data;
}
// integer, float, objects, resources, etc
return $data;
}
static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') { public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
$string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string $string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string
$HTMLstring = ''; $HTMLstring = '';
@ -1052,7 +1067,7 @@ class getid3_lib
case 'UTF-16LE': case 'UTF-16LE':
for ($i = 0; $i < strlen($string); $i += 2) { for ($i = 0; $i < strlen($string); $i += 2) {
$charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2)); $charval = self::LittleEndian2Int(substr($string, $i, 2));
if (($charval >= 32) && ($charval <= 127)) { if (($charval >= 32) && ($charval <= 127)) {
$HTMLstring .= chr($charval); $HTMLstring .= chr($charval);
} else { } else {
@ -1063,7 +1078,7 @@ class getid3_lib
case 'UTF-16BE': case 'UTF-16BE':
for ($i = 0; $i < strlen($string); $i += 2) { for ($i = 0; $i < strlen($string); $i += 2) {
$charval = getid3_lib::BigEndian2Int(substr($string, $i, 2)); $charval = self::BigEndian2Int(substr($string, $i, 2));
if (($charval >= 32) && ($charval <= 127)) { if (($charval >= 32) && ($charval <= 127)) {
$HTMLstring .= chr($charval); $HTMLstring .= chr($charval);
} else { } else {
@ -1081,7 +1096,7 @@ class getid3_lib
static function RGADnameLookup($namecode) { public static function RGADnameLookup($namecode) {
static $RGADname = array(); static $RGADname = array();
if (empty($RGADname)) { if (empty($RGADname)) {
$RGADname[0] = 'not set'; $RGADname[0] = 'not set';
@ -1093,7 +1108,7 @@ class getid3_lib
} }
static function RGADoriginatorLookup($originatorcode) { public static function RGADoriginatorLookup($originatorcode) {
static $RGADoriginator = array(); static $RGADoriginator = array();
if (empty($RGADoriginator)) { if (empty($RGADoriginator)) {
$RGADoriginator[0] = 'unspecified'; $RGADoriginator[0] = 'unspecified';
@ -1106,7 +1121,7 @@ class getid3_lib
} }
static function RGADadjustmentLookup($rawadjustment, $signbit) { public static function RGADadjustmentLookup($rawadjustment, $signbit) {
$adjustment = $rawadjustment / 10; $adjustment = $rawadjustment / 10;
if ($signbit == 1) { if ($signbit == 1) {
$adjustment *= -1; $adjustment *= -1;
@ -1115,7 +1130,7 @@ class getid3_lib
} }
static function RGADgainString($namecode, $originatorcode, $replaygain) { public static function RGADgainString($namecode, $originatorcode, $replaygain) {
if ($replaygain < 0) { if ($replaygain < 0) {
$signbit = '1'; $signbit = '1';
} else { } else {
@ -1130,16 +1145,16 @@ class getid3_lib
return $gainstring; return $gainstring;
} }
static function RGADamplitude2dB($amplitude) { public static function RGADamplitude2dB($amplitude) {
return 20 * log10($amplitude); return 20 * log10($amplitude);
} }
static function GetDataImageSize($imgData, &$imageinfo) { public static function GetDataImageSize($imgData, &$imageinfo=array()) {
static $tempdir = ''; static $tempdir = '';
if (empty($tempdir)) { if (empty($tempdir)) {
// yes this is ugly, feel free to suggest a better way // yes this is ugly, feel free to suggest a better way
require_once(dirname(__FILE__).'/getid3.php'); require_once(dirname(__FILE__) . '/getid3.php');
$getid3_temp = new getID3(); $getid3_temp = new getID3();
$tempdir = $getid3_temp->tempdir; $tempdir = $getid3_temp->tempdir;
unset($getid3_temp); unset($getid3_temp);
@ -1149,14 +1164,21 @@ class getid3_lib
if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) { if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) {
fwrite($tmp, $imgData); fwrite($tmp, $imgData);
fclose($tmp); fclose($tmp);
$GetDataImageSize = @GetImageSize($tempfilename, $imageinfo); $GetDataImageSize = @getimagesize($tempfilename, $imageinfo);
$GetDataImageSize['height'] = $GetDataImageSize[0];
$GetDataImageSize['width'] = $GetDataImageSize[1];
} }
unlink($tempfilename); unlink($tempfilename);
} }
return $GetDataImageSize; return $GetDataImageSize;
} }
static function ImageTypesLookup($imagetypeid) { public static function ImageExtFromMime($mime_type) {
// temporary way, works OK for now, but should be reworked in the future
return str_replace(array('image/', 'x-', 'jpeg'), array('', '', 'jpg'), $mime_type);
}
public static function ImageTypesLookup($imagetypeid) {
static $ImageTypesLookup = array(); static $ImageTypesLookup = array();
if (empty($ImageTypesLookup)) { if (empty($ImageTypesLookup)) {
$ImageTypesLookup[1] = 'gif'; $ImageTypesLookup[1] = 'gif';
@ -1177,7 +1199,7 @@ class getid3_lib
return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : ''); return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : '');
} }
static function CopyTagsToComments(&$ThisFileInfo) { public static function CopyTagsToComments(&$ThisFileInfo) {
// Copy all entries from ['tags'] into common ['comments'] // Copy all entries from ['tags'] into common ['comments']
if (!empty($ThisFileInfo['tags'])) { if (!empty($ThisFileInfo['tags'])) {
@ -1205,16 +1227,21 @@ class getid3_lib
$newvaluelength = strlen(trim($value)); $newvaluelength = strlen(trim($value));
foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) { foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
$oldvaluelength = strlen(trim($existingvalue)); $oldvaluelength = strlen(trim($existingvalue));
if (($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) { if ((strlen($existingvalue) > 10) && ($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
$ThisFileInfo['comments'][$tagname][$existingkey] = trim($value); $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
break 2; //break 2;
break;
} }
} }
} }
if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) { if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
$value = (is_string($value) ? trim($value) : $value); $value = (is_string($value) ? trim($value) : $value);
$ThisFileInfo['comments'][$tagname][] = $value; if (!is_numeric($key)) {
$ThisFileInfo['comments'][$tagname][$key] = $value;
} else {
$ThisFileInfo['comments'][$tagname][] = $value;
}
} }
} }
} }
@ -1222,26 +1249,29 @@ class getid3_lib
} }
// Copy to ['comments_html'] // Copy to ['comments_html']
foreach ($ThisFileInfo['comments'] as $field => $values) { if (!empty($ThisFileInfo['comments'])) {
if ($field == 'picture') { foreach ($ThisFileInfo['comments'] as $field => $values) {
// pictures can take up a lot of space, and we don't need multiple copies of them if ($field == 'picture') {
// let there be a single copy in [comments][picture], and not elsewhere // pictures can take up a lot of space, and we don't need multiple copies of them
continue; // let there be a single copy in [comments][picture], and not elsewhere
} continue;
foreach ($values as $index => $value) { }
if (is_array($value)) { foreach ($values as $index => $value) {
$ThisFileInfo['comments_html'][$field][$index] = $value; if (is_array($value)) {
} else { $ThisFileInfo['comments_html'][$field][$index] = $value;
$ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $ThisFileInfo['encoding'])); } else {
$ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
}
} }
} }
} }
} }
return true; return true;
} }
static function EmbeddedLookup($key, $begin, $end, $file, $name) { public static function EmbeddedLookup($key, $begin, $end, $file, $name) {
// Cached // Cached
static $cache; static $cache;
@ -1287,7 +1317,7 @@ class getid3_lib
return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : ''); return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
} }
static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) { public static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
global $GETID3_ERRORARRAY; global $GETID3_ERRORARRAY;
if (file_exists($filename)) { if (file_exists($filename)) {
@ -1311,6 +1341,40 @@ class getid3_lib
return trim($string, "\x00"); return trim($string, "\x00");
} }
} public static function getFileSizeSyscall($path) {
$filesize = false;
?> if (GETID3_OS_ISWINDOWS) {
if (class_exists('COM')) { // From PHP 5.3.15 and 5.4.5, COM and DOTNET is no longer built into the php core.you have to add COM support in php.ini:
$filesystem = new COM('Scripting.FileSystemObject');
$file = $filesystem->GetFile($path);
$filesize = $file->Size();
unset($filesystem, $file);
} else {
$commandline = 'for %I in ('.escapeshellarg($path).') do @echo %~zI';
}
} else {
$commandline = 'ls -l '.escapeshellarg($path).' | awk \'{print $5}\'';
}
if (isset($commandline)) {
$output = trim(`$commandline`);
if (ctype_digit($output)) {
$filesize = (float) $output;
}
}
return $filesize;
}
/**
* Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268)
* @param string $path A path.
* @param string $suffix If the name component ends in suffix this will also be cut off.
* @return string
*/
public static function mb_basename($path, $suffix = null) {
$splited = preg_split('#/#', rtrim($path, '/ '));
return substr(basename('X'.$splited[count($splited) - 1], $suffix), 1);
}
}

View file

@ -3,23 +3,36 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// Please see readme.txt for more information // // Please see readme.txt for more information //
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// define a constant rather than looking up every time it is needed
if (!defined('GETID3_OS_ISWINDOWS')) {
define('GETID3_OS_ISWINDOWS', (stripos(PHP_OS, 'WIN') === 0));
}
// Get base path of getID3() - ONCE
if (!defined('GETID3_INCLUDEPATH')) {
define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
}
// Workaround Bug #39923 (https://bugs.php.net/bug.php?id=39923)
if (!defined('IMG_JPG') && defined('IMAGETYPE_JPEG')) {
define('IMG_JPG', IMAGETYPE_JPEG);
}
// attempt to define temp dir as something flexible but reliable // attempt to define temp dir as something flexible but reliable
$temp_dir = ini_get('upload_tmp_dir'); $temp_dir = ini_get('upload_tmp_dir');
if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) { if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
$temp_dir = ''; $temp_dir = '';
} }
if (!$temp_dir && function_exists('sys_get_temp_dir')) { if (!$temp_dir && function_exists('sys_get_temp_dir')) { // sys_get_temp_dir added in PHP v5.2.1
// PHP v5.2.1+
// sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
$temp_dir = sys_get_temp_dir(); $temp_dir = sys_get_temp_dir();
} }
$temp_dir = realpath($temp_dir); $temp_dir = @realpath($temp_dir); // see https://github.com/JamesHeinrich/getID3/pull/10
$open_basedir = ini_get('open_basedir'); $open_basedir = ini_get('open_basedir');
if ($open_basedir) { if ($open_basedir) {
// e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/" // e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/"
@ -29,7 +42,7 @@ if ($open_basedir) {
$temp_dir .= DIRECTORY_SEPARATOR; $temp_dir .= DIRECTORY_SEPARATOR;
} }
$found_valid_tempdir = false; $found_valid_tempdir = false;
$open_basedirs = explode(':', $open_basedir); $open_basedirs = explode(PATH_SEPARATOR, $open_basedir);
foreach ($open_basedirs as $basedir) { foreach ($open_basedirs as $basedir) {
if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) { if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
$basedir .= DIRECTORY_SEPARATOR; $basedir .= DIRECTORY_SEPARATOR;
@ -48,29 +61,11 @@ if (!$temp_dir) {
$temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir $temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir
} }
// $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system // $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system
define('GETID3_TEMP_DIR', $temp_dir); if (!defined('GETID3_TEMP_DIR')) {
define('GETID3_TEMP_DIR', $temp_dir);
}
unset($open_basedir, $temp_dir); unset($open_basedir, $temp_dir);
// define a constant rather than looking up every time it is needed
if (!defined('GETID3_OS_ISWINDOWS')) {
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
define('GETID3_OS_ISWINDOWS', true);
} else {
define('GETID3_OS_ISWINDOWS', false);
}
}
// Get base path of getID3() - ONCE
if (!defined('GETID3_INCLUDEPATH')) {
foreach (get_included_files() as $key => $val) {
if (basename($val) == 'getid3.php') {
define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR);
break;
}
}
}
// End: Defines // End: Defines
@ -107,15 +102,15 @@ class getID3
public $filename; // Filename of file being analysed. public $filename; // Filename of file being analysed.
public $fp; // Filepointer to file being analysed. public $fp; // Filepointer to file being analysed.
public $info; // Result array. public $info; // Result array.
public $tempdir = GETID3_TEMP_DIR;
public $memory_limit = 0;
// Protected variables // Protected variables
protected $startup_error = ''; protected $startup_error = '';
protected $startup_warning = ''; protected $startup_warning = '';
protected $memory_limit = 0;
const VERSION = '1.9.3-20111213'; const VERSION = '1.9.9-20141121';
const FREAD_BUFFER_SIZE = 32768; const FREAD_BUFFER_SIZE = 32768;
var $tempdir = GETID3_TEMP_DIR;
const ATTACHMENTS_NONE = false; const ATTACHMENTS_NONE = false;
const ATTACHMENTS_INLINE = true; const ATTACHMENTS_INLINE = true;
@ -124,7 +119,7 @@ class getID3
public function __construct() { public function __construct() {
// Check for PHP version // Check for PHP version
$required_php_version = '5.0.5'; $required_php_version = '5.3.0';
if (version_compare(PHP_VERSION, $required_php_version, '<')) { if (version_compare(PHP_VERSION, $required_php_version, '<')) {
$this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION; $this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION;
return false; return false;
@ -171,7 +166,7 @@ class getID3
} }
// Load support library // Load support library
if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { if (!include_once(GETID3_INCLUDEPATH . 'getid3.lib.php')) {
$this->startup_error .= 'getid3.lib.php is missing or corrupt'; $this->startup_error .= 'getid3.lib.php is missing or corrupt';
} }
@ -234,7 +229,7 @@ class getID3
// public: setOption // public: setOption
function setOption($optArray) { public function setOption($optArray) {
if (!is_array($optArray) || empty($optArray)) { if (!is_array($optArray) || empty($optArray)) {
return false; return false;
} }
@ -261,7 +256,7 @@ class getID3
$this->filename = $filename; $this->filename = $filename;
$this->info = array(); $this->info = array();
$this->info['GETID3_VERSION'] = $this->version(); $this->info['GETID3_VERSION'] = $this->version();
$this->info['php_memory_limit'] = $this->memory_limit; $this->info['php_memory_limit'] = (($this->memory_limit > 0) ? $this->memory_limit : false);
// remote files not supported // remote files not supported
if (preg_match('/^(ht|f)tp:\/\//', $filename)) { if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
@ -272,16 +267,32 @@ class getID3
$filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename); $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
// open local file // open local file
if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { //if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see http://www.getid3.org/phpBB3/viewtopic.php?t=1720
if ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
// great // great
} else { } else {
throw new getid3_exception('Could not open "'.$filename.'" (does not exist, or is not a file)'); $errormessagelist = array();
if (!is_readable($filename)) {
$errormessagelist[] = '!is_readable';
}
if (!is_file($filename)) {
$errormessagelist[] = '!is_file';
}
if (!file_exists($filename)) {
$errormessagelist[] = '!file_exists';
}
if (empty($errormessagelist)) {
$errormessagelist[] = 'fopen failed';
}
throw new getid3_exception('Could not open "'.$filename.'" ('.implode('; ', $errormessagelist).')');
} }
$this->info['filesize'] = filesize($filename); $this->info['filesize'] = filesize($filename);
// set redundant parameters - might be needed in some include file // set redundant parameters - might be needed in some include file
$this->info['filename'] = basename($filename); // filenames / filepaths in getID3 are always expressed with forward slashes (unix-style) for both Windows and other to try and minimize confusion
$filename = str_replace('\\', '/', $filename);
$this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename))); $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
$this->info['filename'] = getid3_lib::mb_basename($filename);
$this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename']; $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
@ -294,20 +305,8 @@ class getID3
if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) || if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) ||
($this->info['filesize'] < 0) || ($this->info['filesize'] < 0) ||
(ftell($this->fp) < 0)) { (ftell($this->fp) < 0)) {
$real_filesize = false; $real_filesize = getid3_lib::getFileSizeSyscall($this->info['filenamepath']);
if (GETID3_OS_ISWINDOWS) {
$commandline = 'dir /-C "'.str_replace('/', DIRECTORY_SEPARATOR, $filename).'"';
$dir_output = `$commandline`;
if (preg_match('#1 File\(s\)[ ]+([0-9]+) bytes#i', $dir_output, $matches)) {
$real_filesize = (float) $matches[1];
}
} else {
$commandline = 'ls -o -g -G --time-style=long-iso '.escapeshellarg($filename);
$dir_output = `$commandline`;
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)) {
$real_filesize = (float) $matches[1];
}
}
if ($real_filesize === false) { if ($real_filesize === false) {
unset($this->info['filesize']); unset($this->info['filesize']);
fclose($this->fp); fclose($this->fp);
@ -318,7 +317,7 @@ class getID3
throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org'); throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org');
} }
$this->info['filesize'] = $real_filesize; $this->info['filesize'] = $real_filesize;
$this->error('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.'); $this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.');
} }
} }
@ -343,7 +342,7 @@ class getID3
} }
// public: analyze file // public: analyze file
function analyze($filename) { public function analyze($filename) {
try { try {
if (!$this->openfile($filename)) { if (!$this->openfile($filename)) {
return $this->info; return $this->info;
@ -375,7 +374,7 @@ class getID3
// ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier
if (!$this->option_tag_id3v2) { if (!$this->option_tag_id3v2) {
fseek($this->fp, 0, SEEK_SET); fseek($this->fp, 0);
$header = fread($this->fp, 10); $header = fread($this->fp, 10);
if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) { if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
$this->info['id3v2']['header'] = true; $this->info['id3v2']['header'] = true;
@ -386,7 +385,7 @@ class getID3
} }
// read 32 kb file data // read 32 kb file data
fseek($this->fp, $this->info['avdataoffset'], SEEK_SET); fseek($this->fp, $this->info['avdataoffset']);
$formattest = fread($this->fp, 32774); $formattest = fread($this->fp, 32774);
// determine format // determine format
@ -447,18 +446,8 @@ class getID3
if (!class_exists($class_name)) { if (!class_exists($class_name)) {
return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.'); return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
} }
//if (isset($determined_format['option'])) { $class = new $class_name($this);
// //$class = new $class_name($this->fp, $this->info, $determined_format['option']);
//} else {
//$class = new $class_name($this->fp, $this->info);
$class = new $class_name($this);
//}
if (!empty($determined_format['set_inline_attachments'])) {
$class->inline_attachments = $this->option_save_attachments;
}
$class->Analyze(); $class->Analyze();
unset($class); unset($class);
// close file // close file
@ -480,7 +469,7 @@ class getID3
// get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
if ($this->option_md5_data) { if ($this->option_md5_data) {
// do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too // do not calc md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) { if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
$this->getHashdata('md5'); $this->getHashdata('md5');
} }
@ -504,7 +493,7 @@ class getID3
// private: error handling // private: error handling
function error($message) { public function error($message) {
$this->CleanUp(); $this->CleanUp();
if (!isset($this->info['error'])) { if (!isset($this->info['error'])) {
$this->info['error'] = array(); $this->info['error'] = array();
@ -515,14 +504,14 @@ class getID3
// private: warning handling // private: warning handling
function warning($message) { public function warning($message) {
$this->info['warning'][] = $message; $this->info['warning'][] = $message;
return true; return true;
} }
// private: CleanUp // private: CleanUp
function CleanUp() { private function CleanUp() {
// remove possible empty keys // remove possible empty keys
$AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate'); $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
@ -570,7 +559,7 @@ class getID3
// return array containing information about all supported formats // return array containing information about all supported formats
function GetFileFormatArray() { public function GetFileFormatArray() {
static $format_info = array(); static $format_info = array();
if (empty($format_info)) { if (empty($format_info)) {
$format_info = array( $format_info = array(
@ -594,15 +583,15 @@ class getID3
'fail_ape' => 'WARNING', 'fail_ape' => 'WARNING',
), ),
/*
// AA - audio - Audible Audiobook // AA - audio - Audible Audiobook
'adts' => array( 'aa' => array(
'pattern' => '^.{4}\x57\x90\x75\x36', 'pattern' => '^.{4}\x57\x90\x75\x36',
'group' => 'audio', 'group' => 'audio',
'module' => 'aa', 'module' => 'aa',
'mime_type' => 'audio/audible ', 'mime_type' => 'audio/audible',
), ),
*/
// AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3) // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
'adts' => array( 'adts' => array(
'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]', 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]',
@ -621,6 +610,14 @@ class getID3
'mime_type' => 'audio/basic', 'mime_type' => 'audio/basic',
), ),
// AMR - audio - Adaptive Multi Rate
'amr' => array(
'pattern' => '^\x23\x21AMR\x0A', // #!AMR[0A]
'group' => 'audio',
'module' => 'amr',
'mime_type' => 'audio/amr',
),
// AVR - audio - Audio Visual Research // AVR - audio - Audio Visual Research
'avr' => array( 'avr' => array(
'pattern' => '^2BIT', 'pattern' => '^2BIT',
@ -639,7 +636,7 @@ class getID3
// DSS - audio - Digital Speech Standard // DSS - audio - Digital Speech Standard
'dss' => array( 'dss' => array(
'pattern' => '^[\x02-\x03]dss', 'pattern' => '^[\x02-\x03]ds[s2]',
'group' => 'audio', 'group' => 'audio',
'module' => 'dss', 'module' => 'dss',
'mime_type' => 'application/octet-stream', 'mime_type' => 'application/octet-stream',
@ -659,7 +656,6 @@ class getID3
'group' => 'audio', 'group' => 'audio',
'module' => 'flac', 'module' => 'flac',
'mime_type' => 'audio/x-flac', 'mime_type' => 'audio/x-flac',
'set_inline_attachments' => true,
), ),
// LA - audio - Lossless Audio (LA) // LA - audio - Lossless Audio (LA)
@ -839,7 +835,6 @@ class getID3
'group' => 'audio-video', 'group' => 'audio-video',
'module' => 'matroska', 'module' => 'matroska',
'mime_type' => 'video/x-matroska', // may also be audio/x-matroska 'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
'set_inline_attachments' => true,
), ),
// MPEG - audio/video - MPEG (Moving Pictures Experts Group) // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
@ -866,7 +861,6 @@ class getID3
'mime_type' => 'application/ogg', 'mime_type' => 'application/ogg',
'fail_id3' => 'WARNING', 'fail_id3' => 'WARNING',
'fail_ape' => 'WARNING', 'fail_ape' => 'WARNING',
'set_inline_attachments' => true,
), ),
// QT - audio/video - Quicktime // QT - audio/video - Quicktime
@ -902,6 +896,14 @@ class getID3
'mime_type' => 'application/x-shockwave-flash', 'mime_type' => 'application/x-shockwave-flash',
), ),
// TS - audio/video - MPEG-2 Transport Stream
'ts' => array(
'pattern' => '^(\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G". Check for at least 10 packets matching this pattern
'group' => 'audio-video',
'module' => 'ts',
'mime_type' => 'video/MP2T',
),
// Still-Image formats // Still-Image formats
@ -980,7 +982,7 @@ class getID3
// EFAX - still image - eFax (TIFF derivative) // EFAX - still image - eFax (TIFF derivative)
'bmp' => array( 'efax' => array(
'pattern' => '^\xDC\xFE', 'pattern' => '^\xDC\xFE',
'group' => 'graphic', 'group' => 'graphic',
'module' => 'efax', 'module' => 'efax',
@ -1102,7 +1104,7 @@ class getID3
function GetFileFormat(&$filedata, $filename='') { public function GetFileFormat(&$filedata, $filename='') {
// this function will determine the format of a file based on usually // this function will determine the format of a file based on usually
// the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG, // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
// and in the case of ISO CD image, 6 bytes offset 32kb from the start // and in the case of ISO CD image, 6 bytes offset 32kb from the start
@ -1141,7 +1143,7 @@ class getID3
// converts array to $encoding charset from $this->encoding // converts array to $encoding charset from $this->encoding
function CharConvert(&$array, $encoding) { public function CharConvert(&$array, $encoding) {
// identical encoding - end here // identical encoding - end here
if ($encoding == $this->encoding) { if ($encoding == $this->encoding) {
@ -1164,7 +1166,7 @@ class getID3
} }
function HandleAllTags() { public function HandleAllTags() {
// key name => array (tag name, character encoding) // key name => array (tag name, character encoding)
static $tags; static $tags;
@ -1187,6 +1189,9 @@ class getID3
'ape' => array('ape' , 'UTF-8'), 'ape' => array('ape' , 'UTF-8'),
'cue' => array('cue' , 'ISO-8859-1'), 'cue' => array('cue' , 'ISO-8859-1'),
'matroska' => array('matroska' , 'UTF-8'), 'matroska' => array('matroska' , 'UTF-8'),
'flac' => array('vorbiscomment' , 'UTF-8'),
'divxtag' => array('divx' , 'ISO-8859-1'),
'iptc' => array('iptc' , 'ISO-8859-1'),
); );
} }
@ -1201,16 +1206,22 @@ class getID3
// copy comments if key name set // copy comments if key name set
if (!empty($this->info[$comment_name]['comments'])) { if (!empty($this->info[$comment_name]['comments'])) {
foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) { foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
foreach ($valuearray as $key => $value) { foreach ($valuearray as $key => $value) {
if (is_string($value)) { if (is_string($value)) {
$value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed! $value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed!
} }
if ($value) { if ($value) {
$this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; if (!is_numeric($key)) {
$this->info['tags'][trim($tag_name)][trim($tag_key)][$key] = $value;
} else {
$this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value;
}
} }
} }
if ($tag_key == 'picture') {
unset($this->info[$comment_name]['comments'][$tag_key]);
}
} }
if (!isset($this->info['tags'][$tag_name])) { if (!isset($this->info['tags'][$tag_name])) {
@ -1220,14 +1231,7 @@ class getID3
if ($this->option_tags_html) { if ($this->option_tags_html) {
foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) { foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
foreach ($valuearray as $key => $value) { $this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $encoding);
if (is_string($value)) {
//$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding);
$this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('&#0;', '', trim(getid3_lib::MultiByteCharString2HTML($value, $encoding)));
} else {
$this->info['tags_html'][$tag_name][$tag_key][$key] = $value;
}
}
} }
} }
@ -1283,8 +1287,7 @@ class getID3
return true; return true;
} }
public function getHashdata($algorithm) {
function getHashdata($algorithm) {
switch ($algorithm) { switch ($algorithm) {
case 'md5': case 'md5':
case 'sha1': case 'sha1':
@ -1408,7 +1411,7 @@ class getID3
} }
function ChannelsBitratePlaytimeCalculations() { public function ChannelsBitratePlaytimeCalculations() {
// set channelmode on audio // set channelmode on audio
if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) { if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) {
@ -1473,7 +1476,7 @@ class getID3
} }
function CalculateCompressionRatioVideo() { public function CalculateCompressionRatioVideo() {
if (empty($this->info['video'])) { if (empty($this->info['video'])) {
return false; return false;
} }
@ -1521,8 +1524,8 @@ class getID3
} }
function CalculateCompressionRatioAudio() { public function CalculateCompressionRatioAudio() {
if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) { if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) {
return false; return false;
} }
$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)); $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));
@ -1538,7 +1541,7 @@ class getID3
} }
function CalculateReplayGain() { public function CalculateReplayGain() {
if (isset($this->info['replay_gain'])) { if (isset($this->info['replay_gain'])) {
if (!isset($this->info['replay_gain']['reference_volume'])) { if (!isset($this->info['replay_gain']['reference_volume'])) {
$this->info['replay_gain']['reference_volume'] = (double) 89.0; $this->info['replay_gain']['reference_volume'] = (double) 89.0;
@ -1560,7 +1563,7 @@ class getID3
return true; return true;
} }
function ProcessAudioStreams() { public function ProcessAudioStreams() {
if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) { if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
if (!isset($this->info['audio']['streams'])) { if (!isset($this->info['audio']['streams'])) {
foreach ($this->info['audio'] as $key => $value) { foreach ($this->info['audio'] as $key => $value) {
@ -1573,72 +1576,10 @@ class getID3
return true; return true;
} }
function getid3_tempnam() { public function getid3_tempnam() {
return tempnam($this->tempdir, 'gI3'); return tempnam($this->tempdir, 'gI3');
} }
public function saveAttachment(&$ThisFileInfoIndex, $filename, $offset, $length) {
try {
if (!getid3_lib::intValueSupported($offset + $length)) {
throw new Exception('cannot extract attachment, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit');
}
// do not extract at all
if ($this->option_save_attachments === getID3::ATTACHMENTS_NONE) {
unset($ThisFileInfoIndex); // do not set any
// extract to return array
} elseif ($this->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
// get whole data in one pass, till it is anyway stored in memory
$ThisFileInfoIndex = file_get_contents($this->info['filenamepath'], false, null, $offset, $length);
if (($ThisFileInfoIndex === false) || (strlen($ThisFileInfoIndex) != $length)) { // verify
throw new Exception('failed to read attachment data');
}
// assume directory path is given
} else {
$dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->option_save_attachments), DIRECTORY_SEPARATOR);
// check supplied directory
if (!is_dir($dir) || !is_writable($dir)) {
throw new Exception('getID3::saveAttachment() -- supplied path ('.$dir.') does not exist, or is not writable');
}
// set up destination path
$dest = $dir.DIRECTORY_SEPARATOR.$filename;
// optimize speed if read buffer size is configured to be large enough
// here stream_copy_to_stream() may also be used. need to do speed-compare tests
if ($length <= $this->fread_buffer_size()) {
$data = file_get_contents($this->info['filenamepath'], false, null, $offset, $length);
if (($data === false) || (strlen($data) != $length)) { // verify
throw new Exception('failed to read attachment data');
}
if (!file_put_contents($dest, $data)) {
throw new Exception('failed to create file '.$dest);
}
} else {
// optimization not available - copy data in loop
// here stream_copy_to_stream() shouldn't be used because it's internal read buffer may be larger than ours!
getid3_lib::CopyFileParts($this->info['filenamepath'], $dest, $offset, $length);
}
$ThisFileInfoIndex = $dest;
}
} catch (Exception $e) {
unset($ThisFileInfoIndex); // do not set any is case of error
$this->warning('Failed to extract attachment '.$filename.': '.$e->getMessage());
return false;
}
return true;
}
public function include_module($name) { public function include_module($name) {
//if (!file_exists($this->include_path.'module.'.$name.'.php')) { //if (!file_exists($this->include_path.'module.'.$name.'.php')) {
if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) { if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) {
@ -1651,94 +1592,212 @@ class getID3
} }
abstract class getid3_handler abstract class getid3_handler {
{
protected $getid3; // pointer
protected $data_string_flag = false; // analyzing filepointer or string /**
protected $data_string; // string to analyze * @var getID3
protected $data_string_position = 0; // seek position in string */
protected $getid3; // pointer
protected $data_string_flag = false; // analyzing filepointer or string
protected $data_string = ''; // string to analyze
protected $data_string_position = 0; // seek position in string
protected $data_string_length = 0; // string length
private $dependency_to = null;
public function __construct(getID3 $getid3) { public function __construct(getID3 $getid3, $call_module=null) {
$this->getid3 = $getid3; $this->getid3 = $getid3;
}
if ($call_module) {
$this->dependency_to = str_replace('getid3_', '', $call_module);
}
}
// Analyze from file pointer // Analyze from file pointer
abstract public function Analyze(); abstract public function Analyze();
// Analyze from string instead // Analyze from string instead
public function AnalyzeString(&$string) { public function AnalyzeString($string) {
// Enter string mode // Enter string mode
$this->data_string_flag = true; $this->setStringMode($string);
$this->data_string = $string;
// Save info // Save info
$saved_avdataoffset = $this->getid3->info['avdataoffset']; $saved_avdataoffset = $this->getid3->info['avdataoffset'];
$saved_avdataend = $this->getid3->info['avdataend']; $saved_avdataend = $this->getid3->info['avdataend'];
$saved_filesize = $this->getid3->info['filesize']; $saved_filesize = (isset($this->getid3->info['filesize']) ? $this->getid3->info['filesize'] : null); // may be not set if called as dependency without openfile() call
// Reset some info // Reset some info
$this->getid3->info['avdataoffset'] = 0; $this->getid3->info['avdataoffset'] = 0;
$this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = strlen($string); $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = $this->data_string_length;
// Analyze // Analyze
$this->Analyze(); $this->Analyze();
// Restore some info // Restore some info
$this->getid3->info['avdataoffset'] = $saved_avdataoffset; $this->getid3->info['avdataoffset'] = $saved_avdataoffset;
$this->getid3->info['avdataend'] = $saved_avdataend; $this->getid3->info['avdataend'] = $saved_avdataend;
$this->getid3->info['filesize'] = $saved_filesize; $this->getid3->info['filesize'] = $saved_filesize;
// Exit string mode // Exit string mode
$this->data_string_flag = false; $this->data_string_flag = false;
} }
public function setStringMode($string) {
$this->data_string_flag = true;
$this->data_string = $string;
$this->data_string_length = strlen($string);
}
protected function ftell() { protected function ftell() {
if ($this->data_string_flag) { if ($this->data_string_flag) {
return $this->data_string_position; return $this->data_string_position;
} }
return ftell($this->getid3->fp); return ftell($this->getid3->fp);
} }
protected function fread($bytes) {
if ($this->data_string_flag) {
$this->data_string_position += $bytes;
return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
}
$pos = $this->ftell() + $bytes;
if (!getid3_lib::intValueSupported($pos)) {
throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10);
}
return fread($this->getid3->fp, $bytes);
}
protected function fread($bytes) { protected function fseek($bytes, $whence=SEEK_SET) {
if ($this->data_string_flag) { if ($this->data_string_flag) {
$this->data_string_position += $bytes; switch ($whence) {
return substr($this->data_string, $this->data_string_position - $bytes, $bytes); case SEEK_SET:
} $this->data_string_position = $bytes;
return fread($this->getid3->fp, $bytes); break;
}
case SEEK_CUR:
$this->data_string_position += $bytes;
break;
protected function fseek($bytes, $whence = SEEK_SET) { case SEEK_END:
if ($this->data_string_flag) { $this->data_string_position = $this->data_string_length + $bytes;
switch ($whence) { break;
case SEEK_SET: }
$this->data_string_position = $bytes; return 0;
return; } else {
$pos = $bytes;
if ($whence == SEEK_CUR) {
$pos = $this->ftell() + $bytes;
} elseif ($whence == SEEK_END) {
$pos = $this->getid3->info['filesize'] + $bytes;
}
if (!getid3_lib::intValueSupported($pos)) {
throw new getid3_exception('cannot fseek('.$pos.') because beyond PHP filesystem limit', 10);
}
}
return fseek($this->getid3->fp, $bytes, $whence);
}
case SEEK_CUR: protected function feof() {
$this->data_string_position += $bytes; if ($this->data_string_flag) {
return; return $this->data_string_position >= $this->data_string_length;
}
return feof($this->getid3->fp);
}
case SEEK_END: final protected function isDependencyFor($module) {
$this->data_string_position = strlen($this->data_string) + $bytes; return $this->dependency_to == $module;
return; }
}
} protected function error($text) {
return fseek($this->getid3->fp, $bytes, $whence); $this->getid3->info['error'][] = $text;
}
return false;
}
protected function warning($text) {
return $this->getid3->warning($text);
}
protected function notice($text) {
// does nothing for now
}
public function saveAttachment($name, $offset, $length, $image_mime=null) {
try {
// do not extract at all
if ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_NONE) {
$attachment = null; // do not set any
// extract to return array
} elseif ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
$this->fseek($offset);
$attachment = $this->fread($length); // get whole data in one pass, till it is anyway stored in memory
if ($attachment === false || strlen($attachment) != $length) {
throw new Exception('failed to read attachment data');
}
// assume directory path is given
} else {
// set up destination path
$dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
if (!is_dir($dir) || !is_writable($dir)) { // check supplied directory
throw new Exception('supplied path ('.$dir.') does not exist, or is not writable');
}
$dest = $dir.DIRECTORY_SEPARATOR.$name.($image_mime ? '.'.getid3_lib::ImageExtFromMime($image_mime) : '');
// create dest file
if (($fp_dest = fopen($dest, 'wb')) == false) {
throw new Exception('failed to create file '.$dest);
}
// copy data
$this->fseek($offset);
$buffersize = ($this->data_string_flag ? $length : $this->getid3->fread_buffer_size());
$bytesleft = $length;
while ($bytesleft > 0) {
if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false || ($byteswritten === 0)) {
throw new Exception($buffer === false ? 'not enough data to read' : 'failed to write to destination file, may be not enough disk space');
}
$bytesleft -= $byteswritten;
}
fclose($fp_dest);
$attachment = $dest;
}
} catch (Exception $e) {
// close and remove dest file if created
if (isset($fp_dest) && is_resource($fp_dest)) {
fclose($fp_dest);
unlink($dest);
}
// do not set any is case of error
$attachment = null;
$this->warning('Failed to extract attachment '.$name.': '.$e->getMessage());
}
// seek to the end of attachment
$this->fseek($offset + $length);
return $attachment;
}
} }
class getid3_exception extends Exception class getid3_exception extends Exception
{ {
public $message; public $message;
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -14,7 +15,7 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// Module originally written by // // Module originally written by //
// Mike Mozolin <teddybearØmail*ru> // // Mike Mozolin <teddybearØmail*ru> //
// // // //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -22,9 +23,9 @@
class getid3_gzip extends getid3_handler { class getid3_gzip extends getid3_handler {
// public: Optional file list - disable for speed. // public: Optional file list - disable for speed.
var $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example) public $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example)
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'gzip'; $info['fileformat'] = 'gzip';
@ -35,12 +36,12 @@ class getid3_gzip extends getid3_handler {
//|ID1|ID2|CM |FLG| MTIME |XFL|OS | //|ID1|ID2|CM |FLG| MTIME |XFL|OS |
//+---+---+---+---+---+---+---+---+---+---+ //+---+---+---+---+---+---+---+---+---+---+
if ($info['filesize'] > $info['php_memory_limit']) { if ($info['php_memory_limit'] && ($info['filesize'] > $info['php_memory_limit'])) {
$info['error'][] = 'File is too large ('.number_format($info['filesize']).' bytes) to read into memory (limit: '.number_format($info['php_memory_limit'] / 1048576).'MB)'; $info['error'][] = 'File is too large ('.number_format($info['filesize']).' bytes) to read into memory (limit: '.number_format($info['php_memory_limit'] / 1048576).'MB)';
return false; return false;
} }
fseek($this->getid3->fp, 0); $this->fseek(0);
$buffer = fread($this->getid3->fp, $info['filesize']); $buffer = $this->fread($info['filesize']);
$arr_members = explode("\x1F\x8B\x08", $buffer); $arr_members = explode("\x1F\x8B\x08", $buffer);
while (true) { while (true) {
@ -55,7 +56,7 @@ class getid3_gzip extends getid3_handler {
$attr = unpack($unpack_header, substr($buf, 0, $start_length)); $attr = unpack($unpack_header, substr($buf, 0, $start_length));
if (!$this->get_os_type(ord($attr['os']))) { if (!$this->get_os_type(ord($attr['os']))) {
// Merge member with previous if wrong OS type // Merge member with previous if wrong OS type
$arr_members[$i - 1] .= $buf; $arr_members[($i - 1)] .= $buf;
$arr_members[$i] = ''; $arr_members[$i] = '';
$is_wrong_members = true; $is_wrong_members = true;
continue; continue;
@ -140,8 +141,9 @@ class getid3_gzip extends getid3_handler {
//|...original file name, zero-terminated...| //|...original file name, zero-terminated...|
//+=========================================+ //+=========================================+
// GZIP files may have only one file, with no filename, so assume original filename is current filename without .gz // GZIP files may have only one file, with no filename, so assume original filename is current filename without .gz
$thisInfo['filename'] = preg_replace('#\.gz$#i', '', $info['filename']); $thisInfo['filename'] = preg_replace('#\\.gz$#i', '', $info['filename']);
if ($thisInfo['flags']['filename']) { if ($thisInfo['flags']['filename']) {
$thisInfo['filename'] = '';
while (true) { while (true) {
if (ord($buff[$fpointer]) == 0) { if (ord($buff[$fpointer]) == 0) {
$fpointer++; $fpointer++;
@ -245,7 +247,7 @@ class getid3_gzip extends getid3_handler {
} }
// Converts the OS type // Converts the OS type
function get_os_type($key) { public function get_os_type($key) {
static $os_type = array( static $os_type = array(
'0' => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)', '0' => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)',
'1' => 'Amiga', '1' => 'Amiga',
@ -267,7 +269,7 @@ class getid3_gzip extends getid3_handler {
} }
// Converts the eXtra FLags // Converts the eXtra FLags
function get_xflag_type($key) { public function get_xflag_type($key) {
static $xflag_type = array( static $xflag_type = array(
'0' => 'unknown', '0' => 'unknown',
'2' => 'maximum compression', '2' => 'maximum compression',
@ -277,4 +279,3 @@ class getid3_gzip extends getid3_handler {
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,9 +18,9 @@
class getid3_rar extends getid3_handler class getid3_rar extends getid3_handler
{ {
var $option_use_rar_extension = false; public $option_use_rar_extension = false;
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'rar'; $info['fileformat'] = 'rar';
@ -48,6 +49,3 @@ class getid3_rar extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,11 +18,11 @@
class getid3_szip extends getid3_handler class getid3_szip extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$SZIPHeader = fread($this->getid3->fp, 6); $SZIPHeader = $this->fread(6);
if (substr($SZIPHeader, 0, 4) != "SZ\x0A\x04") { if (substr($SZIPHeader, 0, 4) != "SZ\x0A\x04") {
$info['error'][] = 'Expecting "53 5A 0A 04" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($SZIPHeader, 0, 4)).'"'; $info['error'][] = 'Expecting "53 5A 0A 04" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($SZIPHeader, 0, 4)).'"';
return false; return false;
@ -29,20 +30,22 @@ class getid3_szip extends getid3_handler
$info['fileformat'] = 'szip'; $info['fileformat'] = 'szip';
$info['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1)); $info['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1));
$info['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1)); $info['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1));
$info['error'][] = 'SZIP parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
return false;
while (!feof($this->getid3->fp)) { while (!$this->feof()) {
$NextBlockID = fread($this->getid3->fp, 2); $NextBlockID = $this->fread(2);
switch ($NextBlockID) { switch ($NextBlockID) {
case 'SZ': case 'SZ':
// Note that szip files can be concatenated, this has the same effect as // Note that szip files can be concatenated, this has the same effect as
// concatenating the files. this also means that global header blocks // concatenating the files. this also means that global header blocks
// might be present between directory/data blocks. // might be present between directory/data blocks.
fseek($this->getid3->fp, 4, SEEK_CUR); $this->fseek(4, SEEK_CUR);
break; break;
case 'BH': case 'BH':
$BHheaderbytes = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 3)); $BHheaderbytes = getid3_lib::BigEndian2Int($this->fread(3));
$BHheaderdata = fread($this->getid3->fp, $BHheaderbytes); $BHheaderdata = $this->fread($BHheaderbytes);
$BHheaderoffset = 0; $BHheaderoffset = 0;
while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) { while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) {
//filename as \0 terminated string (empty string indicates end) //filename as \0 terminated string (empty string indicates end)
@ -92,5 +95,3 @@ class getid3_szip extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -14,7 +15,7 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// Module originally written by // // Module originally written by //
// Mike Mozolin <teddybearØmail*ru> // // Mike Mozolin <teddybearØmail*ru> //
// // // //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -22,7 +23,7 @@
class getid3_tar extends getid3_handler class getid3_tar extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'tar'; $info['fileformat'] = 'tar';
@ -31,9 +32,9 @@ class getid3_tar extends getid3_handler
$unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155prefix'; $unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155prefix';
$null_512k = str_repeat("\x00", 512); // end-of-file marker $null_512k = str_repeat("\x00", 512); // end-of-file marker
fseek($this->getid3->fp, 0); $this->fseek(0);
while (!feof($this->getid3->fp)) { while (!feof($this->getid3->fp)) {
$buffer = fread($this->getid3->fp, 512); $buffer = $this->fread(512);
if (strlen($buffer) < 512) { if (strlen($buffer) < 512) {
break; break;
} }
@ -82,12 +83,12 @@ class getid3_tar extends getid3_handler
} }
// Read to the next chunk // Read to the next chunk
fseek($this->getid3->fp, $size, SEEK_CUR); $this->fseek($size, SEEK_CUR);
$diff = $size % 512; $diff = $size % 512;
if ($diff != 0) { if ($diff != 0) {
// Padding, throw away // Padding, throw away
fseek($this->getid3->fp, (512 - $diff), SEEK_CUR); $this->fseek((512 - $diff), SEEK_CUR);
} }
// Protect against tar-files with garbage at the end // Protect against tar-files with garbage at the end
if ($name == '') { if ($name == '') {
@ -96,13 +97,13 @@ class getid3_tar extends getid3_handler
$info['tar']['file_details'][$name] = array ( $info['tar']['file_details'][$name] = array (
'name' => $name, 'name' => $name,
'mode_raw' => $mode, 'mode_raw' => $mode,
'mode' => getid3_tar::display_perms($mode), 'mode' => self::display_perms($mode),
'uid' => $uid, 'uid' => $uid,
'gid' => $gid, 'gid' => $gid,
'size' => $size, 'size' => $size,
'mtime' => $mtime, 'mtime' => $mtime,
'chksum' => $chksum, 'chksum' => $chksum,
'typeflag' => getid3_tar::get_flag_type($typflag), 'typeflag' => self::get_flag_type($typflag),
'linkname' => $lnkname, 'linkname' => $lnkname,
'magic' => $magic, 'magic' => $magic,
'version' => $ver, 'version' => $ver,
@ -117,7 +118,7 @@ class getid3_tar extends getid3_handler
} }
// Parses the file mode to file permissions // Parses the file mode to file permissions
function display_perms($mode) { public function display_perms($mode) {
// Determine Type // Determine Type
if ($mode & 0x1000) $type='p'; // FIFO pipe if ($mode & 0x1000) $type='p'; // FIFO pipe
elseif ($mode & 0x2000) $type='c'; // Character special elseif ($mode & 0x2000) $type='c'; // Character special
@ -152,7 +153,7 @@ class getid3_tar extends getid3_handler
} }
// Converts the file type // Converts the file type
function get_flag_type($typflag) { public function get_flag_type($typflag) {
static $flag_types = array( static $flag_types = array(
'0' => 'LF_NORMAL', '0' => 'LF_NORMAL',
'1' => 'LF_LINK', '1' => 'LF_LINK',
@ -174,5 +175,3 @@ class getid3_tar extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,7 +18,7 @@
class getid3_zip extends getid3_handler class getid3_zip extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'zip'; $info['fileformat'] = 'zip';
@ -36,16 +37,16 @@ class getid3_zip extends getid3_handler
$EOCDsearchCounter = 0; $EOCDsearchCounter = 0;
while ($EOCDsearchCounter++ < 512) { while ($EOCDsearchCounter++ < 512) {
fseek($this->getid3->fp, -128 * $EOCDsearchCounter, SEEK_END); $this->fseek(-128 * $EOCDsearchCounter, SEEK_END);
$EOCDsearchData = fread($this->getid3->fp, 128).$EOCDsearchData; $EOCDsearchData = $this->fread(128).$EOCDsearchData;
if (strstr($EOCDsearchData, 'PK'."\x05\x06")) { if (strstr($EOCDsearchData, 'PK'."\x05\x06")) {
$EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06"); $EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06");
fseek($this->getid3->fp, (-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END); $this->fseek((-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END);
$info['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory(); $info['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory();
fseek($this->getid3->fp, $info['zip']['end_central_directory']['directory_offset'], SEEK_SET); $this->fseek($info['zip']['end_central_directory']['directory_offset']);
$info['zip']['entries_count'] = 0; $info['zip']['entries_count'] = 0;
while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) { while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) {
$info['zip']['central_directory'][] = $centraldirectoryentry; $info['zip']['central_directory'][] = $centraldirectoryentry;
@ -53,7 +54,8 @@ class getid3_zip extends getid3_handler
$info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; $info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size'];
$info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; $info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
if ($centraldirectoryentry['uncompressed_size'] > 0) { //if ($centraldirectoryentry['uncompressed_size'] > 0) { zero-byte files are valid
if (!empty($centraldirectoryentry['filename'])) {
$info['zip']['files'] = getid3_lib::array_merge_clobber($info['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size'])); $info['zip']['files'] = getid3_lib::array_merge_clobber($info['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size']));
} }
} }
@ -77,37 +79,58 @@ class getid3_zip extends getid3_handler
$info['zip']['compression_speed'] = 'store'; $info['zip']['compression_speed'] = 'store';
} }
return true; // secondary check - we (should) already have all the info we NEED from the Central Directory above, but scanning each
// Local File Header entry will
foreach ($info['zip']['central_directory'] as $central_directory_entry) {
$this->fseek($central_directory_entry['entry_offset']);
if ($fileentry = $this->ZIPparseLocalFileHeader()) {
$info['zip']['entries'][] = $fileentry;
} else {
$info['warning'][] = 'Error parsing Local File Header at offset '.$central_directory_entry['entry_offset'];
}
}
if (!empty($info['zip']['files']['[Content_Types].xml']) &&
!empty($info['zip']['files']['_rels']['.rels']) &&
!empty($info['zip']['files']['docProps']['app.xml']) &&
!empty($info['zip']['files']['docProps']['core.xml'])) {
// http://technet.microsoft.com/en-us/library/cc179224.aspx
$info['fileformat'] = 'zip.msoffice';
if (!empty($ThisFileInfo['zip']['files']['ppt'])) {
$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
} elseif (!empty($ThisFileInfo['zip']['files']['xl'])) {
$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
} elseif (!empty($ThisFileInfo['zip']['files']['word'])) {
$info['mime_type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
}
}
return true;
} }
} }
} }
if ($this->getZIPentriesFilepointer()) { if (!$this->getZIPentriesFilepointer()) {
// central directory couldn't be found and/or parsed
// scan through actual file data entries, recover as much as possible from probable trucated file
if ($info['zip']['compressed_size'] > ($info['filesize'] - 46 - 22)) {
$info['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$info['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($info['filesize'] - 46 - 22).' bytes)';
}
$info['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete';
foreach ($info['zip']['entries'] as $key => $valuearray) {
$info['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
}
return true;
} else {
unset($info['zip']); unset($info['zip']);
$info['fileformat'] = ''; $info['fileformat'] = '';
$info['error'][] = 'Cannot find End Of Central Directory (truncated file?)'; $info['error'][] = 'Cannot find End Of Central Directory (truncated file?)';
return false; return false;
} }
// central directory couldn't be found and/or parsed
// scan through actual file data entries, recover as much as possible from probable trucated file
if ($info['zip']['compressed_size'] > ($info['filesize'] - 46 - 22)) {
$info['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$info['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($info['filesize'] - 46 - 22).' bytes)';
}
$info['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete';
foreach ($info['zip']['entries'] as $key => $valuearray) {
$info['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
}
return true;
} }
function getZIPHeaderFilepointerTopDown() { public function getZIPHeaderFilepointerTopDown() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'zip'; $info['fileformat'] = 'zip';
@ -153,7 +176,7 @@ class getid3_zip extends getid3_handler
} }
function getZIPentriesFilepointer() { public function getZIPentriesFilepointer() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['zip']['compressed_size'] = 0; $info['zip']['compressed_size'] = 0;
@ -176,15 +199,15 @@ class getid3_zip extends getid3_handler
} }
function ZIPparseLocalFileHeader() { public function ZIPparseLocalFileHeader() {
$LocalFileHeader['offset'] = ftell($this->getid3->fp); $LocalFileHeader['offset'] = $this->ftell();
$ZIPlocalFileHeader = fread($this->getid3->fp, 30); $ZIPlocalFileHeader = $this->fread(30);
$LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4)); $LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4));
if ($LocalFileHeader['raw']['signature'] != 0x04034B50) { if ($LocalFileHeader['raw']['signature'] != 0x04034B50) { // "PK\x03\x04"
// invalid Local File Header Signature // invalid Local File Header Signature
fseek($this->getid3->fp, $LocalFileHeader['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly $this->fseek($LocalFileHeader['offset']); // seek back to where filepointer originally was so it can be handled properly
return false; return false;
} }
$LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2)); $LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2));
@ -208,7 +231,7 @@ class getid3_zip extends getid3_handler
$FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length']; $FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length'];
if ($FilenameExtrafieldLength > 0) { if ($FilenameExtrafieldLength > 0) {
$ZIPlocalFileHeader .= fread($this->getid3->fp, $FilenameExtrafieldLength); $ZIPlocalFileHeader .= $this->fread($FilenameExtrafieldLength);
if ($LocalFileHeader['raw']['filename_length'] > 0) { if ($LocalFileHeader['raw']['filename_length'] > 0) {
$LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']); $LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']);
@ -218,30 +241,69 @@ class getid3_zip extends getid3_handler
} }
} }
$LocalFileHeader['data_offset'] = ftell($this->getid3->fp); if ($LocalFileHeader['compressed_size'] == 0) {
//$LocalFileHeader['compressed_data'] = fread($this->getid3->fp, $LocalFileHeader['raw']['compressed_size']); // *Could* be a zero-byte file
fseek($this->getid3->fp, $LocalFileHeader['raw']['compressed_size'], SEEK_CUR); // But could also be a file written on the fly that didn't know compressed filesize beforehand.
// Correct compressed filesize should be in the data_descriptor located after this file data, and also in Central Directory (at end of zip file)
if (!empty($this->getid3->info['zip']['central_directory'])) {
foreach ($this->getid3->info['zip']['central_directory'] as $central_directory_entry) {
if ($central_directory_entry['entry_offset'] == $LocalFileHeader['offset']) {
if ($central_directory_entry['compressed_size'] > 0) {
// overwrite local zero value (but not ['raw']'compressed_size']) so that seeking for data_descriptor (and next file entry) works correctly
$LocalFileHeader['compressed_size'] = $central_directory_entry['compressed_size'];
}
break;
}
}
}
}
$LocalFileHeader['data_offset'] = $this->ftell();
$this->fseek($LocalFileHeader['compressed_size'], SEEK_CUR); // this should (but may not) match value in $LocalFileHeader['raw']['compressed_size'] -- $LocalFileHeader['compressed_size'] could have been overwritten above with value from Central Directory
if ($LocalFileHeader['flags']['data_descriptor_used']) { if ($LocalFileHeader['flags']['data_descriptor_used']) {
$DataDescriptor = fread($this->getid3->fp, 12); $DataDescriptor = $this->fread(16);
$LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4)); $LocalFileHeader['data_descriptor']['signature'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4));
$LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4)); if ($LocalFileHeader['data_descriptor']['signature'] != 0x08074B50) { // "PK\x07\x08"
$LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4)); $this->getid3->warning[] = 'invalid Local File Header Data Descriptor Signature at offset '.($this->ftell() - 16).' - expecting 08 07 4B 50, found '.getid3_lib::PrintHexBytes($LocalFileHeader['data_descriptor']['signature']);
} $this->fseek($LocalFileHeader['offset']); // seek back to where filepointer originally was so it can be handled properly
return false;
}
$LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4));
$LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4));
$LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 12, 4));
if (!$LocalFileHeader['raw']['compressed_size'] && $LocalFileHeader['data_descriptor']['compressed_size']) {
foreach ($this->getid3->info['zip']['central_directory'] as $central_directory_entry) {
if ($central_directory_entry['entry_offset'] == $LocalFileHeader['offset']) {
if ($LocalFileHeader['data_descriptor']['compressed_size'] == $central_directory_entry['compressed_size']) {
// $LocalFileHeader['compressed_size'] already set from Central Directory
} else {
$this->getid3->info['warning'][] = 'conflicting compressed_size from data_descriptor ('.$LocalFileHeader['data_descriptor']['compressed_size'].') vs Central Directory ('.$central_directory_entry['compressed_size'].') for file at offset '.$LocalFileHeader['offset'];
}
if ($LocalFileHeader['data_descriptor']['uncompressed_size'] == $central_directory_entry['uncompressed_size']) {
$LocalFileHeader['uncompressed_size'] = $LocalFileHeader['data_descriptor']['uncompressed_size'];
} else {
$this->getid3->info['warning'][] = 'conflicting uncompressed_size from data_descriptor ('.$LocalFileHeader['data_descriptor']['uncompressed_size'].') vs Central Directory ('.$central_directory_entry['uncompressed_size'].') for file at offset '.$LocalFileHeader['offset'];
}
break;
}
}
}
}
return $LocalFileHeader; return $LocalFileHeader;
} }
function ZIPparseCentralDirectory() { public function ZIPparseCentralDirectory() {
$CentralDirectory['offset'] = ftell($this->getid3->fp); $CentralDirectory['offset'] = $this->ftell();
$ZIPcentralDirectory = fread($this->getid3->fp, 46); $ZIPcentralDirectory = $this->fread(46);
$CentralDirectory['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 0, 4)); $CentralDirectory['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 0, 4));
if ($CentralDirectory['raw']['signature'] != 0x02014B50) { if ($CentralDirectory['raw']['signature'] != 0x02014B50) {
// invalid Central Directory Signature // invalid Central Directory Signature
fseek($this->getid3->fp, $CentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly $this->fseek($CentralDirectory['offset']); // seek back to where filepointer originally was so it can be handled properly
return false; return false;
} }
$CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2)); $CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2));
@ -273,7 +335,7 @@ class getid3_zip extends getid3_handler
$FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length']; $FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length'];
if ($FilenameExtrafieldCommentLength > 0) { if ($FilenameExtrafieldCommentLength > 0) {
$FilenameExtrafieldComment = fread($this->getid3->fp, $FilenameExtrafieldCommentLength); $FilenameExtrafieldComment = $this->fread($FilenameExtrafieldCommentLength);
if ($CentralDirectory['raw']['filename_length'] > 0) { if ($CentralDirectory['raw']['filename_length'] > 0) {
$CentralDirectory['filename'] = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']); $CentralDirectory['filename'] = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']);
@ -289,15 +351,15 @@ class getid3_zip extends getid3_handler
return $CentralDirectory; return $CentralDirectory;
} }
function ZIPparseEndOfCentralDirectory() { public function ZIPparseEndOfCentralDirectory() {
$EndOfCentralDirectory['offset'] = ftell($this->getid3->fp); $EndOfCentralDirectory['offset'] = $this->ftell();
$ZIPendOfCentralDirectory = fread($this->getid3->fp, 22); $ZIPendOfCentralDirectory = $this->fread(22);
$EndOfCentralDirectory['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4)); $EndOfCentralDirectory['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4));
if ($EndOfCentralDirectory['signature'] != 0x06054B50) { if ($EndOfCentralDirectory['signature'] != 0x06054B50) {
// invalid End Of Central Directory Signature // invalid End Of Central Directory Signature
fseek($this->getid3->fp, $EndOfCentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly $this->fseek($EndOfCentralDirectory['offset']); // seek back to where filepointer originally was so it can be handled properly
return false; return false;
} }
$EndOfCentralDirectory['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2)); $EndOfCentralDirectory['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2));
@ -309,15 +371,31 @@ class getid3_zip extends getid3_handler
$EndOfCentralDirectory['comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2)); $EndOfCentralDirectory['comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2));
if ($EndOfCentralDirectory['comment_length'] > 0) { if ($EndOfCentralDirectory['comment_length'] > 0) {
$EndOfCentralDirectory['comment'] = fread($this->getid3->fp, $EndOfCentralDirectory['comment_length']); $EndOfCentralDirectory['comment'] = $this->fread($EndOfCentralDirectory['comment_length']);
} }
return $EndOfCentralDirectory; return $EndOfCentralDirectory;
} }
static function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) { public static function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) {
$ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001); // https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip-printable.html
$ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001);
// 0x0002 -- see below
// 0x0004 -- see below
$ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008);
$ParsedFlags['enhanced_deflation'] = (bool) ($flagbytes & 0x0010);
$ParsedFlags['compressed_patched_data'] = (bool) ($flagbytes & 0x0020);
$ParsedFlags['strong_encryption'] = (bool) ($flagbytes & 0x0040);
// 0x0080 - unused
// 0x0100 - unused
// 0x0200 - unused
// 0x0400 - unused
$ParsedFlags['language_encoding'] = (bool) ($flagbytes & 0x0800);
// 0x1000 - reserved
$ParsedFlags['mask_header_values'] = (bool) ($flagbytes & 0x2000);
// 0x4000 - reserved
// 0x8000 - reserved
switch ($compressionmethod) { switch ($compressionmethod) {
case 6: case 6:
@ -343,13 +421,12 @@ class getid3_zip extends getid3_handler
} }
break; break;
} }
$ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008);
return $ParsedFlags; return $ParsedFlags;
} }
static function ZIPversionOSLookup($index) { public static function ZIPversionOSLookup($index) {
static $ZIPversionOSLookup = array( static $ZIPversionOSLookup = array(
0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)', 0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)',
1 => 'Amiga', 1 => 'Amiga',
@ -368,13 +445,16 @@ class getid3_zip extends getid3_handler
14 => 'VFAT', 14 => 'VFAT',
15 => 'Alternate MVS', 15 => 'Alternate MVS',
16 => 'BeOS', 16 => 'BeOS',
17 => 'Tandem' 17 => 'Tandem',
18 => 'OS/400',
19 => 'OS/X (Darwin)',
); );
return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]'); return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]');
} }
static function ZIPcompressionMethodLookup($index) { public static function ZIPcompressionMethodLookup($index) {
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/ZIP.html
static $ZIPcompressionMethodLookup = array( static $ZIPcompressionMethodLookup = array(
0 => 'store', 0 => 'store',
1 => 'shrink', 1 => 'shrink',
@ -386,13 +466,25 @@ class getid3_zip extends getid3_handler
7 => 'tokenize', 7 => 'tokenize',
8 => 'deflate', 8 => 'deflate',
9 => 'deflate64', 9 => 'deflate64',
10 => 'PKWARE Date Compression Library Imploding' 10 => 'Imploded (old IBM TERSE)',
11 => 'RESERVED[11]',
12 => 'BZIP2',
13 => 'RESERVED[13]',
14 => 'LZMA (EFS)',
15 => 'RESERVED[15]',
16 => 'RESERVED[16]',
17 => 'RESERVED[17]',
18 => 'IBM TERSE (new)',
19 => 'IBM LZ77 z Architecture (PFS)',
96 => 'JPEG recompressed',
97 => 'WavPack compressed',
98 => 'PPMd version I, Rev 1',
); );
return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]'); return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]');
} }
static function DOStime2UNIXtime($DOSdate, $DOStime) { public static function DOStime2UNIXtime($DOSdate, $DOStime) {
// wFatDate // wFatDate
// Specifies the MS-DOS date. The date is a packed 16-bit value with the following format: // Specifies the MS-DOS date. The date is a packed 16-bit value with the following format:
// Bits Contents // Bits Contents
@ -419,6 +511,3 @@ class getid3_zip extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -15,10 +16,9 @@
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_asf extends getid3_handler class getid3_asf extends getid3_handler {
{
function __construct(getID3 $getid3) { public function __construct(getID3 $getid3) {
parent::__construct($getid3); // extends getid3_handler::__construct() parent::__construct($getid3); // extends getid3_handler::__construct()
// initialize all GUID constants // initialize all GUID constants
@ -30,7 +30,7 @@ class getid3_asf extends getid3_handler
} }
} }
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// Shortcuts // Shortcuts
@ -66,25 +66,22 @@ class getid3_asf extends getid3_handler
$info['fileformat'] = 'asf'; $info['fileformat'] = 'asf';
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$HeaderObjectData = fread($this->getid3->fp, 30); $HeaderObjectData = $this->fread(30);
$thisfile_asf_headerobject['objectid'] = substr($HeaderObjectData, 0, 16); $thisfile_asf_headerobject['objectid'] = substr($HeaderObjectData, 0, 16);
$thisfile_asf_headerobject['objectid_guid'] = $this->BytestringToGUID($thisfile_asf_headerobject['objectid']); $thisfile_asf_headerobject['objectid_guid'] = $this->BytestringToGUID($thisfile_asf_headerobject['objectid']);
if ($thisfile_asf_headerobject['objectid'] != GETID3_ASF_Header_Object) { if ($thisfile_asf_headerobject['objectid'] != GETID3_ASF_Header_Object) {
$info['warning'][] = 'ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}'; unset($info['fileformat'], $info['asf']);
unset($info['fileformat']); return $this->error('ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}');
unset($info['asf']);
return false;
break;
} }
$thisfile_asf_headerobject['objectsize'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 16, 8)); $thisfile_asf_headerobject['objectsize'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 16, 8));
$thisfile_asf_headerobject['headerobjects'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 24, 4)); $thisfile_asf_headerobject['headerobjects'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 24, 4));
$thisfile_asf_headerobject['reserved1'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 28, 1)); $thisfile_asf_headerobject['reserved1'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 28, 1));
$thisfile_asf_headerobject['reserved2'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 29, 1)); $thisfile_asf_headerobject['reserved2'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 29, 1));
$NextObjectOffset = ftell($this->getid3->fp); $NextObjectOffset = $this->ftell();
$ASFHeaderData = fread($this->getid3->fp, $thisfile_asf_headerobject['objectsize'] - 30); $ASFHeaderData = $this->fread($thisfile_asf_headerobject['objectsize'] - 30);
$offset = 0; $offset = 0;
for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) { for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) {
@ -226,7 +223,7 @@ class getid3_asf extends getid3_handler
$thisfile_audio['dataformat'] = (!empty($thisfile_audio['dataformat']) ? $thisfile_audio['dataformat'] : 'asf'); $thisfile_audio['dataformat'] = (!empty($thisfile_audio['dataformat']) ? $thisfile_audio['dataformat'] : 'asf');
$thisfile_audio['bitrate_mode'] = (!empty($thisfile_audio['bitrate_mode']) ? $thisfile_audio['bitrate_mode'] : 'cbr'); $thisfile_audio['bitrate_mode'] = (!empty($thisfile_audio['bitrate_mode']) ? $thisfile_audio['bitrate_mode'] : 'cbr');
$audiodata = getid3_riff::RIFFparseWAVEFORMATex(substr($StreamPropertiesObjectData['type_specific_data'], 0, 16)); $audiodata = getid3_riff::parseWAVEFORMATex(substr($StreamPropertiesObjectData['type_specific_data'], 0, 16));
unset($audiodata['raw']); unset($audiodata['raw']);
$thisfile_audio = getid3_lib::array_merge_noclobber($audiodata, $thisfile_audio); $thisfile_audio = getid3_lib::array_merge_noclobber($audiodata, $thisfile_audio);
break; break;
@ -284,7 +281,7 @@ class getid3_asf extends getid3_handler
$offset += 4; $offset += 4;
$thisfile_asf_headerextensionobject['extension_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_headerextensionobject['extension_data_size']); $thisfile_asf_headerextensionobject['extension_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_headerextensionobject['extension_data_size']);
$unhandled_sections = 0; $unhandled_sections = 0;
$thisfile_asf_headerextensionobject['extension_data_parsed'] = $this->ASF_HeaderExtensionObjectDataParse($thisfile_asf_headerextensionobject['extension_data'], $unhandled_sections); $thisfile_asf_headerextensionobject['extension_data_parsed'] = $this->HeaderExtensionObjectDataParse($thisfile_asf_headerextensionobject['extension_data'], $unhandled_sections);
if ($unhandled_sections === 0) { if ($unhandled_sections === 0) {
unset($thisfile_asf_headerextensionobject['extension_data']); unset($thisfile_asf_headerextensionobject['extension_data']);
} }
@ -332,7 +329,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf_codeclistobject_codecentries_current['type_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $thisfile_asf_codeclistobject_codecentries_current['type_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
$offset += 2; $offset += 2;
$thisfile_asf_codeclistobject_codecentries_current['type'] = $this->ASFCodecListObjectTypeLookup($thisfile_asf_codeclistobject_codecentries_current['type_raw']); $thisfile_asf_codeclistobject_codecentries_current['type'] = self::codecListObjectTypeLookup($thisfile_asf_codeclistobject_codecentries_current['type_raw']);
$CodecNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character $CodecNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character
$offset += 2; $offset += 2;
@ -826,22 +823,14 @@ class getid3_asf extends getid3_handler
break; break;
case 'id3': case 'id3':
// id3v2 module might not be loaded $this->getid3->include_module('tag.id3v2');
if (class_exists('getid3_id3v2')) {
$tempfile = tempnam(GETID3_TEMP_DIR, 'getID3');
$tempfilehandle = fopen($tempfile, 'wb');
$tempThisfileInfo = array('encoding'=>$info['encoding']);
fwrite($tempfilehandle, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
fclose($tempfilehandle);
$getid3_temp = new getID3(); $getid3_id3v2 = new getid3_id3v2($this->getid3);
$getid3_temp->openfile($tempfile); $getid3_id3v2->AnalyzeString($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
$getid3_id3v2 = new getid3_id3v2($getid3_temp); unset($getid3_id3v2);
$getid3_id3v2->Analyze();
$info['id3v2'] = $getid3_temp->info['id3v2'];
unset($getid3_temp, $getid3_id3v2);
unlink($tempfile); if ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length'] > 1024) {
$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = '<value too large to display>';
} }
break; break;
@ -860,7 +849,7 @@ class getid3_asf extends getid3_handler
$wm_picture_offset = 0; $wm_picture_offset = 0;
$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 1)); $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 1));
$wm_picture_offset += 1; $wm_picture_offset += 1;
$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type'] = $this->WMpictureTypeLookup($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id']); $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type'] = self::WMpictureTypeLookup($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id']);
$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 4)); $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 4));
$wm_picture_offset += 4; $wm_picture_offset += 4;
@ -1033,7 +1022,7 @@ class getid3_asf extends getid3_handler
$audiomediaoffset = 0; $audiomediaoffset = 0;
$thisfile_asf_audiomedia_currentstream = getid3_riff::RIFFparseWAVEFORMATex(substr($streamdata['type_specific_data'], $audiomediaoffset, 16)); $thisfile_asf_audiomedia_currentstream = getid3_riff::parseWAVEFORMATex(substr($streamdata['type_specific_data'], $audiomediaoffset, 16));
$audiomediaoffset += 16; $audiomediaoffset += 16;
$thisfile_audio['lossless'] = false; $thisfile_audio['lossless'] = false;
@ -1141,7 +1130,7 @@ class getid3_asf extends getid3_handler
} }
} }
$thisfile_asf_videomedia_currentstream['format_data']['codec'] = getid3_riff::RIFFfourccLookup($thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']); $thisfile_asf_videomedia_currentstream['format_data']['codec'] = getid3_riff::fourccLookup($thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']);
$thisfile_video['streams'][$streamnumber]['fourcc'] = $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']; $thisfile_video['streams'][$streamnumber]['fourcc'] = $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc'];
$thisfile_video['streams'][$streamnumber]['codec'] = $thisfile_asf_videomedia_currentstream['format_data']['codec']; $thisfile_video['streams'][$streamnumber]['codec'] = $thisfile_asf_videomedia_currentstream['format_data']['codec'];
@ -1156,8 +1145,8 @@ class getid3_asf extends getid3_handler
} }
} }
while (ftell($this->getid3->fp) < $info['avdataend']) { while ($this->ftell() < $info['avdataend']) {
$NextObjectDataHeader = fread($this->getid3->fp, 24); $NextObjectDataHeader = $this->fread(24);
$offset = 0; $offset = 0;
$NextObjectGUID = substr($NextObjectDataHeader, 0, 16); $NextObjectGUID = substr($NextObjectDataHeader, 0, 16);
$offset += 16; $offset += 16;
@ -1179,7 +1168,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf['data_object'] = array(); $thisfile_asf['data_object'] = array();
$thisfile_asf_dataobject = &$thisfile_asf['data_object']; $thisfile_asf_dataobject = &$thisfile_asf['data_object'];
$DataObjectData = $NextObjectDataHeader.fread($this->getid3->fp, 50 - 24); $DataObjectData = $NextObjectDataHeader.$this->fread(50 - 24);
$offset = 24; $offset = 24;
$thisfile_asf_dataobject['objectid'] = $NextObjectGUID; $thisfile_asf_dataobject['objectid'] = $NextObjectGUID;
@ -1207,9 +1196,9 @@ class getid3_asf extends getid3_handler
// * * Error Correction Present bits 1 // If set, use Opaque Data Packet structure, else use Payload structure // * * Error Correction Present bits 1 // If set, use Opaque Data Packet structure, else use Payload structure
// * Error Correction Data // * Error Correction Data
$info['avdataoffset'] = ftell($this->getid3->fp); $info['avdataoffset'] = $this->ftell();
fseek($this->getid3->fp, ($thisfile_asf_dataobject['objectsize'] - 50), SEEK_CUR); // skip actual audio/video data $this->fseek(($thisfile_asf_dataobject['objectsize'] - 50), SEEK_CUR); // skip actual audio/video data
$info['avdataend'] = ftell($this->getid3->fp); $info['avdataend'] = $this->ftell();
break; break;
case GETID3_ASF_Simple_Index_Object: case GETID3_ASF_Simple_Index_Object:
@ -1229,7 +1218,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf['simple_index_object'] = array(); $thisfile_asf['simple_index_object'] = array();
$thisfile_asf_simpleindexobject = &$thisfile_asf['simple_index_object']; $thisfile_asf_simpleindexobject = &$thisfile_asf['simple_index_object'];
$SimpleIndexObjectData = $NextObjectDataHeader.fread($this->getid3->fp, 56 - 24); $SimpleIndexObjectData = $NextObjectDataHeader.$this->fread(56 - 24);
$offset = 24; $offset = 24;
$thisfile_asf_simpleindexobject['objectid'] = $NextObjectGUID; $thisfile_asf_simpleindexobject['objectid'] = $NextObjectGUID;
@ -1246,7 +1235,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf_simpleindexobject['index_entries_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); $thisfile_asf_simpleindexobject['index_entries_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4));
$offset += 4; $offset += 4;
$IndexEntriesData = $SimpleIndexObjectData.fread($this->getid3->fp, 6 * $thisfile_asf_simpleindexobject['index_entries_count']); $IndexEntriesData = $SimpleIndexObjectData.$this->fread(6 * $thisfile_asf_simpleindexobject['index_entries_count']);
for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $thisfile_asf_simpleindexobject['index_entries_count']; $IndexEntriesCounter++) { for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $thisfile_asf_simpleindexobject['index_entries_count']; $IndexEntriesCounter++) {
$thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_number'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_number'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4));
$offset += 4; $offset += 4;
@ -1283,7 +1272,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf['asf_index_object'] = array(); $thisfile_asf['asf_index_object'] = array();
$thisfile_asf_asfindexobject = &$thisfile_asf['asf_index_object']; $thisfile_asf_asfindexobject = &$thisfile_asf['asf_index_object'];
$ASFIndexObjectData = $NextObjectDataHeader.fread($this->getid3->fp, 34 - 24); $ASFIndexObjectData = $NextObjectDataHeader.$this->fread(34 - 24);
$offset = 24; $offset = 24;
$thisfile_asf_asfindexobject['objectid'] = $NextObjectGUID; $thisfile_asf_asfindexobject['objectid'] = $NextObjectGUID;
@ -1297,7 +1286,7 @@ class getid3_asf extends getid3_handler
$thisfile_asf_asfindexobject['index_blocks_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); $thisfile_asf_asfindexobject['index_blocks_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4));
$offset += 4; $offset += 4;
$ASFIndexObjectData .= fread($this->getid3->fp, 4 * $thisfile_asf_asfindexobject['index_specifiers_count']); $ASFIndexObjectData .= $this->fread(4 * $thisfile_asf_asfindexobject['index_specifiers_count']);
for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) {
$IndexSpecifierStreamNumber = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); $IndexSpecifierStreamNumber = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2));
$offset += 2; $offset += 2;
@ -1307,17 +1296,17 @@ class getid3_asf extends getid3_handler
$thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type_text'] = $this->ASFIndexObjectIndexTypeLookup($thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type']); $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type_text'] = $this->ASFIndexObjectIndexTypeLookup($thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type']);
} }
$ASFIndexObjectData .= fread($this->getid3->fp, 4); $ASFIndexObjectData .= $this->fread(4);
$thisfile_asf_asfindexobject['index_entry_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); $thisfile_asf_asfindexobject['index_entry_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4));
$offset += 4; $offset += 4;
$ASFIndexObjectData .= fread($this->getid3->fp, 8 * $thisfile_asf_asfindexobject['index_specifiers_count']); $ASFIndexObjectData .= $this->fread(8 * $thisfile_asf_asfindexobject['index_specifiers_count']);
for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) {
$thisfile_asf_asfindexobject['block_positions'][$IndexSpecifiersCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 8)); $thisfile_asf_asfindexobject['block_positions'][$IndexSpecifiersCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 8));
$offset += 8; $offset += 8;
} }
$ASFIndexObjectData .= fread($this->getid3->fp, 4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']); $ASFIndexObjectData .= $this->fread(4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']);
for ($IndexEntryCounter = 0; $IndexEntryCounter < $thisfile_asf_asfindexobject['index_entry_count']; $IndexEntryCounter++) { for ($IndexEntryCounter = 0; $IndexEntryCounter < $thisfile_asf_asfindexobject['index_entry_count']; $IndexEntryCounter++) {
for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) {
$thisfile_asf_asfindexobject['offsets'][$IndexSpecifiersCounter][$IndexEntryCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); $thisfile_asf_asfindexobject['offsets'][$IndexSpecifiersCounter][$IndexEntryCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4));
@ -1332,9 +1321,9 @@ class getid3_asf extends getid3_handler
if ($this->GUIDname($NextObjectGUIDtext)) { if ($this->GUIDname($NextObjectGUIDtext)) {
$info['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8); $info['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8);
} else { } else {
$info['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.(ftell($this->getid3->fp) - 16 - 8); $info['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.($this->ftell() - 16 - 8);
} }
fseek($this->getid3->fp, ($NextObjectSize - 16 - 8), SEEK_CUR); $this->fseek(($NextObjectSize - 16 - 8), SEEK_CUR);
break; break;
} }
} }
@ -1433,10 +1422,10 @@ class getid3_asf extends getid3_handler
$thisfile_video['dataformat'] = (!empty($thisfile_video['dataformat']) ? $thisfile_video['dataformat'] : 'asf'); $thisfile_video['dataformat'] = (!empty($thisfile_video['dataformat']) ? $thisfile_video['dataformat'] : 'asf');
} }
if (!empty($thisfile_video['streams'])) { if (!empty($thisfile_video['streams'])) {
$thisfile_video['streams']['resolution_x'] = 0; $thisfile_video['resolution_x'] = 0;
$thisfile_video['streams']['resolution_y'] = 0; $thisfile_video['resolution_y'] = 0;
foreach ($thisfile_video['streams'] as $key => $valuearray) { foreach ($thisfile_video['streams'] as $key => $valuearray) {
if (($valuearray['resolution_x'] > $thisfile_video['streams']['resolution_x']) || ($valuearray['resolution_y'] > $thisfile_video['streams']['resolution_y'])) { if (($valuearray['resolution_x'] > $thisfile_video['resolution_x']) || ($valuearray['resolution_y'] > $thisfile_video['resolution_y'])) {
$thisfile_video['resolution_x'] = $valuearray['resolution_x']; $thisfile_video['resolution_x'] = $valuearray['resolution_x'];
$thisfile_video['resolution_y'] = $valuearray['resolution_y']; $thisfile_video['resolution_y'] = $valuearray['resolution_y'];
} }
@ -1451,18 +1440,17 @@ class getid3_asf extends getid3_handler
return true; return true;
} }
static function ASFCodecListObjectTypeLookup($CodecListType) { public static function codecListObjectTypeLookup($CodecListType) {
static $ASFCodecListObjectTypeLookup = array(); static $lookup = array(
if (empty($ASFCodecListObjectTypeLookup)) { 0x0001 => 'Video Codec',
$ASFCodecListObjectTypeLookup[0x0001] = 'Video Codec'; 0x0002 => 'Audio Codec',
$ASFCodecListObjectTypeLookup[0x0002] = 'Audio Codec'; 0xFFFF => 'Unknown Codec'
$ASFCodecListObjectTypeLookup[0xFFFF] = 'Unknown Codec'; );
}
return (isset($ASFCodecListObjectTypeLookup[$CodecListType]) ? $ASFCodecListObjectTypeLookup[$CodecListType] : 'Invalid Codec Type'); return (isset($lookup[$CodecListType]) ? $lookup[$CodecListType] : 'Invalid Codec Type');
} }
static function KnownGUIDs() { public static function KnownGUIDs() {
static $GUIDarray = array( static $GUIDarray = array(
'GETID3_ASF_Extended_Stream_Properties_Object' => '14E6A5CB-C672-4332-8399-A96952065B5A', 'GETID3_ASF_Extended_Stream_Properties_Object' => '14E6A5CB-C672-4332-8399-A96952065B5A',
'GETID3_ASF_Padding_Object' => '1806D474-CADF-4509-A4BA-9AABCB96AAE8', 'GETID3_ASF_Padding_Object' => '1806D474-CADF-4509-A4BA-9AABCB96AAE8',
@ -1576,15 +1564,15 @@ class getid3_asf extends getid3_handler
return $GUIDarray; return $GUIDarray;
} }
static function GUIDname($GUIDstring) { public static function GUIDname($GUIDstring) {
static $GUIDarray = array(); static $GUIDarray = array();
if (empty($GUIDarray)) { if (empty($GUIDarray)) {
$GUIDarray = getid3_asf::KnownGUIDs(); $GUIDarray = self::KnownGUIDs();
} }
return array_search($GUIDstring, $GUIDarray); return array_search($GUIDstring, $GUIDarray);
} }
static function ASFIndexObjectIndexTypeLookup($id) { public static function ASFIndexObjectIndexTypeLookup($id) {
static $ASFIndexObjectIndexTypeLookup = array(); static $ASFIndexObjectIndexTypeLookup = array();
if (empty($ASFIndexObjectIndexTypeLookup)) { if (empty($ASFIndexObjectIndexTypeLookup)) {
$ASFIndexObjectIndexTypeLookup[1] = 'Nearest Past Data Packet'; $ASFIndexObjectIndexTypeLookup[1] = 'Nearest Past Data Packet';
@ -1594,7 +1582,7 @@ class getid3_asf extends getid3_handler
return (isset($ASFIndexObjectIndexTypeLookup[$id]) ? $ASFIndexObjectIndexTypeLookup[$id] : 'invalid'); return (isset($ASFIndexObjectIndexTypeLookup[$id]) ? $ASFIndexObjectIndexTypeLookup[$id] : 'invalid');
} }
static function GUIDtoBytestring($GUIDstring) { public static function GUIDtoBytestring($GUIDstring) {
// Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way: // Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way:
// first 4 bytes are in little-endian order // first 4 bytes are in little-endian order
// next 2 bytes are appended in little-endian order // next 2 bytes are appended in little-endian order
@ -1629,7 +1617,7 @@ class getid3_asf extends getid3_handler
return $hexbytecharstring; return $hexbytecharstring;
} }
static function BytestringToGUID($Bytestring) { public static function BytestringToGUID($Bytestring) {
$GUIDstring = str_pad(dechex(ord($Bytestring{3})), 2, '0', STR_PAD_LEFT); $GUIDstring = str_pad(dechex(ord($Bytestring{3})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{2})), 2, '0', STR_PAD_LEFT); $GUIDstring .= str_pad(dechex(ord($Bytestring{2})), 2, '0', STR_PAD_LEFT);
$GUIDstring .= str_pad(dechex(ord($Bytestring{1})), 2, '0', STR_PAD_LEFT); $GUIDstring .= str_pad(dechex(ord($Bytestring{1})), 2, '0', STR_PAD_LEFT);
@ -1654,7 +1642,7 @@ class getid3_asf extends getid3_handler
return strtoupper($GUIDstring); return strtoupper($GUIDstring);
} }
static function FILETIMEtoUNIXtime($FILETIME, $round=true) { public static function FILETIMEtoUNIXtime($FILETIME, $round=true) {
// FILETIME is a 64-bit unsigned integer representing // FILETIME is a 64-bit unsigned integer representing
// the number of 100-nanosecond intervals since January 1, 1601 // the number of 100-nanosecond intervals since January 1, 1601
// UNIX timestamp is number of seconds since January 1, 1970 // UNIX timestamp is number of seconds since January 1, 1970
@ -1665,32 +1653,38 @@ class getid3_asf extends getid3_handler
return ($FILETIME - 116444736000000000) / 10000000; return ($FILETIME - 116444736000000000) / 10000000;
} }
static function WMpictureTypeLookup($WMpictureType) { public static function WMpictureTypeLookup($WMpictureType) {
static $WMpictureTypeLookup = array(); static $lookup = null;
if (empty($WMpictureTypeLookup)) { if ($lookup === null) {
$WMpictureTypeLookup[0x03] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Front Cover'); $lookup = array(
$WMpictureTypeLookup[0x04] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Back Cover'); 0x03 => 'Front Cover',
$WMpictureTypeLookup[0x00] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'User Defined'); 0x04 => 'Back Cover',
$WMpictureTypeLookup[0x05] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Leaflet Page'); 0x00 => 'User Defined',
$WMpictureTypeLookup[0x06] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Media Label'); 0x05 => 'Leaflet Page',
$WMpictureTypeLookup[0x07] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Lead Artist'); 0x06 => 'Media Label',
$WMpictureTypeLookup[0x08] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Artist'); 0x07 => 'Lead Artist',
$WMpictureTypeLookup[0x09] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Conductor'); 0x08 => 'Artist',
$WMpictureTypeLookup[0x0A] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Band'); 0x09 => 'Conductor',
$WMpictureTypeLookup[0x0B] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Composer'); 0x0A => 'Band',
$WMpictureTypeLookup[0x0C] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Lyricist'); 0x0B => 'Composer',
$WMpictureTypeLookup[0x0D] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Recording Location'); 0x0C => 'Lyricist',
$WMpictureTypeLookup[0x0E] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'During Recording'); 0x0D => 'Recording Location',
$WMpictureTypeLookup[0x0F] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'During Performance'); 0x0E => 'During Recording',
$WMpictureTypeLookup[0x10] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Video Screen Capture'); 0x0F => 'During Performance',
$WMpictureTypeLookup[0x12] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Illustration'); 0x10 => 'Video Screen Capture',
$WMpictureTypeLookup[0x13] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Band Logotype'); 0x12 => 'Illustration',
$WMpictureTypeLookup[0x14] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Publisher Logotype'); 0x13 => 'Band Logotype',
0x14 => 'Publisher Logotype'
);
$lookup = array_map(function($str) {
return getid3_lib::iconv_fallback('UTF-8', 'UTF-16LE', $str);
}, $lookup);
} }
return (isset($WMpictureTypeLookup[$WMpictureType]) ? $WMpictureTypeLookup[$WMpictureType] : '');
return (isset($lookup[$WMpictureType]) ? $lookup[$WMpictureType] : '');
} }
function ASF_HeaderExtensionObjectDataParse(&$asf_header_extension_object_data, &$unhandled_sections) { public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_data, &$unhandled_sections) {
// http://msdn.microsoft.com/en-us/library/bb643323.aspx // http://msdn.microsoft.com/en-us/library/bb643323.aspx
$offset = 0; $offset = 0;
@ -1825,7 +1819,7 @@ class getid3_asf extends getid3_handler
$descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); $descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2));
$offset += 2; $offset += 2;
$descriptionRecord['data_type_text'] = $this->ASFmetadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']); $descriptionRecord['data_type_text'] = self::metadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']);
$descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); $descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4));
$offset += 4; $offset += 4;
@ -1897,7 +1891,7 @@ class getid3_asf extends getid3_handler
$descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); $descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2));
$offset += 2; $offset += 2;
$descriptionRecord['data_type_text'] = $this->ASFmetadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']); $descriptionRecord['data_type_text'] = self::metadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']);
$descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); $descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4));
$offset += 4; $offset += 4;
@ -1937,8 +1931,8 @@ class getid3_asf extends getid3_handler
} }
static function ASFmetadataLibraryObjectDataTypeLookup($id) { public static function metadataLibraryObjectDataTypeLookup($id) {
static $ASFmetadataLibraryObjectDataTypeLookup = array( static $lookup = array(
0x0000 => 'Unicode string', // The data consists of a sequence of Unicode characters 0x0000 => 'Unicode string', // The data consists of a sequence of Unicode characters
0x0001 => 'BYTE array', // The type of the data is implementation-specific 0x0001 => 'BYTE array', // The type of the data is implementation-specific
0x0002 => 'BOOL', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer. Only 0x0000 or 0x0001 are permitted values 0x0002 => 'BOOL', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer. Only 0x0000 or 0x0001 are permitted values
@ -1947,10 +1941,10 @@ class getid3_asf extends getid3_handler
0x0005 => 'WORD', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer 0x0005 => 'WORD', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer
0x0006 => 'GUID', // The data is 16 bytes long and should be interpreted as a 128-bit GUID 0x0006 => 'GUID', // The data is 16 bytes long and should be interpreted as a 128-bit GUID
); );
return (isset($ASFmetadataLibraryObjectDataTypeLookup[$id]) ? $ASFmetadataLibraryObjectDataTypeLookup[$id] : 'invalid'); return (isset($lookup[$id]) ? $lookup[$id] : 'invalid');
} }
function ASF_WMpicture(&$data) { public function ASF_WMpicture(&$data) {
//typedef struct _WMPicture{ //typedef struct _WMPicture{
// LPWSTR pwszMIMEType; // LPWSTR pwszMIMEType;
// BYTE bPictureType; // BYTE bPictureType;
@ -1964,7 +1958,7 @@ class getid3_asf extends getid3_handler
$offset = 0; $offset = 0;
$WMpicture['image_type_id'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 1)); $WMpicture['image_type_id'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 1));
$offset += 1; $offset += 1;
$WMpicture['image_type'] = $this->WMpictureTypeLookup($WMpicture['image_type_id']); $WMpicture['image_type'] = self::WMpictureTypeLookup($WMpicture['image_type_id']);
$WMpicture['image_size'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 4)); $WMpicture['image_size'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 4));
$offset += 4; $offset += 4;
@ -2002,13 +1996,13 @@ class getid3_asf extends getid3_handler
// Remove terminator 00 00 and convert UTF-16LE to Latin-1 // Remove terminator 00 00 and convert UTF-16LE to Latin-1
static function TrimConvert($string) { public static function TrimConvert($string) {
return trim(getid3_lib::iconv_fallback('UTF-16LE', 'ISO-8859-1', getid3_asf::TrimTerm($string)), ' '); return trim(getid3_lib::iconv_fallback('UTF-16LE', 'ISO-8859-1', self::TrimTerm($string)), ' ');
} }
// Remove terminator 00 00 // Remove terminator 00 00
static function TrimTerm($string) { public static function TrimTerm($string) {
// remove terminator, only if present (it should be, but...) // remove terminator, only if present (it should be, but...)
if (substr($string, -2) === "\x00\x00") { if (substr($string, -2) === "\x00\x00") {
$string = substr($string, 0, -2); $string = substr($string, 0, -2);
@ -2017,5 +2011,3 @@ class getid3_asf extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,13 +18,13 @@
class getid3_bink extends getid3_handler class getid3_bink extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['error'][] = 'Bink / Smacker files not properly processed by this version of getID3() ['.$this->getid3->version().']'; $info['error'][] = 'Bink / Smacker files not properly processed by this version of getID3() ['.$this->getid3->version().']';
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$fileTypeID = fread($this->getid3->fp, 3); $fileTypeID = $this->fread(3);
switch ($fileTypeID) { switch ($fileTypeID) {
case 'BIK': case 'BIK':
return $this->ParseBink(); return $this->ParseBink();
@ -43,12 +44,12 @@ $info['error'][] = 'Bink / Smacker files not properly processed by this version
} }
function ParseBink() { public function ParseBink() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'bink'; $info['fileformat'] = 'bink';
$info['video']['dataformat'] = 'bink'; $info['video']['dataformat'] = 'bink';
$fileData = 'BIK'.fread($this->getid3->fp, 13); $fileData = 'BIK'.$this->fread(13);
$info['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4)); $info['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
$info['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2)); $info['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2));
@ -60,7 +61,7 @@ $info['error'][] = 'Bink / Smacker files not properly processed by this version
return true; return true;
} }
function ParseSmacker() { public function ParseSmacker() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'smacker'; $info['fileformat'] = 'smacker';
$info['video']['dataformat'] = 'smacker'; $info['video']['dataformat'] = 'smacker';
@ -69,5 +70,3 @@ $info['error'][] = 'Bink / Smacker files not properly processed by this version
} }
} }
?>

View file

@ -3,8 +3,9 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
// // // //
// FLV module by Seth Kaufman <seth@whirl-i-gig.com> // // FLV module by Seth Kaufman <sethØwhirl-i-gig*com> //
// // // //
// * version 0.1 (26 June 2005) // // * version 0.1 (26 June 2005) //
// // // //
@ -14,7 +15,7 @@
// // // //
// * version 0.2 (22 February 2006) // // * version 0.2 (22 February 2006) //
// Support for On2 VP6 codec and meta information // // Support for On2 VP6 codec and meta information //
// by Steve Webster <steve.webster@featurecreep.com> // // by Steve Webster <steve.websterØfeaturecreep*com> //
// // // //
// * version 0.3 (15 June 2006) // // * version 0.3 (15 June 2006) //
// Modified to not read entire file into memory // // Modified to not read entire file into memory //
@ -23,21 +24,26 @@
// * version 0.4 (07 December 2007) // // * version 0.4 (07 December 2007) //
// Bugfixes for incorrectly parsed FLV dimensions // // Bugfixes for incorrectly parsed FLV dimensions //
// and incorrect parsing of onMetaTag // // and incorrect parsing of onMetaTag //
// by Evgeny Moysevich <moysevich@gmail.com> // // by Evgeny Moysevich <moysevichØgmail*com> //
// // // //
// * version 0.5 (21 May 2009) // // * version 0.5 (21 May 2009) //
// Fixed parsing of audio tags and added additional codec // // Fixed parsing of audio tags and added additional codec //
// details. The duration is now read from onMetaTag (if // // details. The duration is now read from onMetaTag (if //
// exists), rather than parsing whole file // // exists), rather than parsing whole file //
// by Nigel Barnes <ngbarnes@hotmail.com> // // by Nigel Barnes <ngbarnesØhotmail*com> //
// // // //
// * version 0.6 (24 May 2009) // // * version 0.6 (24 May 2009) //
// Better parsing of files with h264 video // // Better parsing of files with h264 video //
// by Evgeny Moysevich <moysevichØgmail*com> // // by Evgeny Moysevich <moysevichØgmail*com> //
// // // //
// * version 0.6.1 (30 May 2011) // // * version 0.6.1 (30 May 2011) //
// prevent infinite loops in expGolombUe() // // prevent infinite loops in expGolombUe() //
// // // //
// * version 0.7.0 (16 Jul 2013) //
// handle GETID3_FLV_VIDEO_VP6FLV_ALPHA //
// improved AVCSequenceParameterSetReader::readData() //
// by Xander Schouwerwou <schouwerwouØgmail*com> //
// //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// module.audio-video.flv.php // // module.audio-video.flv.php //
@ -67,38 +73,38 @@ define('H264_PROFILE_HIGH422', 122);
define('H264_PROFILE_HIGH444', 144); define('H264_PROFILE_HIGH444', 144);
define('H264_PROFILE_HIGH444_PREDICTIVE', 244); define('H264_PROFILE_HIGH444_PREDICTIVE', 244);
class getid3_flv extends getid3_handler class getid3_flv extends getid3_handler {
{
var $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration
function Analyze() { const magic = 'FLV';
public $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration
public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$FLVdataLength = $info['avdataend'] - $info['avdataoffset']; $FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
$FLVheader = fread($this->getid3->fp, 5); $FLVheader = $this->fread(5);
$info['fileformat'] = 'flv'; $info['fileformat'] = 'flv';
$info['flv']['header']['signature'] = substr($FLVheader, 0, 3); $info['flv']['header']['signature'] = substr($FLVheader, 0, 3);
$info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1)); $info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
$TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1)); $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
$magic = 'FLV'; if ($info['flv']['header']['signature'] != self::magic) {
if ($info['flv']['header']['signature'] != $magic) { $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(self::magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"';
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"'; unset($info['flv'], $info['fileformat']);
unset($info['flv']);
unset($info['fileformat']);
return false; return false;
} }
$info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04); $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
$info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01); $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
$FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 4)); $FrameSizeDataLength = getid3_lib::BigEndian2Int($this->fread(4));
$FLVheaderFrameLength = 9; $FLVheaderFrameLength = 9;
if ($FrameSizeDataLength > $FLVheaderFrameLength) { if ($FrameSizeDataLength > $FLVheaderFrameLength) {
fseek($this->getid3->fp, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); $this->fseek($FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
} }
$Duration = 0; $Duration = 0;
$found_video = false; $found_video = false;
@ -108,15 +114,15 @@ class getid3_flv extends getid3_handler
$tagParseCount = 0; $tagParseCount = 0;
$info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0); $info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0);
$flv_framecount = &$info['flv']['framecount']; $flv_framecount = &$info['flv']['framecount'];
while (((ftell($this->getid3->fp) + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) { while ((($this->ftell() + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) {
$ThisTagHeader = fread($this->getid3->fp, 16); $ThisTagHeader = $this->fread(16);
$PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4)); $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4));
$TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1)); $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1));
$DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3)); $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3));
$Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3)); $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3));
$LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1)); $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
$NextOffset = ftell($this->getid3->fp) - 1 + $DataLength; $NextOffset = $this->ftell() - 1 + $DataLength;
if ($Timestamp > $Duration) { if ($Timestamp > $Duration) {
$Duration = $Timestamp; $Duration = $Timestamp;
} }
@ -140,10 +146,10 @@ class getid3_flv extends getid3_handler
$found_video = true; $found_video = true;
$info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07; $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
$FLVvideoHeader = fread($this->getid3->fp, 11); $FLVvideoHeader = $this->fread(11);
if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) { if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) {
// this code block contributed by: moysevichØgmail*com // this code block contributed by: moysevichØgmail*com
$AVCPacketType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 0, 1)); $AVCPacketType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 0, 1));
if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) { if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) {
@ -160,7 +166,7 @@ class getid3_flv extends getid3_handler
//$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2)); //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
$spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2)); $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
// read the first SequenceParameterSet // read the first SequenceParameterSet
$sps = fread($this->getid3->fp, $spsSize); $sps = $this->fread($spsSize);
if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red
$spsReader = new AVCSequenceParameterSetReader($sps); $spsReader = new AVCSequenceParameterSetReader($sps);
$spsReader->readData(); $spsReader->readData();
@ -169,7 +175,7 @@ class getid3_flv extends getid3_handler
} }
} }
} }
// end: moysevichØgmail*com // end: moysevichØgmail*com
} elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) { } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) {
@ -185,19 +191,15 @@ class getid3_flv extends getid3_handler
//$PictureSizeEnc <<= 1; //$PictureSizeEnc <<= 1;
//$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)); $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)) >> 7;
$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)) >> 7;
$PictureSizeEnc['x'] >>= 7;
$PictureSizeEnc['y'] >>= 7;
$info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF; $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
$info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF; $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
break; break;
case 1: case 1:
$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3)); $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3)) >> 7;
$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)); $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)) >> 7;
$PictureSizeEnc['x'] >>= 7;
$PictureSizeEnc['y'] >>= 7;
$info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF; $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
$info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF; $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
break; break;
@ -233,8 +235,22 @@ class getid3_flv extends getid3_handler
break; break;
} }
} elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_VP6FLV_ALPHA) {
/* contributed by schouwerwouØgmail*com */
if (!isset($info['video']['resolution_x'])) { // only when meta data isn't set
$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 2));
$info['video']['resolution_x'] = ($PictureSizeEnc['x'] & 0xFF) << 3;
$info['video']['resolution_y'] = ($PictureSizeEnc['y'] & 0xFF) << 3;
}
/* end schouwerwouØgmail*com */
}
if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) {
$info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
} }
$info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
} }
break; break;
@ -242,8 +258,8 @@ class getid3_flv extends getid3_handler
case GETID3_FLV_TAG_META: case GETID3_FLV_TAG_META:
if (!$found_meta) { if (!$found_meta) {
$found_meta = true; $found_meta = true;
fseek($this->getid3->fp, -1, SEEK_CUR); $this->fseek(-1, SEEK_CUR);
$datachunk = fread($this->getid3->fp, $DataLength); $datachunk = $this->fread($DataLength);
$AMFstream = new AMFStream($datachunk); $AMFstream = new AMFStream($datachunk);
$reader = new AMFReader($AMFstream); $reader = new AMFReader($AMFstream);
$eventName = $reader->readData(); $eventName = $reader->readData();
@ -279,7 +295,7 @@ class getid3_flv extends getid3_handler
// noop // noop
break; break;
} }
fseek($this->getid3->fp, $NextOffset, SEEK_SET); $this->fseek($NextOffset);
} }
$info['playtime_seconds'] = $Duration / 1000; $info['playtime_seconds'] = $Duration / 1000;
@ -288,16 +304,16 @@ class getid3_flv extends getid3_handler
} }
if ($info['flv']['header']['hasAudio']) { if ($info['flv']['header']['hasAudio']) {
$info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['audio']['audioFormat']); $info['audio']['codec'] = self::audioFormatLookup($info['flv']['audio']['audioFormat']);
$info['audio']['sample_rate'] = $this->FLVaudioRate($info['flv']['audio']['audioRate']); $info['audio']['sample_rate'] = self::audioRateLookup($info['flv']['audio']['audioRate']);
$info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info['flv']['audio']['audioSampleSize']); $info['audio']['bits_per_sample'] = self::audioBitDepthLookup($info['flv']['audio']['audioSampleSize']);
$info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
$info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed $info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
$info['audio']['dataformat'] = 'flv'; $info['audio']['dataformat'] = 'flv';
} }
if (!empty($info['flv']['header']['hasVideo'])) { if (!empty($info['flv']['header']['hasVideo'])) {
$info['video']['codec'] = $this->FLVvideoCodec($info['flv']['video']['videoCodec']); $info['video']['codec'] = self::videoCodecLookup($info['flv']['video']['videoCodec']);
$info['video']['dataformat'] = 'flv'; $info['video']['dataformat'] = 'flv';
$info['video']['lossless'] = false; $info['video']['lossless'] = false;
} }
@ -308,17 +324,17 @@ class getid3_flv extends getid3_handler
$info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
} }
if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) { if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) {
$info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']); $info['audio']['codec'] = self::audioFormatLookup($info['flv']['meta']['onMetaData']['audiocodecid']);
} }
if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) { if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
$info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']); $info['video']['codec'] = self::videoCodecLookup($info['flv']['meta']['onMetaData']['videocodecid']);
} }
return true; return true;
} }
function FLVaudioFormat($id) { public static function audioFormatLookup($id) {
$FLVaudioFormat = array( static $lookup = array(
0 => 'Linear PCM, platform endian', 0 => 'Linear PCM, platform endian',
1 => 'ADPCM', 1 => 'ADPCM',
2 => 'mp3', 2 => 'mp3',
@ -330,35 +346,35 @@ class getid3_flv extends getid3_handler
8 => 'G.711 mu-law logarithmic PCM', 8 => 'G.711 mu-law logarithmic PCM',
9 => 'reserved', 9 => 'reserved',
10 => 'AAC', 10 => 'AAC',
11 => false, // unknown? 11 => 'Speex',
12 => false, // unknown? 12 => false, // unknown?
13 => false, // unknown? 13 => false, // unknown?
14 => 'mp3 8kHz', 14 => 'mp3 8kHz',
15 => 'Device-specific sound', 15 => 'Device-specific sound',
); );
return (isset($FLVaudioFormat[$id]) ? $FLVaudioFormat[$id] : false); return (isset($lookup[$id]) ? $lookup[$id] : false);
} }
function FLVaudioRate($id) { public static function audioRateLookup($id) {
$FLVaudioRate = array( static $lookup = array(
0 => 5500, 0 => 5500,
1 => 11025, 1 => 11025,
2 => 22050, 2 => 22050,
3 => 44100, 3 => 44100,
); );
return (isset($FLVaudioRate[$id]) ? $FLVaudioRate[$id] : false); return (isset($lookup[$id]) ? $lookup[$id] : false);
} }
function FLVaudioBitDepth($id) { public static function audioBitDepthLookup($id) {
$FLVaudioBitDepth = array( static $lookup = array(
0 => 8, 0 => 8,
1 => 16, 1 => 16,
); );
return (isset($FLVaudioBitDepth[$id]) ? $FLVaudioBitDepth[$id] : false); return (isset($lookup[$id]) ? $lookup[$id] : false);
} }
function FLVvideoCodec($id) { public static function videoCodecLookup($id) {
$FLVvideoCodec = array( static $lookup = array(
GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', GETID3_FLV_VIDEO_H263 => 'Sorenson H.263',
GETID3_FLV_VIDEO_SCREEN => 'Screen video', GETID3_FLV_VIDEO_SCREEN => 'Screen video',
GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6', GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6',
@ -366,87 +382,87 @@ class getid3_flv extends getid3_handler
GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2', GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2',
GETID3_FLV_VIDEO_H264 => 'Sorenson H.264', GETID3_FLV_VIDEO_H264 => 'Sorenson H.264',
); );
return (isset($FLVvideoCodec[$id]) ? $FLVvideoCodec[$id] : false); return (isset($lookup[$id]) ? $lookup[$id] : false);
} }
} }
class AMFStream { class AMFStream {
var $bytes; public $bytes;
var $pos; public $pos;
function AMFStream(&$bytes) { public function __construct(&$bytes) {
$this->bytes =& $bytes; $this->bytes =& $bytes;
$this->pos = 0; $this->pos = 0;
} }
function readByte() { public function readByte() {
return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1)); return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
} }
function readInt() { public function readInt() {
return ($this->readByte() << 8) + $this->readByte(); return ($this->readByte() << 8) + $this->readByte();
} }
function readLong() { public function readLong() {
return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
} }
function readDouble() { public function readDouble() {
return getid3_lib::BigEndian2Float($this->read(8)); return getid3_lib::BigEndian2Float($this->read(8));
} }
function readUTF() { public function readUTF() {
$length = $this->readInt(); $length = $this->readInt();
return $this->read($length); return $this->read($length);
} }
function readLongUTF() { public function readLongUTF() {
$length = $this->readLong(); $length = $this->readLong();
return $this->read($length); return $this->read($length);
} }
function read($length) { public function read($length) {
$val = substr($this->bytes, $this->pos, $length); $val = substr($this->bytes, $this->pos, $length);
$this->pos += $length; $this->pos += $length;
return $val; return $val;
} }
function peekByte() { public function peekByte() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readByte(); $val = $this->readByte();
$this->pos = $pos; $this->pos = $pos;
return $val; return $val;
} }
function peekInt() { public function peekInt() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readInt(); $val = $this->readInt();
$this->pos = $pos; $this->pos = $pos;
return $val; return $val;
} }
function peekLong() { public function peekLong() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readLong(); $val = $this->readLong();
$this->pos = $pos; $this->pos = $pos;
return $val; return $val;
} }
function peekDouble() { public function peekDouble() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readDouble(); $val = $this->readDouble();
$this->pos = $pos; $this->pos = $pos;
return $val; return $val;
} }
function peekUTF() { public function peekUTF() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readUTF(); $val = $this->readUTF();
$this->pos = $pos; $this->pos = $pos;
return $val; return $val;
} }
function peekLongUTF() { public function peekLongUTF() {
$pos = $this->pos; $pos = $this->pos;
$val = $this->readLongUTF(); $val = $this->readLongUTF();
$this->pos = $pos; $this->pos = $pos;
@ -455,13 +471,13 @@ class AMFStream {
} }
class AMFReader { class AMFReader {
var $stream; public $stream;
function AMFReader(&$stream) { public function __construct(&$stream) {
$this->stream =& $stream; $this->stream =& $stream;
} }
function readData() { public function readData() {
$value = null; $value = null;
$type = $this->stream->readByte(); $type = $this->stream->readByte();
@ -531,19 +547,19 @@ class AMFReader {
return $value; return $value;
} }
function readDouble() { public function readDouble() {
return $this->stream->readDouble(); return $this->stream->readDouble();
} }
function readBoolean() { public function readBoolean() {
return $this->stream->readByte() == 1; return $this->stream->readByte() == 1;
} }
function readString() { public function readString() {
return $this->stream->readUTF(); return $this->stream->readUTF();
} }
function readObject() { public function readObject() {
// Get highest numerical index - ignored // Get highest numerical index - ignored
// $highestIndex = $this->stream->readLong(); // $highestIndex = $this->stream->readLong();
@ -560,7 +576,7 @@ class AMFReader {
return $data; return $data;
} }
function readMixedArray() { public function readMixedArray() {
// Get highest numerical index - ignored // Get highest numerical index - ignored
$highestIndex = $this->stream->readLong(); $highestIndex = $this->stream->readLong();
@ -581,7 +597,7 @@ class AMFReader {
return $data; return $data;
} }
function readArray() { public function readArray() {
$length = $this->stream->readLong(); $length = $this->stream->readLong();
$data = array(); $data = array();
@ -591,103 +607,103 @@ class AMFReader {
return $data; return $data;
} }
function readDate() { public function readDate() {
$timestamp = $this->stream->readDouble(); $timestamp = $this->stream->readDouble();
$timezone = $this->stream->readInt(); $timezone = $this->stream->readInt();
return $timestamp; return $timestamp;
} }
function readLongString() { public function readLongString() {
return $this->stream->readLongUTF(); return $this->stream->readLongUTF();
} }
function readXML() { public function readXML() {
return $this->stream->readLongUTF(); return $this->stream->readLongUTF();
} }
function readTypedObject() { public function readTypedObject() {
$className = $this->stream->readUTF(); $className = $this->stream->readUTF();
return $this->readObject(); return $this->readObject();
} }
} }
class AVCSequenceParameterSetReader { class AVCSequenceParameterSetReader {
var $sps; public $sps;
var $start = 0; public $start = 0;
var $currentBytes = 0; public $currentBytes = 0;
var $currentBits = 0; public $currentBits = 0;
var $width; public $width;
var $height; public $height;
function AVCSequenceParameterSetReader($sps) { public function __construct($sps) {
$this->sps = $sps; $this->sps = $sps;
} }
function readData() { public function readData() {
$this->skipBits(8); $this->skipBits(8);
$this->skipBits(8); $this->skipBits(8);
$profile = $this->getBits(8); // read profile $profile = $this->getBits(8); // read profile
$this->skipBits(16); if ($profile > 0) {
$this->expGolombUe(); // read sps id $this->skipBits(8);
if (in_array($profile, array(H264_PROFILE_HIGH, H264_PROFILE_HIGH10, H264_PROFILE_HIGH422, H264_PROFILE_HIGH444, H264_PROFILE_HIGH444_PREDICTIVE))) { $level_idc = $this->getBits(8); // level_idc
if ($this->expGolombUe() == 3) { $this->expGolombUe(); // seq_parameter_set_id // sps
$this->skipBits(1); $this->expGolombUe(); // log2_max_frame_num_minus4
} $picOrderType = $this->expGolombUe(); // pic_order_cnt_type
$this->expGolombUe(); if ($picOrderType == 0) {
$this->expGolombUe(); $this->expGolombUe(); // log2_max_pic_order_cnt_lsb_minus4
$this->skipBits(1); } elseif ($picOrderType == 1) {
if ($this->getBit()) { $this->skipBits(1); // delta_pic_order_always_zero_flag
for ($i = 0; $i < 8; $i++) { $this->expGolombSe(); // offset_for_non_ref_pic
if ($this->getBit()) { $this->expGolombSe(); // offset_for_top_to_bottom_field
$size = $i < 6 ? 16 : 64; $num_ref_frames_in_pic_order_cnt_cycle = $this->expGolombUe(); // num_ref_frames_in_pic_order_cnt_cycle
$lastScale = 8; for ($i = 0; $i < $num_ref_frames_in_pic_order_cnt_cycle; $i++) {
$nextScale = 8; $this->expGolombSe(); // offset_for_ref_frame[ i ]
for ($j = 0; $j < $size; $j++) {
if ($nextScale != 0) {
$deltaScale = $this->expGolombUe();
$nextScale = ($lastScale + $deltaScale + 256) % 256;
}
if ($nextScale != 0) {
$lastScale = $nextScale;
}
}
}
} }
} }
} $this->expGolombUe(); // num_ref_frames
$this->expGolombUe(); $this->skipBits(1); // gaps_in_frame_num_value_allowed_flag
$pocType = $this->expGolombUe(); $pic_width_in_mbs_minus1 = $this->expGolombUe(); // pic_width_in_mbs_minus1
if ($pocType == 0) { $pic_height_in_map_units_minus1 = $this->expGolombUe(); // pic_height_in_map_units_minus1
$this->expGolombUe();
} elseif ($pocType == 1) { $frame_mbs_only_flag = $this->getBits(1); // frame_mbs_only_flag
$this->skipBits(1); if ($frame_mbs_only_flag == 0) {
$this->expGolombSe(); $this->skipBits(1); // mb_adaptive_frame_field_flag
$this->expGolombSe();
$pocCycleLength = $this->expGolombUe();
for ($i = 0; $i < $pocCycleLength; $i++) {
$this->expGolombSe();
} }
$this->skipBits(1); // direct_8x8_inference_flag
$frame_cropping_flag = $this->getBits(1); // frame_cropping_flag
$frame_crop_left_offset = 0;
$frame_crop_right_offset = 0;
$frame_crop_top_offset = 0;
$frame_crop_bottom_offset = 0;
if ($frame_cropping_flag) {
$frame_crop_left_offset = $this->expGolombUe(); // frame_crop_left_offset
$frame_crop_right_offset = $this->expGolombUe(); // frame_crop_right_offset
$frame_crop_top_offset = $this->expGolombUe(); // frame_crop_top_offset
$frame_crop_bottom_offset = $this->expGolombUe(); // frame_crop_bottom_offset
}
$this->skipBits(1); // vui_parameters_present_flag
// etc
$this->width = (($pic_width_in_mbs_minus1 + 1) * 16) - ($frame_crop_left_offset * 2) - ($frame_crop_right_offset * 2);
$this->height = ((2 - $frame_mbs_only_flag) * ($pic_height_in_map_units_minus1 + 1) * 16) - ($frame_crop_top_offset * 2) - ($frame_crop_bottom_offset * 2);
} }
$this->expGolombUe();
$this->skipBits(1);
$this->width = ($this->expGolombUe() + 1) * 16;
$heightMap = $this->expGolombUe() + 1;
$this->height = (2 - $this->getBit()) * $heightMap * 16;
} }
function skipBits($bits) { public function skipBits($bits) {
$newBits = $this->currentBits + $bits; $newBits = $this->currentBits + $bits;
$this->currentBytes += (int)floor($newBits / 8); $this->currentBytes += (int)floor($newBits / 8);
$this->currentBits = $newBits % 8; $this->currentBits = $newBits % 8;
} }
function getBit() { public function getBit() {
$result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01; $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01;
$this->skipBits(1); $this->skipBits(1);
return $result; return $result;
} }
function getBits($bits) { public function getBits($bits) {
$result = 0; $result = 0;
for ($i = 0; $i < $bits; $i++) { for ($i = 0; $i < $bits; $i++) {
$result = ($result << 1) + $this->getBit(); $result = ($result << 1) + $this->getBit();
@ -695,7 +711,7 @@ class AVCSequenceParameterSetReader {
return $result; return $result;
} }
function expGolombUe() { public function expGolombUe() {
$significantBits = 0; $significantBits = 0;
$bit = $this->getBit(); $bit = $this->getBit();
while ($bit == 0) { while ($bit == 0) {
@ -710,7 +726,7 @@ class AVCSequenceParameterSetReader {
return (1 << $significantBits) + $this->getBits($significantBits) - 1; return (1 << $significantBits) + $this->getBits($significantBits) - 1;
} }
function expGolombSe() { public function expGolombSe() {
$result = $this->expGolombUe(); $result = $this->expGolombUe();
if (($result & 0x01) == 0) { if (($result & 0x01) == 0) {
return -($result >> 1); return -($result >> 1);
@ -719,13 +735,11 @@ class AVCSequenceParameterSetReader {
} }
} }
function getWidth() { public function getWidth() {
return $this->width; return $this->width;
} }
function getHeight() { public function getHeight() {
return $this->height; return $this->height;
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -14,7 +15,6 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// from: http://www.matroska.org/technical/specs/index.html
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. 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.
define('EBML_ID_SEEKHEAD', 0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements. define('EBML_ID_SEEKHEAD', 0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
define('EBML_ID_TAGS', 0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found <http://www.matroska.org/technical/specs/tagging/index.html>. define('EBML_ID_TAGS', 0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found <http://www.matroska.org/technical/specs/tagging/index.html>.
@ -90,7 +90,8 @@ define('EBML_ID_CUEBLOCKNUMBER', 0x1378); // [53][78] --
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. 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.
define('EBML_ID_SEEKID', 0x13AB); // [53][AB] -- The binary ID corresponding to the element name. define('EBML_ID_SEEKID', 0x13AB); // [53][AB] -- The binary ID corresponding to the element name.
define('EBML_ID_SEEKPOSITION', 0x13AC); // [53][AC] -- The position of the element in the segment in octets (0 = first level 1 element). define('EBML_ID_SEEKPOSITION', 0x13AC); // [53][AC] -- The position of the element in the segment in octets (0 = first level 1 element).
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). define('EBML_ID_STEREOMODE', 0x13B8); // [53][B8] -- Stereo-3D video mode.
define('EBML_ID_OLDSTEREOMODE', 0x13B9); // [53][B9] -- Bogus StereoMode value used in old versions of libmatroska. DO NOT USE. (0: mono, 1: right eye, 2: left eye, 3: both eyes).
define('EBML_ID_PIXELCROPBOTTOM', 0x14AA); // [54][AA] -- The number of video pixels to remove at the bottom of the image (for HDTV content). define('EBML_ID_PIXELCROPBOTTOM', 0x14AA); // [54][AA] -- The number of video pixels to remove at the bottom of the image (for HDTV content).
define('EBML_ID_DISPLAYWIDTH', 0x14B0); // [54][B0] -- Width of the video frames to display. define('EBML_ID_DISPLAYWIDTH', 0x14B0); // [54][B0] -- Width of the video frames to display.
define('EBML_ID_DISPLAYUNIT', 0x14B2); // [54][B2] -- Type of the unit for DisplayWidth/Height (0: pixels, 1: centimeters, 2: inches). define('EBML_ID_DISPLAYUNIT', 0x14B2); // [54][B2] -- Type of the unit for DisplayWidth/Height (0: pixels, 1: centimeters, 2: inches).
@ -206,19 +207,25 @@ define('EBML_ID_CLUSTERREFERENCEBLOCK', 0x7B); // [FB] --
define('EBML_ID_CLUSTERREFERENCEVIRTUAL', 0x7D); // [FD] -- Relative position of the data that should be in position of the virtual block. define('EBML_ID_CLUSTERREFERENCEVIRTUAL', 0x7D); // [FD] -- Relative position of the data that should be in position of the virtual block.
/**
* @tutorial http://www.matroska.org/technical/specs/index.html
*
* @todo Rewrite EBML parser to reduce it's size and honor default element values
* @todo After rewrite implement stream size calculation, that will provide additional useful info and enable AAC/FLAC audio bitrate detection
*/
class getid3_matroska extends getid3_handler class getid3_matroska extends getid3_handler
{ {
// public options // public options
public static $hide_clusters = true; // if true, do not return information about CLUSTER chunks, since there's a lot of them and they're not usually useful [default: TRUE] public static $hide_clusters = true; // if true, do not return information about CLUSTER chunks, since there's a lot of them and they're not usually useful [default: TRUE]
public static $parse_whole_file = false; // true to parse the whole file, not only header [default: FALSE] public static $parse_whole_file = false; // true to parse the whole file, not only header [default: FALSE]
// private parser settings/placeholders
private $EBMLbuffer = '';
private $EBMLbuffer_offset = 0;
private $EBMLbuffer_length = 0;
private $current_offset = 0;
private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID);
// private parser settings/placeholders
private $EBMLbuffer = '';
private $EBMLbuffer_offset = 0;
private $EBMLbuffer_length = 0;
private $current_offset = 0;
private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID);
public function Analyze() public function Analyze()
{ {
$info = &$this->getid3->info; $info = &$this->getid3->info;
@ -226,8 +233,7 @@ class getid3_matroska extends getid3_handler
// parse container // parse container
try { try {
$this->parseEBML($info); $this->parseEBML($info);
} } catch (Exception $e) {
catch (Exception $e) {
$info['error'][] = 'EBML parser: '.$e->getMessage(); $info['error'][] = 'EBML parser: '.$e->getMessage();
} }
@ -252,44 +258,71 @@ class getid3_matroska extends getid3_handler
// process tracks // process tracks
if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) { if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
foreach ($info['matroska']['tracks']['tracks'] as $key => $trackarray) { foreach ($info['matroska']['tracks']['tracks'] as $key => $trackarray) {
$track_info = array(); $track_info = array();
$track_info['dataformat'] = self::MatroskaCodecIDtoCommonName($trackarray['CodecID']); $track_info['dataformat'] = self::CodecIDtoCommonName($trackarray['CodecID']);
$track_info['default'] = (isset($trackarray['FlagDefault']) ? $trackarray['FlagDefault'] : true); $track_info['default'] = (isset($trackarray['FlagDefault']) ? $trackarray['FlagDefault'] : true);
if (isset($trackarray['Name'])) { $track_info['name'] = $trackarray['Name']; } if (isset($trackarray['Name'])) { $track_info['name'] = $trackarray['Name']; }
switch ($trackarray['TrackType']) { switch ($trackarray['TrackType']) {
case 1: // Video case 1: // Video
$track_info['resolution_x'] = $trackarray['PixelWidth']; $track_info['resolution_x'] = $trackarray['PixelWidth'];
$track_info['resolution_y'] = $trackarray['PixelHeight']; $track_info['resolution_y'] = $trackarray['PixelHeight'];
if (isset($trackarray['DisplayWidth'])) { $track_info['display_x'] = $trackarray['DisplayWidth']; } $track_info['display_unit'] = self::displayUnit(isset($trackarray['DisplayUnit']) ? $trackarray['DisplayUnit'] : 0);
if (isset($trackarray['DisplayHeight'])) { $track_info['display_y'] = $trackarray['DisplayHeight']; } $track_info['display_x'] = (isset($trackarray['DisplayWidth']) ? $trackarray['DisplayWidth'] : $trackarray['PixelWidth']);
if (isset($trackarray['DefaultDuration'])) { $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3); } $track_info['display_y'] = (isset($trackarray['DisplayHeight']) ? $trackarray['DisplayHeight'] : $trackarray['PixelHeight']);
//if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; }
if (isset($trackarray['PixelCropBottom'])) { $track_info['crop_bottom'] = $trackarray['PixelCropBottom']; }
if (isset($trackarray['PixelCropTop'])) { $track_info['crop_top'] = $trackarray['PixelCropTop']; }
if (isset($trackarray['PixelCropLeft'])) { $track_info['crop_left'] = $trackarray['PixelCropLeft']; }
if (isset($trackarray['PixelCropRight'])) { $track_info['crop_right'] = $trackarray['PixelCropRight']; }
if (isset($trackarray['DefaultDuration'])) { $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3); }
if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; }
switch ($trackarray['CodecID']) { switch ($trackarray['CodecID']) {
case 'V_MS/VFW/FOURCC': case 'V_MS/VFW/FOURCC':
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
$this->getid3->warning('Unable to parse codec private data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio-video.riff.php"');
break;
}
$parsed = getid3_riff::ParseBITMAPINFOHEADER($trackarray['CodecPrivate']); $parsed = getid3_riff::ParseBITMAPINFOHEADER($trackarray['CodecPrivate']);
$track_info['codec'] = getid3_riff::RIFFfourccLookup($parsed['fourcc']); $track_info['codec'] = getid3_riff::fourccLookup($parsed['fourcc']);
$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed; $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
break; break;
/*case 'V_MPEG4/ISO/AVC':
$h264['profile'] = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 1, 1));
$h264['level'] = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 3, 1));
$rn = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 4, 1));
$h264['NALUlength'] = ($rn & 3) + 1;
$rn = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 5, 1));
$nsps = ($rn & 31);
$offset = 6;
for ($i = 0; $i < $nsps; $i ++) {
$length = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2));
$h264['SPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length);
$offset += 2 + $length;
}
$npps = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 1));
$offset += 1;
for ($i = 0; $i < $npps; $i ++) {
$length = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2));
$h264['PPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length);
$offset += 2 + $length;
}
$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $h264;
break;*/
} }
$info['video']['streams'][] = $track_info; $info['video']['streams'][] = $track_info;
break; break;
case 2: // Audio case 2: // Audio
$track_info['sample_rate'] = (isset($trackarray['SamplingFrequency']) ? $trackarray['SamplingFrequency'] : 8000.0); $track_info['sample_rate'] = (isset($trackarray['SamplingFrequency']) ? $trackarray['SamplingFrequency'] : 8000.0);
$track_info['channels'] = (isset($trackarray['Channels']) ? $trackarray['Channels'] : 1); $track_info['channels'] = (isset($trackarray['Channels']) ? $trackarray['Channels'] : 1);
$track_info['language'] = (isset($trackarray['Language']) ? $trackarray['Language'] : 'eng'); $track_info['language'] = (isset($trackarray['Language']) ? $trackarray['Language'] : 'eng');
if (isset($trackarray['BitDepth'])) { $track_info['bits_per_sample'] = $trackarray['BitDepth']; } if (isset($trackarray['BitDepth'])) { $track_info['bits_per_sample'] = $trackarray['BitDepth']; }
//if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; } if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; }
switch ($trackarray['CodecID']) { switch ($trackarray['CodecID']) {
case 'A_PCM/INT/LIT': case 'A_PCM/INT/LIT':
case 'A_PCM/INT/BIG': case 'A_PCM/INT/BIG':
@ -299,32 +332,29 @@ class getid3_matroska extends getid3_handler
case 'A_AC3': case 'A_AC3':
case 'A_DTS': case 'A_DTS':
case 'A_MPEG/L3': case 'A_MPEG/L3':
//case 'A_FLAC': case 'A_MPEG/L2':
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.$track_info['dataformat'].'.php', __FILE__, false)) { case 'A_FLAC':
$this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.'.$track_info['dataformat'].'.php"'); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']).'.php', __FILE__, true);
break;
}
if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) { if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) {
$this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set'); $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set');
break; break;
} }
// create temp instance // create temp instance
$getid3_temp = new getID3(); $getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename); if ($track_info['dataformat'] != 'flac') {
$getid3_temp->openfile($this->getid3->filename);
}
$getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset']; $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
if ($track_info['dataformat'] == 'mp3' || $track_info['dataformat'] == 'flac') { if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') {
$getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length']; $getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length'];
} }
// analyze // analyze
$class = 'getid3_'.$track_info['dataformat']; $class = 'getid3_'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']);
$header_data_key = $track_info['dataformat'] == 'mp3' ? 'mpeg' : $track_info['dataformat']; $header_data_key = $track_info['dataformat'][0] == 'm' ? 'mpeg' : $track_info['dataformat'];
$getid3_audio = new $class($getid3_temp); $getid3_audio = new $class($getid3_temp, __CLASS__);
if ($track_info['dataformat'] == 'mp3') {
$getid3_audio->allow_bruteforce = true;
}
if ($track_info['dataformat'] == 'flac') { if ($track_info['dataformat'] == 'flac') {
$getid3_audio->AnalyzeString($trackarray['CodecPrivate']); $getid3_audio->AnalyzeString($trackarray['CodecPrivate']);
} }
@ -332,7 +362,6 @@ class getid3_matroska extends getid3_handler
$getid3_audio->Analyze(); $getid3_audio->Analyze();
} }
if (!empty($getid3_temp->info[$header_data_key])) { if (!empty($getid3_temp->info[$header_data_key])) {
unset($getid3_temp->info[$header_data_key]['GETID3_VERSION']);
$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key]; $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key];
if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
foreach ($getid3_temp->info['audio'] as $key => $value) { foreach ($getid3_temp->info['audio'] as $key => $value) {
@ -341,22 +370,18 @@ class getid3_matroska extends getid3_handler
} }
} }
else { else {
$this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because '.$class.'::Analyze() failed at offset '.$getid3_temp->info['avdataoffset']); $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because '.$class.'::Analyze() failed at offset '.$getid3_temp->info['avdataoffset']);
} }
// copy errors and warnings // copy errors and warnings
if (!empty($getid3_temp->info['error'])) { if (!empty($getid3_temp->info['error'])) {
foreach ($getid3_temp->info['error'] as $newerror) { foreach ($getid3_temp->info['error'] as $newerror) {
$this->getid3->warning($class.'() says: ['.$newerror.']'); $this->warning($class.'() says: ['.$newerror.']');
} }
} }
if (!empty($getid3_temp->info['warning'])) { if (!empty($getid3_temp->info['warning'])) {
foreach ($getid3_temp->info['warning'] as $newerror) { foreach ($getid3_temp->info['warning'] as $newerror) {
if ($track_info['dataformat'] == 'mp3' && preg_match('/^Probable truncated file: expecting \d+ bytes of audio data, only found \d+ \(short by \d+ bytes\)$/', $newerror)) { $this->warning($class.'() says: ['.$newerror.']');
// LAME/Xing header is probably set, but audio data is chunked into Matroska file and near-impossible to verify if audio stream is complete, so ignore useless warning
continue;
}
$this->getid3->warning($class.'() says: ['.$newerror.']');
} }
} }
unset($getid3_temp, $getid3_audio); unset($getid3_temp, $getid3_audio);
@ -364,30 +389,28 @@ class getid3_matroska extends getid3_handler
case 'A_AAC': case 'A_AAC':
case 'A_AAC/MPEG2/LC': case 'A_AAC/MPEG2/LC':
case 'A_AAC/MPEG2/LC/SBR':
case 'A_AAC/MPEG4/LC': case 'A_AAC/MPEG4/LC':
case 'A_AAC/MPEG4/LC/SBR': case 'A_AAC/MPEG4/LC/SBR':
$this->getid3->warning($trackarray['CodecID'].' audio data contains no header, audio/video bitrates can\'t be calculated'); $this->warning($trackarray['CodecID'].' audio data contains no header, audio/video bitrates can\'t be calculated');
break; break;
case 'A_VORBIS': case 'A_VORBIS':
if (!isset($trackarray['CodecPrivate'])) { if (!isset($trackarray['CodecPrivate'])) {
$this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data not set'); $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data not set');
break; break;
} }
$vorbis_offset = strpos($trackarray['CodecPrivate'], 'vorbis', 1); $vorbis_offset = strpos($trackarray['CodecPrivate'], 'vorbis', 1);
if ($vorbis_offset === false) { if ($vorbis_offset === false) {
$this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data does not contain "vorbis" keyword'); $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data does not contain "vorbis" keyword');
break; break;
} }
$vorbis_offset -= 1; $vorbis_offset -= 1;
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
$this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.ogg.php"');
}
// create temp instance // create temp instance
$getid3_temp = new getID3(); $getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
// analyze // analyze
$getid3_ogg = new getid3_ogg($getid3_temp); $getid3_ogg = new getid3_ogg($getid3_temp);
@ -401,19 +424,19 @@ class getid3_matroska extends getid3_handler
} }
} }
} }
// copy errors and warnings // copy errors and warnings
if (!empty($getid3_temp->info['error'])) { if (!empty($getid3_temp->info['error'])) {
foreach ($getid3_temp->info['error'] as $newerror) { foreach ($getid3_temp->info['error'] as $newerror) {
$this->getid3->warning('getid3_ogg() says: ['.$newerror.']'); $this->warning('getid3_ogg() says: ['.$newerror.']');
} }
} }
if (!empty($getid3_temp->info['warning'])) { if (!empty($getid3_temp->info['warning'])) {
foreach ($getid3_temp->info['warning'] as $newerror) { foreach ($getid3_temp->info['warning'] as $newerror) {
$this->getid3->warning('getid3_ogg() says: ['.$newerror.']'); $this->warning('getid3_ogg() says: ['.$newerror.']');
} }
} }
if (!empty($getid3_temp->info['ogg']['bitrate_nominal'])) { if (!empty($getid3_temp->info['ogg']['bitrate_nominal'])) {
$track_info['bitrate'] = $getid3_temp->info['ogg']['bitrate_nominal']; $track_info['bitrate'] = $getid3_temp->info['ogg']['bitrate_nominal'];
} }
@ -421,12 +444,9 @@ class getid3_matroska extends getid3_handler
break; break;
case 'A_MS/ACM': case 'A_MS/ACM':
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
$this->getid3->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio-video.riff.php"');
break; $parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']);
}
$parsed = getid3_riff::RIFFparseWAVEFORMATex($trackarray['CodecPrivate']);
foreach ($parsed as $key => $value) { foreach ($parsed as $key => $value) {
if ($key != 'raw') { if ($key != 'raw') {
$track_info[$key] = $value; $track_info[$key] = $value;
@ -434,16 +454,16 @@ class getid3_matroska extends getid3_handler
} }
$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed; $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
break; break;
default: default:
$this->getid3->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"'); $this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"');
} }
$info['audio']['streams'][] = $track_info; $info['audio']['streams'][] = $track_info;
break; break;
} }
} }
if (!empty($info['video']['streams'])) { if (!empty($info['video']['streams'])) {
$info['video'] = self::getDefaultStreamInfo($info['video']['streams']); $info['video'] = self::getDefaultStreamInfo($info['video']['streams']);
} }
@ -452,7 +472,16 @@ class getid3_matroska extends getid3_handler
} }
} }
// determine mime type // process attachments
if (isset($info['matroska']['attachments']) && $this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE) {
foreach ($info['matroska']['attachments'] as $i => $entry) {
if (strpos($entry['FileMimeType'], 'image/') === 0 && !empty($entry['FileData'])) {
$info['matroska']['comments']['picture'][] = array('data' => $entry['FileData'], 'image_mime' => $entry['FileMimeType'], 'filename' => $entry['FileName']);
}
}
}
// determine mime type
if (!empty($info['video']['streams'])) { if (!empty($info['video']['streams'])) {
$info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'video/webm' : 'video/x-matroska'); $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'video/webm' : 'video/x-matroska');
} elseif (!empty($info['audio']['streams'])) { } elseif (!empty($info['audio']['streams'])) {
@ -464,11 +493,7 @@ class getid3_matroska extends getid3_handler
return true; return true;
} }
private function parseEBML(&$info) {
///////////////////////////////////////
private function parseEBML(&$info)
{
// http://www.matroska.org/technical/specs/index.html#EBMLBasics // http://www.matroska.org/technical/specs/index.html#EBMLBasics
$this->current_offset = $info['avdataoffset']; $this->current_offset = $info['avdataoffset'];
@ -476,7 +501,6 @@ class getid3_matroska extends getid3_handler
switch ($top_element['id']) { switch ($top_element['id']) {
case EBML_ID_EBML: case EBML_ID_EBML:
$info['fileformat'] = 'matroska';
$info['matroska']['header']['offset'] = $top_element['offset']; $info['matroska']['header']['offset'] = $top_element['offset'];
$info['matroska']['header']['length'] = $top_element['length']; $info['matroska']['header']['length'] = $top_element['length'];
@ -495,20 +519,15 @@ class getid3_matroska extends getid3_handler
case EBML_ID_DOCTYPE: case EBML_ID_DOCTYPE:
$element_data['data'] = getid3_lib::trimNullByte($element_data['data']); $element_data['data'] = getid3_lib::trimNullByte($element_data['data']);
$info['matroska']['doctype'] = $element_data['data']; $info['matroska']['doctype'] = $element_data['data'];
break; $info['fileformat'] = $element_data['data'];
case EBML_ID_CRC32: // not useful, ignore
$this->current_offset = $element_data['end'];
unset($element_data);
break; break;
default: default:
$this->unhandledElement('header', __LINE__, $element_data); $this->unhandledElement('header', __LINE__, $element_data);
} }
if (!empty($element_data)) {
unset($element_data['offset'], $element_data['end']); unset($element_data['offset'], $element_data['end']);
$info['matroska']['header']['elements'][] = $element_data; $info['matroska']['header']['elements'][] = $element_data;
}
} }
break; break;
@ -564,7 +583,7 @@ class getid3_matroska extends getid3_handler
case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements. case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements.
while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS))) { while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) {
switch ($subelement['id']) { switch ($subelement['id']) {
case EBML_ID_TRACKNUMBER: case EBML_ID_TRACKNUMBER:
@ -587,9 +606,9 @@ class getid3_matroska extends getid3_handler
case EBML_ID_CODECNAME: case EBML_ID_CODECNAME:
$track_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']); $track_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
break; break;
case EBML_ID_CODECPRIVATE: case EBML_ID_CODECPRIVATE:
$track_entry[$subelement['id_name']] = $subelement['data']; $track_entry[$subelement['id_name']] = $this->readEBMLelementData($subelement['length'], true);
break; break;
case EBML_ID_FLAGENABLED: case EBML_ID_FLAGENABLED:
@ -607,7 +626,6 @@ class getid3_matroska extends getid3_handler
case EBML_ID_PIXELWIDTH: case EBML_ID_PIXELWIDTH:
case EBML_ID_PIXELHEIGHT: case EBML_ID_PIXELHEIGHT:
case EBML_ID_STEREOMODE:
case EBML_ID_PIXELCROPBOTTOM: case EBML_ID_PIXELCROPBOTTOM:
case EBML_ID_PIXELCROPTOP: case EBML_ID_PIXELCROPTOP:
case EBML_ID_PIXELCROPLEFT: case EBML_ID_PIXELCROPLEFT:
@ -616,6 +634,8 @@ class getid3_matroska extends getid3_handler
case EBML_ID_DISPLAYHEIGHT: case EBML_ID_DISPLAYHEIGHT:
case EBML_ID_DISPLAYUNIT: case EBML_ID_DISPLAYUNIT:
case EBML_ID_ASPECTRATIOTYPE: case EBML_ID_ASPECTRATIOTYPE:
case EBML_ID_STEREOMODE:
case EBML_ID_OLDSTEREOMODE:
$track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
break; break;
@ -752,8 +772,6 @@ class getid3_matroska extends getid3_handler
while ($this->getEBMLelement($subelement, $element_data['end'], true)) { while ($this->getEBMLelement($subelement, $element_data['end'], true)) {
switch ($subelement['id']) { switch ($subelement['id']) {
case EBML_ID_CHAPTERTRANSLATEEDITIONUID:
case EBML_ID_CHAPTERTRANSLATECODEC:
case EBML_ID_TIMECODESCALE: case EBML_ID_TIMECODESCALE:
$info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']); $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
break; break;
@ -770,11 +788,13 @@ class getid3_matroska extends getid3_handler
case EBML_ID_SEGMENTUID: case EBML_ID_SEGMENTUID:
case EBML_ID_PREVUID: case EBML_ID_PREVUID:
case EBML_ID_NEXTUID: case EBML_ID_NEXTUID:
case EBML_ID_SEGMENTFAMILY:
case EBML_ID_CHAPTERTRANSLATEID:
$info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']); $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
break; break;
case EBML_ID_SEGMENTFAMILY:
$info_entry[$subelement['id_name']][] = getid3_lib::trimNullByte($subelement['data']);
break;
case EBML_ID_SEGMENTFILENAME: case EBML_ID_SEGMENTFILENAME:
case EBML_ID_PREVFILENAME: case EBML_ID_PREVFILENAME:
case EBML_ID_NEXTFILENAME: case EBML_ID_NEXTFILENAME:
@ -785,6 +805,31 @@ class getid3_matroska extends getid3_handler
$info['matroska']['comments'][strtolower($subelement['id_name'])][] = $info_entry[$subelement['id_name']]; $info['matroska']['comments'][strtolower($subelement['id_name'])][] = $info_entry[$subelement['id_name']];
break; break;
case EBML_ID_CHAPTERTRANSLATE:
$chaptertranslate_entry = array();
while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
switch ($sub_subelement['id']) {
case EBML_ID_CHAPTERTRANSLATEEDITIONUID:
$chaptertranslate_entry[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data']);
break;
case EBML_ID_CHAPTERTRANSLATECODEC:
$chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
break;
case EBML_ID_CHAPTERTRANSLATEID:
$chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
break;
default:
$this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement);
}
}
$info_entry[$subelement['id_name']] = $chaptertranslate_entry;
break;
default: default:
$this->unhandledElement('info', __LINE__, $subelement); $this->unhandledElement('info', __LINE__, $subelement);
} }
@ -809,8 +854,8 @@ class getid3_matroska extends getid3_handler
switch ($sub_subelement['id']) { switch ($sub_subelement['id']) {
case EBML_ID_CUETRACKPOSITIONS: case EBML_ID_CUETRACKPOSITIONS:
$cuetrackpositions_entry = array(); $cuetrackpositions_entry = array();
while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) { while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
switch ($sub_sub_subelement['id']) { switch ($sub_sub_subelement['id']) {
@ -847,14 +892,14 @@ class getid3_matroska extends getid3_handler
break; break;
case EBML_ID_TAGS: // Element containing elements specific to Tracks/Chapters. case EBML_ID_TAGS: // Element containing elements specific to Tracks/Chapters.
$tags_entry = array(); $tags_entry = array();
while ($this->getEBMLelement($subelement, $element_data['end'], false)) { while ($this->getEBMLelement($subelement, $element_data['end'], false)) {
switch ($subelement['id']) { switch ($subelement['id']) {
case EBML_ID_TAG: case EBML_ID_TAG:
$tag_entry = array(); $tag_entry = array();
while ($this->getEBMLelement($sub_subelement, $subelement['end'], false)) { while ($this->getEBMLelement($sub_subelement, $subelement['end'], false)) {
switch ($sub_subelement['id']) { switch ($sub_subelement['id']) {
@ -866,14 +911,14 @@ class getid3_matroska extends getid3_handler
case EBML_ID_TARGETTYPEVALUE: case EBML_ID_TARGETTYPEVALUE:
$targets_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); $targets_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
$targets_entry[strtolower($sub_sub_subelement['id_name']).'_long'] = self::MatroskaTargetTypeValue($targets_entry[$sub_sub_subelement['id_name']]); $targets_entry[strtolower($sub_sub_subelement['id_name']).'_long'] = self::TargetTypeValue($targets_entry[$sub_sub_subelement['id_name']]);
break; break;
case EBML_ID_TARGETTYPE: case EBML_ID_TARGETTYPE:
$targets_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data']; $targets_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
break; break;
case EBML_ID_TAGTRACKUID: case EBML_ID_TAGTRACKUID:
case EBML_ID_TAGEDITIONUID: case EBML_ID_TAGEDITIONUID:
case EBML_ID_TAGCHAPTERUID: case EBML_ID_TAGCHAPTERUID:
case EBML_ID_TAGATTACHMENTUID: case EBML_ID_TAGATTACHMENTUID:
@ -926,17 +971,11 @@ class getid3_matroska extends getid3_handler
$attachedfile_entry['data_offset'] = $this->current_offset; $attachedfile_entry['data_offset'] = $this->current_offset;
$attachedfile_entry['data_length'] = $sub_subelement['length']; $attachedfile_entry['data_length'] = $sub_subelement['length'];
$this->getid3->saveAttachment( $attachedfile_entry[$sub_subelement['id_name']] = $this->saveAttachment(
$attachedfile_entry[$sub_subelement['id_name']],
$attachedfile_entry['FileName'], $attachedfile_entry['FileName'],
$attachedfile_entry['data_offset'], $attachedfile_entry['data_offset'],
$attachedfile_entry['data_length']); $attachedfile_entry['data_length']);
if (@$attachedfile_entry[$sub_subelement['id_name']] && is_file($attachedfile_entry[$sub_subelement['id_name']])) {
$attachedfile_entry[$sub_subelement['id_name'].'_filename'] = $attachedfile_entry[$sub_subelement['id_name']];
unset($attachedfile_entry[$sub_subelement['id_name']]);
}
$this->current_offset = $sub_subelement['end']; $this->current_offset = $sub_subelement['end'];
break; break;
@ -948,19 +987,7 @@ class getid3_matroska extends getid3_handler
$this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement); $this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement);
} }
} }
if (!empty($attachedfile_entry['FileData']) && !empty($attachedfile_entry['FileMimeType']) && preg_match('#^image/#i', $attachedfile_entry['FileMimeType'])) { $info['matroska']['attachments'][] = $attachedfile_entry;
if ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
$attachedfile_entry['data'] = $attachedfile_entry['FileData'];
$attachedfile_entry['image_mime'] = $attachedfile_entry['FileMimeType'];
$info['matroska']['comments']['picture'][] = array('data' => $attachedfile_entry['data'], 'image_mime' => $attachedfile_entry['image_mime'], 'filename' => (!empty($attachedfile_entry['FileName']) ? $attachedfile_entry['FileName'] : ''));
unset($attachedfile_entry['FileData'], $attachedfile_entry['FileMimeType']);
}
}
if (!empty($attachedfile_entry['image_mime']) && preg_match('#^image/#i', $attachedfile_entry['image_mime'])) {
// don't add a second copy of attached images, which are grouped under the standard location [comments][picture]
} else {
$info['matroska']['attachments'][] = $attachedfile_entry;
}
break; break;
default: default:
@ -1115,7 +1142,7 @@ class getid3_matroska extends getid3_handler
case EBML_ID_CLUSTERREFERENCEBLOCK: // signed-int case EBML_ID_CLUSTERREFERENCEBLOCK: // signed-int
$cluster_block_group[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data'], false, true); $cluster_block_group[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data'], false, true);
break; break;
case EBML_ID_CLUSTERCODECSTATE: case EBML_ID_CLUSTERCODECSTATE:
$cluster_block_group[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']); $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
break; break;
@ -1144,7 +1171,9 @@ class getid3_matroska extends getid3_handler
if (!self::$parse_whole_file) { if (!self::$parse_whole_file) {
if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) { if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
return; if (count($info['matroska']['track_data_offsets']) == count($info['matroska']['tracks']['tracks'])) {
return;
}
} }
} }
} }
@ -1160,38 +1189,35 @@ class getid3_matroska extends getid3_handler
$this->unhandledElement('root', __LINE__, $top_element); $this->unhandledElement('root', __LINE__, $top_element);
} }
} }
} }
private function EnsureBufferHasEnoughData($min_data = 1024) private function EnsureBufferHasEnoughData($min_data=1024) {
{
if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) { if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) {
$read_bytes = max($min_data, $this->getid3->fread_buffer_size());
if (!getid3_lib::intValueSupported($this->current_offset + $this->getid3->fread_buffer_size())) { try {
$this->getid3->info['error'][] = 'EBML parser: cannot read past '.$this->current_offset; $this->fseek($this->current_offset);
$this->EBMLbuffer_offset = $this->current_offset;
$this->EBMLbuffer = $this->fread($read_bytes);
$this->EBMLbuffer_length = strlen($this->EBMLbuffer);
} catch (getid3_exception $e) {
$this->warning('EBML parser: '.$e->getMessage());
return false; return false;
} }
fseek($this->getid3->fp, $this->current_offset, SEEK_SET); if ($this->EBMLbuffer_length == 0 && $this->feof()) {
$this->EBMLbuffer_offset = $this->current_offset; return $this->error('EBML parser: ran out of file at offset '.$this->current_offset);
$this->EBMLbuffer = fread($this->getid3->fp, max($min_data, $this->getid3->fread_buffer_size()));
$this->EBMLbuffer_length = strlen($this->EBMLbuffer);
if ($this->EBMLbuffer_length == 0 && feof($this->getid3->fp)) {
$this->getid3->info['error'][] = 'EBML parser: ran out of file at offset '.$this->current_offset;
return false;
} }
} }
return true; return true;
} }
private function readEBMLint() private function readEBMLint() {
{
$actual_offset = $this->current_offset - $this->EBMLbuffer_offset; $actual_offset = $this->current_offset - $this->EBMLbuffer_offset;
// get length of integer // get length of integer
$first_byte_int = ord($this->EBMLbuffer[$actual_offset]); $first_byte_int = ord($this->EBMLbuffer[$actual_offset]);
if (0x80 & $first_byte_int) { if (0x80 & $first_byte_int) {
$length = 1; $length = 1;
} elseif (0x40 & $first_byte_int) { } elseif (0x40 & $first_byte_int) {
$length = 2; $length = 2;
@ -1218,16 +1244,16 @@ class getid3_matroska extends getid3_handler
return $int_value; return $int_value;
} }
private function readEBMLelementData($length) private function readEBMLelementData($length, $check_buffer=false) {
{ if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) {
return false;
}
$data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length); $data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length);
$this->current_offset += $length; $this->current_offset += $length;
return $data; return $data;
} }
private function getEBMLelement(&$element, $parent_end, $get_data = false) private function getEBMLelement(&$element, $parent_end, $get_data=false) {
{
if ($this->current_offset >= $parent_end) { if ($this->current_offset >= $parent_end) {
return false; return false;
} }
@ -1263,11 +1289,10 @@ class getid3_matroska extends getid3_handler
return true; return true;
} }
private function unhandledElement($type, $line, $element) private function unhandledElement($type, $line, $element) {
{
// warn only about unknown and missed elements, not about unuseful // warn only about unknown and missed elements, not about unuseful
if (!in_array($element['id'], $this->unuseful_elements)) { if (!in_array($element['id'], $this->unuseful_elements)) {
$this->getid3->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']); $this->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']);
} }
// increase offset for unparsed elements // increase offset for unparsed elements
@ -1276,8 +1301,7 @@ class getid3_matroska extends getid3_handler
} }
} }
private function ExtractCommentsSimpleTag($SimpleTagArray) private function ExtractCommentsSimpleTag($SimpleTagArray) {
{
if (!empty($SimpleTagArray['SimpleTag'])) { if (!empty($SimpleTagArray['SimpleTag'])) {
foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) { foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) {
if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) { if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) {
@ -1292,8 +1316,7 @@ class getid3_matroska extends getid3_handler
return true; return true;
} }
private function HandleEMBLSimpleTag($parent_end) private function HandleEMBLSimpleTag($parent_end) {
{
$simpletag_entry = array(); $simpletag_entry = array();
while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) { while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) {
@ -1322,58 +1345,67 @@ class getid3_matroska extends getid3_handler
return $simpletag_entry; return $simpletag_entry;
} }
private function HandleEMBLClusterBlock($element, $block_type, &$info) private function HandleEMBLClusterBlock($element, $block_type, &$info) {
{
// http://www.matroska.org/technical/specs/index.html#block_structure // http://www.matroska.org/technical/specs/index.html#block_structure
// http://www.matroska.org/technical/specs/index.html#simpleblock_structure // http://www.matroska.org/technical/specs/index.html#simpleblock_structure
$cluster_block_data = array(); $block_data = array();
$cluster_block_data['tracknumber'] = $this->readEBMLint(); $block_data['tracknumber'] = $this->readEBMLint();
$cluster_block_data['timecode'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(2)); $block_data['timecode'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(2), false, true);
$cluster_block_data['flags_raw'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)); $block_data['flags_raw'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) { if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
$cluster_block_data['flags']['keyframe'] = (($cluster_block_data['flags_raw'] & 0x80) >> 7); $block_data['flags']['keyframe'] = (($block_data['flags_raw'] & 0x80) >> 7);
//$cluster_block_data['flags']['reserved1'] = (($cluster_block_data['flags_raw'] & 0x70) >> 4); //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0x70) >> 4);
} }
else { else {
//$cluster_block_data['flags']['reserved1'] = (($cluster_block_data['flags_raw'] & 0xF0) >> 4); //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0xF0) >> 4);
} }
$cluster_block_data['flags']['invisible'] = (bool)(($cluster_block_data['flags_raw'] & 0x08) >> 3); $block_data['flags']['invisible'] = (bool)(($block_data['flags_raw'] & 0x08) >> 3);
$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 $block_data['flags']['lacing'] = (($block_data['flags_raw'] & 0x06) >> 1); // 00=no lacing; 01=Xiph lacing; 11=EBML lacing; 10=fixed-size lacing
if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) { if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
$cluster_block_data['flags']['discardable'] = (($cluster_block_data['flags_raw'] & 0x01)); $block_data['flags']['discardable'] = (($block_data['flags_raw'] & 0x01));
} }
else { else {
//$cluster_block_data['flags']['reserved2'] = (($cluster_block_data['flags_raw'] & 0x01) >> 0); //$block_data['flags']['reserved2'] = (($block_data['flags_raw'] & 0x01) >> 0);
} }
$cluster_block_data['flags']['lacing_type'] = self::MatroskaBlockLacingType($cluster_block_data['flags']['lacing']); $block_data['flags']['lacing_type'] = self::BlockLacingType($block_data['flags']['lacing']);
// Lace (when lacing bit is set) // Lace (when lacing bit is set)
if ($cluster_block_data['flags']['lacing'] > 0) { if ($block_data['flags']['lacing'] > 0) {
$cluster_block_data['lace_frames'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)) + 1; // Number of frames in the lace-1 (uint8) $block_data['lace_frames'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)) + 1; // Number of frames in the lace-1 (uint8)
if ($cluster_block_data['flags']['lacing'] != 0x02) { // 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). if ($block_data['flags']['lacing'] != 0x02) {
for ($i = 1; $i < $cluster_block_data['lace_frames']; $i ++) { for ($i = 1; $i < $block_data['lace_frames']; $i ++) { // Lace-coded size of each frame of the lace, except for the last one (multiple uint8). *This is not used with Fixed-size lacing as it is calculated automatically from (total size of lace) / (number of frames in lace).
if ($cluster_block_data['flags']['lacing'] == 0x03) { // EBML lacing if ($block_data['flags']['lacing'] == 0x03) { // EBML lacing
// TODO: read size correctly, calc size for the last frame. For now offsets are deteminded OK with readEBMLint() and that's the most important thing. $block_data['lace_frames_size'][$i] = $this->readEBMLint(); // TODO: read size correctly, calc size for the last frame. For now offsets are deteminded OK with readEBMLint() and that's the most important thing.
$cluster_block_data['lace_frames_size'][$i] = $this->readEBMLint();
} }
else { // Xiph lacing else { // Xiph lacing
$cluster_block_data['lace_frames_size'][$i] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)); $block_data['lace_frames_size'][$i] = 0;
do {
$size = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
$block_data['lace_frames_size'][$i] += $size;
}
while ($size == 255);
} }
} }
if ($block_data['flags']['lacing'] == 0x01) { // calc size of the last frame only for Xiph lacing, till EBML sizes are now anyway determined incorrectly
$block_data['lace_frames_size'][] = $element['end'] - $this->current_offset - array_sum($block_data['lace_frames_size']);
}
} }
} }
if (!isset($info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']])) { if (!isset($info['matroska']['track_data_offsets'][$block_data['tracknumber']])) {
$info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']]['offset'] = $this->current_offset; $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['offset'] = $this->current_offset;
$info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']]['length'] = $element['end'] - $this->current_offset; $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'] = $element['end'] - $this->current_offset;
//$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] = 0;
} }
//$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] += $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'];
//$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['duration'] = $block_data['timecode'] * ((isset($info['matroska']['info'][0]['TimecodeScale']) ? $info['matroska']['info'][0]['TimecodeScale'] : 1000000) / 1000000000);
// set offset manually // set offset manually
$this->current_offset = $element['end']; $this->current_offset = $element['end'];
return $cluster_block_data; return $block_data;
} }
private static function EBML2Int($EBMLstring) { private static function EBML2Int($EBMLstring) {
@ -1424,66 +1456,66 @@ class getid3_matroska extends getid3_handler
return round(($EBMLdatestamp / 1000000000) + 978307200); return round(($EBMLdatestamp / 1000000000) + 978307200);
} }
public static function MatroskaTargetTypeValue($target_type) { public static function TargetTypeValue($target_type) {
// http://www.matroska.org/technical/specs/tagging/index.html // http://www.matroska.org/technical/specs/tagging/index.html
static $MatroskaTargetTypeValue = array(); static $TargetTypeValue = array();
if (empty($MatroskaTargetTypeValue)) { if (empty($TargetTypeValue)) {
$MatroskaTargetTypeValue[10] = 'A: ~ V:shot'; // the lowest hierarchy found in music or movies $TargetTypeValue[10] = 'A: ~ V:shot'; // the lowest hierarchy found in music or movies
$MatroskaTargetTypeValue[20] = 'A:subtrack/part/movement ~ V:scene'; // corresponds to parts of a track for audio (like a movement) $TargetTypeValue[20] = 'A:subtrack/part/movement ~ V:scene'; // corresponds to parts of a track for audio (like a movement)
$MatroskaTargetTypeValue[30] = 'A:track/song ~ V:chapter'; // the common parts of an album or a movie $TargetTypeValue[30] = 'A:track/song ~ V:chapter'; // the common parts of an album or a movie
$MatroskaTargetTypeValue[40] = 'A:part/session ~ V:part/session'; // when an album or episode has different logical parts $TargetTypeValue[40] = 'A:part/session ~ V:part/session'; // when an album or episode has different logical parts
$MatroskaTargetTypeValue[50] = 'A:album/opera/concert ~ V:movie/episode/concert'; // the most common grouping level of music and video (equals to an episode for TV series) $TargetTypeValue[50] = 'A:album/opera/concert ~ V:movie/episode/concert'; // the most common grouping level of music and video (equals to an episode for TV series)
$MatroskaTargetTypeValue[60] = 'A:edition/issue/volume/opus ~ V:season/sequel/volume'; // a list of lower levels grouped together $TargetTypeValue[60] = 'A:edition/issue/volume/opus ~ V:season/sequel/volume'; // a list of lower levels grouped together
$MatroskaTargetTypeValue[70] = 'A:collection ~ V:collection'; // the high hierarchy consisting of many different lower items $TargetTypeValue[70] = 'A:collection ~ V:collection'; // the high hierarchy consisting of many different lower items
} }
return (isset($MatroskaTargetTypeValue[$target_type]) ? $MatroskaTargetTypeValue[$target_type] : $target_type); return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type);
} }
public static function MatroskaBlockLacingType($lacingtype) { public static function BlockLacingType($lacingtype) {
// http://matroska.org/technical/specs/index.html#block_structure // http://matroska.org/technical/specs/index.html#block_structure
static $MatroskaBlockLacingType = array(); static $BlockLacingType = array();
if (empty($MatroskaBlockLacingType)) { if (empty($BlockLacingType)) {
$MatroskaBlockLacingType[0x00] = 'no lacing'; $BlockLacingType[0x00] = 'no lacing';
$MatroskaBlockLacingType[0x01] = 'Xiph lacing'; $BlockLacingType[0x01] = 'Xiph lacing';
$MatroskaBlockLacingType[0x02] = 'fixed-size lacing'; $BlockLacingType[0x02] = 'fixed-size lacing';
$MatroskaBlockLacingType[0x03] = 'EBML lacing'; $BlockLacingType[0x03] = 'EBML lacing';
} }
return (isset($MatroskaBlockLacingType[$lacingtype]) ? $MatroskaBlockLacingType[$lacingtype] : $lacingtype); return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype);
} }
public static function MatroskaCodecIDtoCommonName($codecid) { public static function CodecIDtoCommonName($codecid) {
// http://www.matroska.org/technical/specs/codecid/index.html // http://www.matroska.org/technical/specs/codecid/index.html
static $MatroskaCodecIDlist = array(); static $CodecIDlist = array();
if (empty($MatroskaCodecIDlist)) { if (empty($CodecIDlist)) {
$MatroskaCodecIDlist['A_AAC'] = 'aac'; $CodecIDlist['A_AAC'] = 'aac';
$MatroskaCodecIDlist['A_AAC/MPEG2/LC'] = 'aac'; $CodecIDlist['A_AAC/MPEG2/LC'] = 'aac';
$MatroskaCodecIDlist['A_AC3'] = 'ac3'; $CodecIDlist['A_AC3'] = 'ac3';
$MatroskaCodecIDlist['A_DTS'] = 'dts'; $CodecIDlist['A_DTS'] = 'dts';
$MatroskaCodecIDlist['A_FLAC'] = 'flac'; $CodecIDlist['A_FLAC'] = 'flac';
$MatroskaCodecIDlist['A_MPEG/L1'] = 'mp1'; $CodecIDlist['A_MPEG/L1'] = 'mp1';
$MatroskaCodecIDlist['A_MPEG/L2'] = 'mp2'; $CodecIDlist['A_MPEG/L2'] = 'mp2';
$MatroskaCodecIDlist['A_MPEG/L3'] = 'mp3'; $CodecIDlist['A_MPEG/L3'] = 'mp3';
$MatroskaCodecIDlist['A_PCM/INT/LIT'] = 'pcm'; // PCM Integer Little Endian $CodecIDlist['A_PCM/INT/LIT'] = 'pcm'; // PCM Integer Little Endian
$MatroskaCodecIDlist['A_PCM/INT/BIG'] = 'pcm'; // PCM Integer Big Endian $CodecIDlist['A_PCM/INT/BIG'] = 'pcm'; // PCM Integer Big Endian
$MatroskaCodecIDlist['A_QUICKTIME/QDMC'] = 'quicktime'; // Quicktime: QDesign Music $CodecIDlist['A_QUICKTIME/QDMC'] = 'quicktime'; // Quicktime: QDesign Music
$MatroskaCodecIDlist['A_QUICKTIME/QDM2'] = 'quicktime'; // Quicktime: QDesign Music v2 $CodecIDlist['A_QUICKTIME/QDM2'] = 'quicktime'; // Quicktime: QDesign Music v2
$MatroskaCodecIDlist['A_VORBIS'] = 'vorbis'; $CodecIDlist['A_VORBIS'] = 'vorbis';
$MatroskaCodecIDlist['V_MPEG1'] = 'mpeg'; $CodecIDlist['V_MPEG1'] = 'mpeg';
$MatroskaCodecIDlist['V_THEORA'] = 'theora'; $CodecIDlist['V_THEORA'] = 'theora';
$MatroskaCodecIDlist['V_REAL/RV40'] = 'real'; $CodecIDlist['V_REAL/RV40'] = 'real';
$MatroskaCodecIDlist['V_REAL/RV10'] = 'real'; $CodecIDlist['V_REAL/RV10'] = 'real';
$MatroskaCodecIDlist['V_REAL/RV20'] = 'real'; $CodecIDlist['V_REAL/RV20'] = 'real';
$MatroskaCodecIDlist['V_REAL/RV30'] = 'real'; $CodecIDlist['V_REAL/RV30'] = 'real';
$MatroskaCodecIDlist['V_QUICKTIME'] = 'quicktime'; // Quicktime $CodecIDlist['V_QUICKTIME'] = 'quicktime'; // Quicktime
$MatroskaCodecIDlist['V_MPEG4/ISO/AP'] = 'mpeg4'; $CodecIDlist['V_MPEG4/ISO/AP'] = 'mpeg4';
$MatroskaCodecIDlist['V_MPEG4/ISO/ASP'] = 'mpeg4'; $CodecIDlist['V_MPEG4/ISO/ASP'] = 'mpeg4';
$MatroskaCodecIDlist['V_MPEG4/ISO/AVC'] = 'h264'; $CodecIDlist['V_MPEG4/ISO/AVC'] = 'h264';
$MatroskaCodecIDlist['V_MPEG4/ISO/SP'] = 'mpeg4'; $CodecIDlist['V_MPEG4/ISO/SP'] = 'mpeg4';
$MatroskaCodecIDlist['V_VP8'] = 'vp8'; $CodecIDlist['V_VP8'] = 'vp8';
$MatroskaCodecIDlist['V_MS/VFW/FOURCC'] = 'riff'; $CodecIDlist['V_MS/VFW/FOURCC'] = 'vcm'; // Microsoft (TM) Video Codec Manager (VCM)
$MatroskaCodecIDlist['A_MS/ACM'] = 'riff'; $CodecIDlist['A_MS/ACM'] = 'acm'; // Microsoft (TM) Audio Codec Manager (ACM)
} }
return (isset($MatroskaCodecIDlist[$codecid]) ? $MatroskaCodecIDlist[$codecid] : $codecid); return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid);
} }
private static function EBMLidName($value) { private static function EBMLidName($value) {
@ -1647,6 +1679,7 @@ class getid3_matroska extends getid3_handler
$EBMLidList[EBML_ID_SIMPLETAG] = 'SimpleTag'; $EBMLidList[EBML_ID_SIMPLETAG] = 'SimpleTag';
$EBMLidList[EBML_ID_CLUSTERSLICES] = 'ClusterSlices'; $EBMLidList[EBML_ID_CLUSTERSLICES] = 'ClusterSlices';
$EBMLidList[EBML_ID_STEREOMODE] = 'StereoMode'; $EBMLidList[EBML_ID_STEREOMODE] = 'StereoMode';
$EBMLidList[EBML_ID_OLDSTEREOMODE] = 'OldStereoMode';
$EBMLidList[EBML_ID_TAG] = 'Tag'; $EBMLidList[EBML_ID_TAG] = 'Tag';
$EBMLidList[EBML_ID_TAGATTACHMENTUID] = 'TagAttachmentUID'; $EBMLidList[EBML_ID_TAGATTACHMENTUID] = 'TagAttachmentUID';
$EBMLidList[EBML_ID_TAGBINARY] = 'TagBinary'; $EBMLidList[EBML_ID_TAGBINARY] = 'TagBinary';
@ -1682,7 +1715,18 @@ class getid3_matroska extends getid3_handler
return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value)); return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
} }
public static function displayUnit($value) {
// http://www.matroska.org/technical/specs/index.html#DisplayUnit
static $units = array(
0 => 'pixels',
1 => 'centimeters',
2 => 'inches',
3 => 'Display Aspect Ratio');
return (isset($units[$value]) ? $units[$value] : 'unknown');
}
private static function getDefaultStreamInfo($streams) private static function getDefaultStreamInfo($streams)
{ {
foreach (array_reverse($streams) as $stream) { foreach (array_reverse($streams) as $stream) {
@ -1690,17 +1734,18 @@ class getid3_matroska extends getid3_handler
break; break;
} }
} }
unset($stream['default']);
if (isset($stream['name'])) { $unset = array('default', 'name');
unset($stream['name']); foreach ($unset as $u) {
if (isset($stream[$u])) {
unset($stream[$u]);
}
} }
$info = $stream; $info = $stream;
$info['streams'] = $streams; $info['streams'] = $streams;
return $info; return $info;
} }
} }
?>

View file

@ -0,0 +1,606 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio-video.mpeg.php //
// module for analyzing MPEG files //
// dependencies: module.audio.mp3.php //
// ///
/////////////////////////////////////////////////////////////////
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
class getid3_mpeg extends getid3_handler {
const START_CODE_BASE = "\x00\x00\x01";
const VIDEO_PICTURE_START = "\x00\x00\x01\x00";
const VIDEO_USER_DATA_START = "\x00\x00\x01\xB2";
const VIDEO_SEQUENCE_HEADER = "\x00\x00\x01\xB3";
const VIDEO_SEQUENCE_ERROR = "\x00\x00\x01\xB4";
const VIDEO_EXTENSION_START = "\x00\x00\x01\xB5";
const VIDEO_SEQUENCE_END = "\x00\x00\x01\xB7";
const VIDEO_GROUP_START = "\x00\x00\x01\xB8";
const AUDIO_START = "\x00\x00\x01\xC0";
public function Analyze() {
$info = &$this->getid3->info;
$info['fileformat'] = 'mpeg';
$this->fseek($info['avdataoffset']);
$MPEGstreamData = $this->fread($this->getid3->option_fread_buffer_size);
$MPEGstreamBaseOffset = 0; // how far are we from the beginning of the file data ($info['avdataoffset'])
$MPEGstreamDataOffset = 0; // how far are we from the beginning of the buffer data (~32kB)
$StartCodeValue = false;
$prevStartCodeValue = false;
$GOPcounter = -1;
$FramesByGOP = array();
$ParsedAVchannels = array();
do {
//echo $MPEGstreamDataOffset.' vs '.(strlen($MPEGstreamData) - 1024).'<Br>';
if ($MPEGstreamDataOffset > (strlen($MPEGstreamData) - 16384)) {
// buffer running low, get more data
//echo 'reading more data<br>';
$MPEGstreamData .= $this->fread($this->getid3->option_fread_buffer_size);
if (strlen($MPEGstreamData) > $this->getid3->option_fread_buffer_size) {
$MPEGstreamData = substr($MPEGstreamData, $MPEGstreamDataOffset);
$MPEGstreamBaseOffset += $MPEGstreamDataOffset;
$MPEGstreamDataOffset = 0;
}
}
if (($StartCodeOffset = strpos($MPEGstreamData, self::START_CODE_BASE, $MPEGstreamDataOffset)) === false) {
//echo 'no more start codes found.<br>';
break;
} else {
$MPEGstreamDataOffset = $StartCodeOffset;
$prevStartCodeValue = $StartCodeValue;
$StartCodeValue = ord(substr($MPEGstreamData, $StartCodeOffset + 3, 1));
//echo 'Found "'.strtoupper(dechex($StartCodeValue)).'" at offset '.($MPEGstreamBaseOffset + $StartCodeOffset).' ($MPEGstreamDataOffset = '.$MPEGstreamDataOffset.')<br>';
}
$MPEGstreamDataOffset += 4;
switch ($StartCodeValue) {
case 0x00: // picture_start_code
if (!empty($info['mpeg']['video']['bitrate_mode']) && ($info['mpeg']['video']['bitrate_mode'] == 'vbr')) {
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4));
$bitstreamoffset = 0;
$PictureHeader = array();
$PictureHeader['temporal_reference'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 10); // 10-bit unsigned integer associated with each input picture. It is incremented by one, modulo 1024, for each input frame. When a frame is coded as two fields the temporal reference in the picture header of both fields is the same. Following a group start header the temporal reference of the earliest picture (in display order) shall be reset to zero.
$PictureHeader['picture_coding_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_coding_type
$PictureHeader['vbv_delay'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 16); // 16 bits for vbv_delay
//... etc
$FramesByGOP[$GOPcounter][] = $PictureHeader;
}
break;
case 0xB3: // sequence_header_code
/*
Note: purposely doing the less-pretty (and probably a bit slower) method of using string of bits rather than bitwise operations.
Mostly because PHP 32-bit doesn't handle unsigned integers well for bitwise operation.
Also the MPEG stream is designed as a bitstream and often doesn't align nicely with byte boundaries.
*/
$info['video']['codec'] = 'MPEG-1'; // will be updated if extension_start_code found
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8));
$bitstreamoffset = 0;
$info['mpeg']['video']['raw']['horizontal_size_value'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for horizontal frame size. Note: horizontal_size_extension, if present, will add 2 most-significant bits to this value
$info['mpeg']['video']['raw']['vertical_size_value'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for vertical frame size. Note: vertical_size_extension, if present, will add 2 most-significant bits to this value
$info['mpeg']['video']['raw']['aspect_ratio_information'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for aspect_ratio_information
$info['mpeg']['video']['raw']['frame_rate_code'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for Frame Rate id code
$info['mpeg']['video']['raw']['bitrate'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 18); // 18 bits for bit_rate_value (18 set bits = VBR, otherwise bitrate = this value * 400)
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
$info['mpeg']['video']['raw']['vbv_buffer_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 10); // 10 bits vbv_buffer_size_value
$info['mpeg']['video']['raw']['constrained_param_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: constrained_param_flag
$info['mpeg']['video']['raw']['load_intra_quantiser_matrix'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: load_intra_quantiser_matrix
if ($info['mpeg']['video']['raw']['load_intra_quantiser_matrix']) {
$bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 12, 64));
for ($i = 0; $i < 64; $i++) {
$info['mpeg']['video']['raw']['intra_quantiser_matrix'][$i] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
}
}
$info['mpeg']['video']['raw']['load_non_intra_quantiser_matrix'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
if ($info['mpeg']['video']['raw']['load_non_intra_quantiser_matrix']) {
$bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 12 + ($info['mpeg']['video']['raw']['load_intra_quantiser_matrix'] ? 64 : 0), 64));
for ($i = 0; $i < 64; $i++) {
$info['mpeg']['video']['raw']['non_intra_quantiser_matrix'][$i] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
}
}
$info['mpeg']['video']['pixel_aspect_ratio'] = self::videoAspectRatioLookup($info['mpeg']['video']['raw']['aspect_ratio_information']);
$info['mpeg']['video']['pixel_aspect_ratio_text'] = self::videoAspectRatioTextLookup($info['mpeg']['video']['raw']['aspect_ratio_information']);
$info['mpeg']['video']['frame_rate'] = self::videoFramerateLookup($info['mpeg']['video']['raw']['frame_rate_code']);
if ($info['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits = VBR
//$this->warning('This version of getID3() ['.$this->getid3->version().'] cannot determine average bitrate of VBR MPEG video files');
$info['mpeg']['video']['bitrate_mode'] = 'vbr';
} else {
$info['mpeg']['video']['bitrate'] = $info['mpeg']['video']['raw']['bitrate'] * 400;
$info['mpeg']['video']['bitrate_mode'] = 'cbr';
$info['video']['bitrate'] = $info['mpeg']['video']['bitrate'];
}
$info['video']['resolution_x'] = $info['mpeg']['video']['raw']['horizontal_size_value'];
$info['video']['resolution_y'] = $info['mpeg']['video']['raw']['vertical_size_value'];
$info['video']['frame_rate'] = $info['mpeg']['video']['frame_rate'];
$info['video']['bitrate_mode'] = $info['mpeg']['video']['bitrate_mode'];
$info['video']['pixel_aspect_ratio'] = $info['mpeg']['video']['pixel_aspect_ratio'];
$info['video']['lossless'] = false;
$info['video']['bits_per_sample'] = 24;
break;
case 0xB5: // extension_start_code
$info['video']['codec'] = 'MPEG-2';
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8)); // 48 bits for Sequence Extension ID; 61 bits for Sequence Display Extension ID; 59 bits for Sequence Scalable Extension ID
$bitstreamoffset = 0;
$info['mpeg']['video']['raw']['extension_start_code_identifier'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for extension_start_code_identifier
//echo $info['mpeg']['video']['raw']['extension_start_code_identifier'].'<br>';
switch ($info['mpeg']['video']['raw']['extension_start_code_identifier']) {
case 1: // 0001 Sequence Extension ID
$info['mpeg']['video']['raw']['profile_and_level_indication'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for profile_and_level_indication
$info['mpeg']['video']['raw']['progressive_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: progressive_sequence
$info['mpeg']['video']['raw']['chroma_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for chroma_format
$info['mpeg']['video']['raw']['horizontal_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for horizontal_size_extension
$info['mpeg']['video']['raw']['vertical_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for vertical_size_extension
$info['mpeg']['video']['raw']['bit_rate_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12); // 12 bits for bit_rate_extension
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
$info['mpeg']['video']['raw']['vbv_buffer_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for vbv_buffer_size_extension
$info['mpeg']['video']['raw']['low_delay'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: low_delay
$info['mpeg']['video']['raw']['frame_rate_extension_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for frame_rate_extension_n
$info['mpeg']['video']['raw']['frame_rate_extension_d'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for frame_rate_extension_d
$info['video']['resolution_x'] = ($info['mpeg']['video']['raw']['horizontal_size_extension'] << 12) | $info['mpeg']['video']['raw']['horizontal_size_value'];
$info['video']['resolution_y'] = ($info['mpeg']['video']['raw']['vertical_size_extension'] << 12) | $info['mpeg']['video']['raw']['vertical_size_value'];
$info['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence'];
$info['mpeg']['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence'];
$info['mpeg']['video']['chroma_format'] = self::chromaFormatTextLookup($info['mpeg']['video']['raw']['chroma_format']);
break;
case 2: // 0010 Sequence Display Extension ID
$info['mpeg']['video']['raw']['video_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for video_format
$info['mpeg']['video']['raw']['colour_description'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: colour_description
if ($info['mpeg']['video']['raw']['colour_description']) {
$info['mpeg']['video']['raw']['colour_primaries'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for colour_primaries
$info['mpeg']['video']['raw']['transfer_characteristics'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for transfer_characteristics
$info['mpeg']['video']['raw']['matrix_coefficients'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for matrix_coefficients
}
$info['mpeg']['video']['raw']['display_horizontal_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for display_horizontal_size
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
$info['mpeg']['video']['raw']['display_vertical_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for display_vertical_size
$info['mpeg']['video']['video_format'] = self::videoFormatTextLookup($info['mpeg']['video']['raw']['video_format']);
break;
case 3: // 0011 Quant Matrix Extension ID
break;
case 5: // 0101 Sequence Scalable Extension ID
$info['mpeg']['video']['raw']['scalable_mode'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for scalable_mode
$info['mpeg']['video']['raw']['layer_id'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for layer_id
if ($info['mpeg']['video']['raw']['scalable_mode'] == 1) { // "spatial scalability"
$info['mpeg']['video']['raw']['lower_layer_prediction_horizontal_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for lower_layer_prediction_horizontal_size
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
$info['mpeg']['video']['raw']['lower_layer_prediction_vertical_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14); // 14 bits for lower_layer_prediction_vertical_size
$info['mpeg']['video']['raw']['horizontal_subsampling_factor_m'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for horizontal_subsampling_factor_m
$info['mpeg']['video']['raw']['horizontal_subsampling_factor_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for horizontal_subsampling_factor_n
$info['mpeg']['video']['raw']['vertical_subsampling_factor_m'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for vertical_subsampling_factor_m
$info['mpeg']['video']['raw']['vertical_subsampling_factor_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for vertical_subsampling_factor_n
} elseif ($info['mpeg']['video']['raw']['scalable_mode'] == 3) { // "temporal scalability"
$info['mpeg']['video']['raw']['picture_mux_enable'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: picture_mux_enable
if ($info['mpeg']['video']['raw']['picture_mux_enable']) {
$info['mpeg']['video']['raw']['mux_to_progressive_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: mux_to_progressive_sequence
}
$info['mpeg']['video']['raw']['picture_mux_order'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_mux_order
$info['mpeg']['video']['raw']['picture_mux_factor'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for picture_mux_factor
}
$info['mpeg']['video']['scalable_mode'] = self::scalableModeTextLookup($info['mpeg']['video']['raw']['scalable_mode']);
break;
case 7: // 0111 Picture Display Extension ID
break;
case 8: // 1000 Picture Coding Extension ID
$info['mpeg']['video']['raw']['f_code_00'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[0][0] (forward horizontal)
$info['mpeg']['video']['raw']['f_code_01'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[0][1] (forward vertical)
$info['mpeg']['video']['raw']['f_code_10'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[1][0] (backward horizontal)
$info['mpeg']['video']['raw']['f_code_11'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4); // 4 bits for f_code[1][1] (backward vertical)
$info['mpeg']['video']['raw']['intra_dc_precision'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for intra_dc_precision
$info['mpeg']['video']['raw']['picture_structure'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for picture_structure
$info['mpeg']['video']['raw']['top_field_first'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: top_field_first
$info['mpeg']['video']['raw']['frame_pred_frame_dct'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: frame_pred_frame_dct
$info['mpeg']['video']['raw']['concealment_motion_vectors'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: concealment_motion_vectors
$info['mpeg']['video']['raw']['q_scale_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: q_scale_type
$info['mpeg']['video']['raw']['intra_vlc_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: intra_vlc_format
$info['mpeg']['video']['raw']['alternate_scan'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: alternate_scan
$info['mpeg']['video']['raw']['repeat_first_field'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: repeat_first_field
$info['mpeg']['video']['raw']['chroma_420_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: chroma_420_type
$info['mpeg']['video']['raw']['progressive_frame'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: progressive_frame
$info['mpeg']['video']['raw']['composite_display_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: composite_display_flag
if ($info['mpeg']['video']['raw']['composite_display_flag']) {
$info['mpeg']['video']['raw']['v_axis'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: v_axis
$info['mpeg']['video']['raw']['field_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3); // 3 bits for field_sequence
$info['mpeg']['video']['raw']['sub_carrier'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: sub_carrier
$info['mpeg']['video']['raw']['burst_amplitude'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 7); // 7 bits for burst_amplitude
$info['mpeg']['video']['raw']['sub_carrier_phase'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 8 bits for sub_carrier_phase
}
$info['mpeg']['video']['intra_dc_precision_bits'] = $info['mpeg']['video']['raw']['intra_dc_precision'] + 8;
$info['mpeg']['video']['picture_structure'] = self::pictureStructureTextLookup($info['mpeg']['video']['raw']['picture_structure']);
break;
case 9: // 1001 Picture Spatial Scalable Extension ID
break;
case 10: // 1010 Picture Temporal Scalable Extension ID
break;
default:
$this->warning('Unexpected $info[mpeg][video][raw][extension_start_code_identifier] value of '.$info['mpeg']['video']['raw']['extension_start_code_identifier']);
break;
}
break;
case 0xB8: // group_of_pictures_header
$GOPcounter++;
if ($info['mpeg']['video']['bitrate_mode'] == 'vbr') {
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4)); // 27 bits needed for group_of_pictures_header
$bitstreamoffset = 0;
$GOPheader = array();
$GOPheader['byte_offset'] = $MPEGstreamBaseOffset + $StartCodeOffset;
$GOPheader['drop_frame_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: drop_frame_flag
$GOPheader['time_code_hours'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5); // 5 bits for time_code_hours
$GOPheader['time_code_minutes'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_minutes
$marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
$GOPheader['time_code_seconds'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_seconds
$GOPheader['time_code_pictures'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6); // 6 bits for time_code_pictures
$GOPheader['closed_gop'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: closed_gop
$GOPheader['broken_link'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: broken_link
$time_code_separator = ($GOPheader['drop_frame_flag'] ? ';' : ':'); // While non-drop time code is displayed with colons separating the digit pairs—"HH:MM:SS:FF"—drop frame is usually represented with a semi-colon (;) or period (.) as the divider between all the digit pairs—"HH;MM;SS;FF", "HH.MM.SS.FF"
$GOPheader['time_code'] = sprintf('%02d'.$time_code_separator.'%02d'.$time_code_separator.'%02d'.$time_code_separator.'%02d', $GOPheader['time_code_hours'], $GOPheader['time_code_minutes'], $GOPheader['time_code_seconds'], $GOPheader['time_code_pictures']);
$info['mpeg']['group_of_pictures'][] = $GOPheader;
}
break;
case 0xC0: // audio stream
case 0xC1: // audio stream
case 0xC2: // audio stream
case 0xC3: // audio stream
case 0xC4: // audio stream
case 0xC5: // audio stream
case 0xC6: // audio stream
case 0xC7: // audio stream
case 0xC8: // audio stream
case 0xC9: // audio stream
case 0xCA: // audio stream
case 0xCB: // audio stream
case 0xCC: // audio stream
case 0xCD: // audio stream
case 0xCE: // audio stream
case 0xCF: // audio stream
case 0xD0: // audio stream
case 0xD1: // audio stream
case 0xD2: // audio stream
case 0xD3: // audio stream
case 0xD4: // audio stream
case 0xD5: // audio stream
case 0xD6: // audio stream
case 0xD7: // audio stream
case 0xD8: // audio stream
case 0xD9: // audio stream
case 0xDA: // audio stream
case 0xDB: // audio stream
case 0xDC: // audio stream
case 0xDD: // audio stream
case 0xDE: // audio stream
case 0xDF: // audio stream
//case 0xE0: // video stream
//case 0xE1: // video stream
//case 0xE2: // video stream
//case 0xE3: // video stream
//case 0xE4: // video stream
//case 0xE5: // video stream
//case 0xE6: // video stream
//case 0xE7: // video stream
//case 0xE8: // video stream
//case 0xE9: // video stream
//case 0xEA: // video stream
//case 0xEB: // video stream
//case 0xEC: // video stream
//case 0xED: // video stream
//case 0xEE: // video stream
//case 0xEF: // video stream
if (isset($ParsedAVchannels[$StartCodeValue])) {
break;
}
$ParsedAVchannels[$StartCodeValue] = $StartCodeValue;
// http://en.wikipedia.org/wiki/Packetized_elementary_stream
// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
/*
$PackedElementaryStream = array();
if ($StartCodeValue >= 0xE0) {
$PackedElementaryStream['stream_type'] = 'video';
$PackedElementaryStream['stream_id'] = $StartCodeValue - 0xE0;
} else {
$PackedElementaryStream['stream_type'] = 'audio';
$PackedElementaryStream['stream_id'] = $StartCodeValue - 0xC0;
}
$PackedElementaryStream['packet_length'] = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $StartCodeOffset + 4, 2));
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 6, 3)); // more may be needed below
$bitstreamoffset = 0;
$PackedElementaryStream['marker_bits'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for marker_bits -- should be "10" = 2
echo 'marker_bits = '.$PackedElementaryStream['marker_bits'].'<br>';
$PackedElementaryStream['scrambling_control'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2); // 2 bits for scrambling_control -- 00 implies not scrambled
$PackedElementaryStream['priority'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: priority
$PackedElementaryStream['data_alignment_indicator'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: data_alignment_indicator -- 1 indicates that the PES packet header is immediately followed by the video start code or audio syncword
$PackedElementaryStream['copyright'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: copyright -- 1 implies copyrighted
$PackedElementaryStream['original_or_copy'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: original_or_copy -- 1 implies original
$PackedElementaryStream['pts_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: pts_flag -- Presentation Time Stamp
$PackedElementaryStream['dts_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: dts_flag -- Decode Time Stamp
$PackedElementaryStream['escr_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: escr_flag -- Elementary Stream Clock Reference
$PackedElementaryStream['es_rate_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: es_rate_flag -- Elementary Stream [data] Rate
$PackedElementaryStream['dsm_trick_mode_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: dsm_trick_mode_flag -- DSM trick mode - not used by DVD
$PackedElementaryStream['additional_copy_info_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: additional_copy_info_flag
$PackedElementaryStream['crc_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: crc_flag
$PackedElementaryStream['extension_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1); // 1 bit flag: extension_flag
$PackedElementaryStream['pes_remain_header_length'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8); // 1 bit flag: priority
$additional_header_bytes = 0;
$additional_header_bytes += ($PackedElementaryStream['pts_flag'] ? 5 : 0);
$additional_header_bytes += ($PackedElementaryStream['dts_flag'] ? 5 : 0);
$additional_header_bytes += ($PackedElementaryStream['escr_flag'] ? 6 : 0);
$additional_header_bytes += ($PackedElementaryStream['es_rate_flag'] ? 3 : 0);
$additional_header_bytes += ($PackedElementaryStream['additional_copy_info_flag'] ? 1 : 0);
$additional_header_bytes += ($PackedElementaryStream['crc_flag'] ? 2 : 0);
$additional_header_bytes += ($PackedElementaryStream['extension_flag'] ? 1 : 0);
$PackedElementaryStream['additional_header_bytes'] = $additional_header_bytes;
$bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 9, $additional_header_bytes));
$info['mpeg']['packed_elementary_streams'][$PackedElementaryStream['stream_type']][$PackedElementaryStream['stream_id']][] = $PackedElementaryStream;
*/
$getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename);
$getid3_temp->info = $info;
$getid3_mp3 = new getid3_mp3($getid3_temp);
for ($i = 0; $i <= 7; $i++) {
// some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
// I have no idea why or what the difference is, so this is a stupid hack.
// If anybody has any better idea of what's going on, please let me know - info@getid3.org
$getid3_temp->info = $info; // only overwrite real data if valid header found
//echo 'audio at? '.($MPEGstreamBaseOffset + $StartCodeOffset + 4 + 8 + $i).'<br>';
if ($getid3_mp3->decodeMPEGaudioHeader($MPEGstreamBaseOffset + $StartCodeOffset + 4 + 8 + $i, $getid3_temp->info, false)) {
//echo 'yes!<br>';
$info = $getid3_temp->info;
$info['audio']['bitrate_mode'] = 'cbr';
$info['audio']['lossless'] = false;
break;
}
}
unset($getid3_temp, $getid3_mp3);
break;
case 0xBC: // Program Stream Map
case 0xBD: // Private stream 1 (non MPEG audio, subpictures)
case 0xBE: // Padding stream
case 0xBF: // Private stream 2 (navigation data)
case 0xF0: // ECM stream
case 0xF1: // EMM stream
case 0xF2: // DSM-CC stream
case 0xF3: // ISO/IEC_13522_stream
case 0xF4: // ITU-I Rec. H.222.1 type A
case 0xF5: // ITU-I Rec. H.222.1 type B
case 0xF6: // ITU-I Rec. H.222.1 type C
case 0xF7: // ITU-I Rec. H.222.1 type D
case 0xF8: // ITU-I Rec. H.222.1 type E
case 0xF9: // ancilliary stream
case 0xFA: // ISO/IEC 14496-1 SL-packtized stream
case 0xFB: // ISO/IEC 14496-1 FlexMux stream
case 0xFC: // metadata stream
case 0xFD: // extended stream ID
case 0xFE: // reserved data stream
case 0xFF: // program stream directory
// ignore
break;
default:
// ignore
break;
}
} while (true);
// // Temporary hack to account for interleaving overhead:
// if (!empty($info['video']['bitrate']) && !empty($info['audio']['bitrate'])) {
// $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['video']['bitrate'] + $info['audio']['bitrate']);
//
// // Interleaved MPEG audio/video files have a certain amount of overhead that varies
// // by both video and audio bitrates, and not in any sensible, linear/logarithmic pattern
// // Use interpolated lookup tables to approximately guess how much is overhead, because
// // playtime is calculated as filesize / total-bitrate
// $info['playtime_seconds'] *= self::systemNonOverheadPercentage($info['video']['bitrate'], $info['audio']['bitrate']);
//
// //switch ($info['video']['bitrate']) {
// // case('5000000'):
// // $multiplier = 0.93292642112380355828048824319889;
// // break;
// // case('5500000'):
// // $multiplier = 0.93582895375200989965359777343219;
// // break;
// // case('6000000'):
// // $multiplier = 0.93796247714820932532911373859139;
// // break;
// // case('7000000'):
// // $multiplier = 0.9413264083635103463010117778776;
// // break;
// // default:
// // $multiplier = 1;
// // break;
// //}
// //$info['playtime_seconds'] *= $multiplier;
// //$info['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.';
// if ($info['video']['bitrate'] < 50000) {
// $this->warning('Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.');
// }
// }
//
/*
$time_prev = 0;
$byte_prev = 0;
$vbr_bitrates = array();
foreach ($info['mpeg']['group_of_pictures'] as $gopkey => $gopdata) {
$time_this = ($gopdata['time_code_hours'] * 3600) + ($gopdata['time_code_minutes'] * 60) + $gopdata['time_code_seconds'] + ($gopdata['time_code_seconds'] / 30);
$byte_this = $gopdata['byte_offset'];
if ($gopkey > 0) {
if ($time_this > $time_prev) {
$bytedelta = $byte_this - $byte_prev;
$timedelta = $time_this - $time_prev;
$this_bitrate = ($bytedelta * 8) / $timedelta;
echo $gopkey.': ('.number_format($time_prev, 2).'-'.number_format($time_this, 2).') '.number_format($bytedelta).' bytes over '.number_format($timedelta, 3).' seconds = '.number_format($this_bitrate / 1000, 2).'kbps<br>';
$time_prev = $time_this;
$byte_prev = $byte_this;
$vbr_bitrates[] = $this_bitrate;
}
}
}
echo 'average_File_bitrate = '.number_format(array_sum($vbr_bitrates) / count($vbr_bitrates), 1).'<br>';
*/
//echo '<pre>'.print_r($FramesByGOP, true).'</pre>';
if (!empty($info['mpeg']['video']['bitrate_mode']) && ($info['mpeg']['video']['bitrate_mode'] == 'vbr')) {
$last_GOP_id = max(array_keys($FramesByGOP));
$frames_in_last_GOP = count($FramesByGOP[$last_GOP_id]);
$gopdata = &$info['mpeg']['group_of_pictures'][$last_GOP_id];
$info['playtime_seconds'] = ($gopdata['time_code_hours'] * 3600) + ($gopdata['time_code_minutes'] * 60) + $gopdata['time_code_seconds'] + (($gopdata['time_code_pictures'] + $frames_in_last_GOP + 1) / $info['mpeg']['video']['frame_rate']);
if (!isset($info['video']['bitrate'])) {
$overall_bitrate = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
$info['video']['bitrate'] = $overall_bitrate - (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0);
}
unset($info['mpeg']['group_of_pictures']);
}
return true;
}
private function readBitsFromStream(&$bitstream, &$bitstreamoffset, $bits_to_read, $return_singlebit_as_boolean=true) {
$return = bindec(substr($bitstream, $bitstreamoffset, $bits_to_read));
$bitstreamoffset += $bits_to_read;
if (($bits_to_read == 1) && $return_singlebit_as_boolean) {
$return = (bool) $return;
}
return $return;
}
public static function systemNonOverheadPercentage($VideoBitrate, $AudioBitrate) {
$OverheadPercentage = 0;
$AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?)
$VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss)
//OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps)
$OverheadMultiplierByBitrate[32] = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940);
$OverheadMultiplierByBitrate[48] = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960);
$OverheadMultiplierByBitrate[56] = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340);
$OverheadMultiplierByBitrate[64] = array(0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470);
$OverheadMultiplierByBitrate[96] = array(0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690);
$OverheadMultiplierByBitrate[128] = array(0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050);
$OverheadMultiplierByBitrate[160] = array(0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570);
$OverheadMultiplierByBitrate[192] = array(0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620);
$OverheadMultiplierByBitrate[224] = array(0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480);
$OverheadMultiplierByBitrate[256] = array(0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790);
$OverheadMultiplierByBitrate[320] = array(0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190);
$OverheadMultiplierByBitrate[384] = array(0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890);
$BitrateToUseMin = 32;
$BitrateToUseMax = 32;
$previousBitrate = 32;
foreach ($OverheadMultiplierByBitrate as $key => $value) {
if ($AudioBitrate >= $previousBitrate) {
$BitrateToUseMin = $previousBitrate;
}
if ($AudioBitrate < $key) {
$BitrateToUseMax = $key;
break;
}
$previousBitrate = $key;
}
$FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin);
$VideoBitrateLog10 = log10($VideoBitrate);
$VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)];
$VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)];
$VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)];
$VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)];
$FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10);
$OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV;
$OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV;
$OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV);
$OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV);
return $OverheadPercentage;
}
public static function videoFramerateLookup($rawframerate) {
$lookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60);
return (isset($lookup[$rawframerate]) ? (float) $lookup[$rawframerate] : (float) 0);
}
public static function videoAspectRatioLookup($rawaspectratio) {
$lookup = array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0);
return (isset($lookup[$rawaspectratio]) ? (float) $lookup[$rawaspectratio] : (float) 0);
}
public static function videoAspectRatioTextLookup($rawaspectratio) {
$lookup = array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved');
return (isset($lookup[$rawaspectratio]) ? $lookup[$rawaspectratio] : '');
}
public static function videoFormatTextLookup($video_format) {
// ISO/IEC 13818-2, section 6.3.6, Table 6-6. Meaning of video_format
$lookup = array('component', 'PAL', 'NTSC', 'SECAM', 'MAC', 'Unspecified video format', 'reserved(6)', 'reserved(7)');
return (isset($lookup[$video_format]) ? $lookup[$video_format] : '');
}
public static function scalableModeTextLookup($scalable_mode) {
// ISO/IEC 13818-2, section 6.3.8, Table 6-10. Definition of scalable_mode
$lookup = array('data partitioning', 'spatial scalability', 'SNR scalability', 'temporal scalability');
return (isset($lookup[$scalable_mode]) ? $lookup[$scalable_mode] : '');
}
public static function pictureStructureTextLookup($picture_structure) {
// ISO/IEC 13818-2, section 6.3.11, Table 6-14 Meaning of picture_structure
$lookup = array('reserved', 'Top Field', 'Bottom Field', 'Frame picture');
return (isset($lookup[$picture_structure]) ? $lookup[$picture_structure] : '');
}
public static function chromaFormatTextLookup($chroma_format) {
// ISO/IEC 13818-2, section 6.3.11, Table 6-14 Meaning of picture_structure
$lookup = array('reserved', '4:2:0', '4:2:2', '4:4:4');
return (isset($lookup[$chroma_format]) ? $lookup[$chroma_format] : '');
}
}

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,11 +18,11 @@
class getid3_nsv extends getid3_handler class getid3_nsv extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$NSVheader = fread($this->getid3->fp, 4); $NSVheader = $this->fread(4);
switch ($NSVheader) { switch ($NSVheader) {
case 'NSVs': case 'NSVs':
@ -58,10 +59,10 @@ class getid3_nsv extends getid3_handler
return true; return true;
} }
function getNSVsHeaderFilepointer($fileoffset) { public function getNSVsHeaderFilepointer($fileoffset) {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $fileoffset, SEEK_SET); $this->fseek($fileoffset);
$NSVsheader = fread($this->getid3->fp, 28); $NSVsheader = $this->fread(28);
$offset = 0; $offset = 0;
$info['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4); $info['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4);
@ -131,10 +132,10 @@ class getid3_nsv extends getid3_handler
return true; return true;
} }
function getNSVfHeaderFilepointer($fileoffset, $getTOCoffsets=false) { public function getNSVfHeaderFilepointer($fileoffset, $getTOCoffsets=false) {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $fileoffset, SEEK_SET); $this->fseek($fileoffset);
$NSVfheader = fread($this->getid3->fp, 28); $NSVfheader = $this->fread(28);
$offset = 0; $offset = 0;
$info['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4); $info['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4);
@ -171,7 +172,7 @@ class getid3_nsv extends getid3_handler
return false; return false;
} }
$NSVfheader .= fread($this->getid3->fp, $info['nsv']['NSVf']['meta_size'] + (4 * $info['nsv']['NSVf']['TOC_entries_1']) + (4 * $info['nsv']['NSVf']['TOC_entries_2'])); $NSVfheader .= $this->fread($info['nsv']['NSVf']['meta_size'] + (4 * $info['nsv']['NSVf']['TOC_entries_1']) + (4 * $info['nsv']['NSVf']['TOC_entries_2']));
$NSVfheaderlength = strlen($NSVfheader); $NSVfheaderlength = strlen($NSVfheader);
$info['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $info['nsv']['NSVf']['meta_size']); $info['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $info['nsv']['NSVf']['meta_size']);
$offset += $info['nsv']['NSVf']['meta_size']; $offset += $info['nsv']['NSVf']['meta_size'];
@ -205,7 +206,7 @@ class getid3_nsv extends getid3_handler
} }
static function NSVframerateLookup($framerateindex) { public static function NSVframerateLookup($framerateindex) {
if ($framerateindex <= 127) { if ($framerateindex <= 127) {
return (float) $framerateindex; return (float) $framerateindex;
} }
@ -221,6 +222,3 @@ class getid3_nsv extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -18,22 +19,22 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php',
class getid3_real extends getid3_handler class getid3_real extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'real'; $info['fileformat'] = 'real';
$info['bitrate'] = 0; $info['bitrate'] = 0;
$info['playtime_seconds'] = 0; $info['playtime_seconds'] = 0;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$ChunkCounter = 0; $ChunkCounter = 0;
while (ftell($this->getid3->fp) < $info['avdataend']) { while ($this->ftell() < $info['avdataend']) {
$ChunkData = fread($this->getid3->fp, 8); $ChunkData = $this->fread(8);
$ChunkName = substr($ChunkData, 0, 4); $ChunkName = substr($ChunkData, 0, 4);
$ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4)); $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4));
if ($ChunkName == '.ra'."\xFD") { if ($ChunkName == '.ra'."\xFD") {
$ChunkData .= fread($this->getid3->fp, $ChunkSize - 8); $ChunkData .= $this->fread($ChunkSize - 8);
if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $info['real']['old_ra_header'])) { if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $info['real']['old_ra_header'])) {
$info['audio']['dataformat'] = 'real'; $info['audio']['dataformat'] = 'real';
$info['audio']['lossless'] = false; $info['audio']['lossless'] = false;
@ -63,7 +64,7 @@ class getid3_real extends getid3_handler
$thisfile_real_chunks_currentchunk = &$info['real']['chunks'][$ChunkCounter]; $thisfile_real_chunks_currentchunk = &$info['real']['chunks'][$ChunkCounter];
$thisfile_real_chunks_currentchunk['name'] = $ChunkName; $thisfile_real_chunks_currentchunk['name'] = $ChunkName;
$thisfile_real_chunks_currentchunk['offset'] = ftell($this->getid3->fp) - 8; $thisfile_real_chunks_currentchunk['offset'] = $this->ftell() - 8;
$thisfile_real_chunks_currentchunk['length'] = $ChunkSize; $thisfile_real_chunks_currentchunk['length'] = $ChunkSize;
if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $info['avdataend']) { if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $info['avdataend']) {
$info['warning'][] = 'Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file'; $info['warning'][] = 'Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file';
@ -72,12 +73,12 @@ class getid3_real extends getid3_handler
if ($ChunkSize > ($this->getid3->fread_buffer_size() + 8)) { if ($ChunkSize > ($this->getid3->fread_buffer_size() + 8)) {
$ChunkData .= fread($this->getid3->fp, $this->getid3->fread_buffer_size() - 8); $ChunkData .= $this->fread($this->getid3->fread_buffer_size() - 8);
fseek($this->getid3->fp, $thisfile_real_chunks_currentchunk['offset'] + $ChunkSize, SEEK_SET); $this->fseek($thisfile_real_chunks_currentchunk['offset'] + $ChunkSize);
} elseif(($ChunkSize - 8) > 0) { } elseif(($ChunkSize - 8) > 0) {
$ChunkData .= fread($this->getid3->fp, $ChunkSize - 8); $ChunkData .= $this->fread($ChunkSize - 8);
} }
$offset = 8; $offset = 8;
@ -202,7 +203,7 @@ class getid3_real extends getid3_handler
//$thisfile_real_chunks_currentchunk_videoinfo['unknown8'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2)); //$thisfile_real_chunks_currentchunk_videoinfo['unknown8'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2));
//$thisfile_real_chunks_currentchunk_videoinfo['unknown9'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2)); //$thisfile_real_chunks_currentchunk_videoinfo['unknown9'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2));
$thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::RIFFfourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']); $thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::fourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']);
$info['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width']; $info['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width'];
$info['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height']; $info['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height'];
@ -347,7 +348,7 @@ class getid3_real extends getid3_handler
break 2; break 2;
} else { } else {
// non-last index chunk, seek to next index chunk (skipping actual index data) // non-last index chunk, seek to next index chunk (skipping actual index data)
fseek($this->getid3->fp, $thisfile_real_chunks_currentchunk['next_index_header'], SEEK_SET); $this->fseek($thisfile_real_chunks_currentchunk['next_index_header']);
} }
} }
break; break;
@ -370,7 +371,7 @@ class getid3_real extends getid3_handler
} }
function ParseOldRAheader($OldRAheaderData, &$ParsedArray) { public function ParseOldRAheader($OldRAheaderData, &$ParsedArray) {
// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html // http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
$ParsedArray = array(); $ParsedArray = array();
@ -484,7 +485,7 @@ class getid3_real extends getid3_handler
return true; return true;
} }
function RealAudioCodecFourCClookup($fourcc, $bitrate) { public function RealAudioCodecFourCClookup($fourcc, $bitrate) {
static $RealAudioCodecFourCClookup = array(); static $RealAudioCodecFourCClookup = array();
if (empty($RealAudioCodecFourCClookup)) { if (empty($RealAudioCodecFourCClookup)) {
// http://www.its.msstate.edu/net/real/reports/config/tags.stats // http://www.its.msstate.edu/net/real/reports/config/tags.stats
@ -525,6 +526,3 @@ class getid3_real extends getid3_handler
} }
} }
?>

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -16,9 +17,9 @@
class getid3_swf extends getid3_handler class getid3_swf extends getid3_handler
{ {
var $ReturnAllTagData = false; public $ReturnAllTagData = false;
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'swf'; $info['fileformat'] = 'swf';
@ -26,9 +27,9 @@ class getid3_swf extends getid3_handler
// http://www.openswf.org/spec/SWFfileformat.html // http://www.openswf.org/spec/SWFfileformat.html
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$SWFfileData = fread($this->getid3->fp, $info['avdataend'] - $info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data $SWFfileData = $this->fread($info['avdataend'] - $info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
$info['swf']['header']['signature'] = substr($SWFfileData, 0, 3); $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
switch ($info['swf']['header']['signature']) { switch ($info['swf']['header']['signature']) {
@ -137,6 +138,3 @@ class getid3_swf extends getid3_handler
} }
} }
?>

View file

@ -0,0 +1,79 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio-video.ts.php //
// module for analyzing MPEG Transport Stream (.ts) files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_ts extends getid3_handler
{
public function Analyze() {
$info = &$this->getid3->info;
$this->fseek($info['avdataoffset']);
$TSheader = $this->fread(19);
$magic = "\x47";
if (substr($TSheader, 0, 1) != $magic) {
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($TSheader, 0, 1)).' instead.';
return false;
}
$info['fileformat'] = 'ts';
// http://en.wikipedia.org/wiki/.ts
$offset = 0;
$info['ts']['packet']['sync'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 1)); $offset += 1;
$pid_flags_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 2)); $offset += 2;
$SAC_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 1)); $offset += 1;
$info['ts']['packet']['flags']['transport_error_indicator'] = (bool) ($pid_flags_raw & 0x8000); // Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error
$info['ts']['packet']['flags']['payload_unit_start_indicator'] = (bool) ($pid_flags_raw & 0x4000); // 1 means start of PES data or PSI otherwise zero only.
$info['ts']['packet']['flags']['transport_high_priority'] = (bool) ($pid_flags_raw & 0x2000); // 1 means higher priority than other packets with the same PID.
$info['ts']['packet']['packet_id'] = ($pid_flags_raw & 0x1FFF) >> 0;
$info['ts']['packet']['raw']['scrambling_control'] = ($SAC_raw & 0xC0) >> 6;
$info['ts']['packet']['flags']['adaption_field_exists'] = (bool) ($SAC_raw & 0x20);
$info['ts']['packet']['flags']['payload_exists'] = (bool) ($SAC_raw & 0x10);
$info['ts']['packet']['continuity_counter'] = ($SAC_raw & 0x0F) >> 0; // Incremented only when a payload is present
$info['ts']['packet']['scrambling_control'] = $this->TSscramblingControlLookup($info['ts']['packet']['raw']['scrambling_control']);
if ($info['ts']['packet']['flags']['adaption_field_exists']) {
$AdaptionField_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 2)); $offset += 2;
$info['ts']['packet']['adaption']['field_length'] = ($AdaptionField_raw & 0xFF00) >> 8; // Number of bytes in the adaptation field immediately following this byte
$info['ts']['packet']['adaption']['flags']['discontinuity'] = (bool) ($AdaptionField_raw & 0x0080); // Set to 1 if current TS packet is in a discontinuity state with respect to either the continuity counter or the program clock reference
$info['ts']['packet']['adaption']['flags']['random_access'] = (bool) ($AdaptionField_raw & 0x0040); // Set to 1 if the PES packet in this TS packet starts a video/audio sequence
$info['ts']['packet']['adaption']['flags']['high_priority'] = (bool) ($AdaptionField_raw & 0x0020); // 1 = higher priority
$info['ts']['packet']['adaption']['flags']['pcr'] = (bool) ($AdaptionField_raw & 0x0010); // 1 means adaptation field does contain a PCR field
$info['ts']['packet']['adaption']['flags']['opcr'] = (bool) ($AdaptionField_raw & 0x0008); // 1 means adaptation field does contain an OPCR field
$info['ts']['packet']['adaption']['flags']['splice_point'] = (bool) ($AdaptionField_raw & 0x0004); // 1 means presence of splice countdown field in adaptation field
$info['ts']['packet']['adaption']['flags']['private_data'] = (bool) ($AdaptionField_raw & 0x0002); // 1 means presence of private data bytes in adaptation field
$info['ts']['packet']['adaption']['flags']['extension'] = (bool) ($AdaptionField_raw & 0x0001); // 1 means presence of adaptation field extension
if ($info['ts']['packet']['adaption']['flags']['pcr']) {
$info['ts']['packet']['adaption']['raw']['pcr'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 6)); $offset += 6;
}
if ($info['ts']['packet']['adaption']['flags']['opcr']) {
$info['ts']['packet']['adaption']['raw']['opcr'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 6)); $offset += 6;
}
}
$info['error'][] = 'MPEG Transport Stream (.ts) parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
return false;
}
public function TSscramblingControlLookup($raw) {
$TSscramblingControlLookup = array(0x00=>'not scrambled', 0x01=>'reserved', 0x02=>'scrambled, even key', 0x03=>'scrambled, odd key');
return (isset($TSscramblingControlLookup[$raw]) ? $TSscramblingControlLookup[$raw] : 'invalid');
}
}

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,11 +18,11 @@
class getid3_aa extends getid3_handler class getid3_aa extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$AAheader = fread($this->getid3->fp, 8); $AAheader = $this->fread(8);
$magic = "\x57\x90\x75\x36"; $magic = "\x57\x90\x75\x36";
if (substr($AAheader, 4, 4) != $magic) { if (substr($AAheader, 4, 4) != $magic) {
@ -31,21 +32,23 @@ class getid3_aa extends getid3_handler
// shortcut // shortcut
$info['aa'] = array(); $info['aa'] = array();
$thisfile_au = &$info['aa']; $thisfile_aa = &$info['aa'];
$info['fileformat'] = 'aa'; $info['fileformat'] = 'aa';
$info['audio']['dataformat'] = 'aa'; $info['audio']['dataformat'] = 'aa';
$info['error'][] = 'Audible Audiobook (.aa) parsing not enabled in this version of getID3() ['.$this->getid3->version().']';
return false;
$info['audio']['bitrate_mode'] = 'cbr'; // is it? $info['audio']['bitrate_mode'] = 'cbr'; // is it?
$thisfile_au['encoding'] = 'ISO-8859-1'; $thisfile_aa['encoding'] = 'ISO-8859-1';
$thisfile_au['filesize'] = getid3_lib::BigEndian2Int(substr($AUheader, 0, 4)); $thisfile_aa['filesize'] = getid3_lib::BigEndian2Int(substr($AUheader, 0, 4));
if ($thisfile_au['filesize'] > ($info['avdataend'] - $info['avdataoffset'])) { if ($thisfile_aa['filesize'] > ($info['avdataend'] - $info['avdataoffset'])) {
$info['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['filesize'].'" bytes of data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"'; $info['warning'][] = 'Possible truncated file - expecting "'.$thisfile_aa['filesize'].'" bytes of data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"';
} }
$info['audio']['bits_per_sample'] = 16; // is it? $info['audio']['bits_per_sample'] = 16; // is it?
$info['audio']['sample_rate'] = $thisfile_au['sample_rate']; $info['audio']['sample_rate'] = $thisfile_aa['sample_rate'];
$info['audio']['channels'] = $thisfile_au['channels']; $info['audio']['channels'] = $thisfile_aa['channels'];
//$info['playtime_seconds'] = 0; //$info['playtime_seconds'] = 0;
//$info['audio']['bitrate'] = 0; //$info['audio']['bitrate'] = 0;
@ -54,6 +57,3 @@ class getid3_aa extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -16,10 +17,10 @@
class getid3_aac extends getid3_handler class getid3_aac extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
if (fread($this->getid3->fp, 4) == 'ADIF') { if ($this->fread(4) == 'ADIF') {
$this->getAACADIFheaderFilepointer(); $this->getAACADIFheaderFilepointer();
} else { } else {
$this->getAACADTSheaderFilepointer(); $this->getAACADTSheaderFilepointer();
@ -29,14 +30,14 @@ class getid3_aac extends getid3_handler
function getAACADIFheaderFilepointer() { public function getAACADIFheaderFilepointer() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'aac'; $info['fileformat'] = 'aac';
$info['audio']['dataformat'] = 'aac'; $info['audio']['dataformat'] = 'aac';
$info['audio']['lossless'] = false; $info['audio']['lossless'] = false;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$AACheader = fread($this->getid3->fp, 1024); $AACheader = $this->fread(1024);
$offset = 0; $offset = 0;
if (substr($AACheader, 0, 4) == 'ADIF') { if (substr($AACheader, 0, 4) == 'ADIF') {
@ -257,10 +258,10 @@ class getid3_aac extends getid3_handler
} }
function getAACADTSheaderFilepointer($MaxFramesToScan=1000000, $ReturnExtendedInfo=false) { public function getAACADTSheaderFilepointer($MaxFramesToScan=1000000, $ReturnExtendedInfo=false) {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// based loosely on code from AACfile by Jurgen Faul <jfaulØgmx.de> // based loosely on code from AACfile by Jurgen Faul <jfaulØgmx.de>
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
@ -310,16 +311,16 @@ class getid3_aac extends getid3_handler
// or MaxFramesToScan frames have been scanned // or MaxFramesToScan frames have been scanned
if (!getid3_lib::intValueSupported($byteoffset)) { if (!getid3_lib::intValueSupported($byteoffset)) {
$info['warning'][] = 'Unable to parse AAC file beyond '.ftell($this->getid3->fp).' (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)'; $info['warning'][] = 'Unable to parse AAC file beyond '.$this->ftell().' (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)';
return false; return false;
} }
fseek($this->getid3->fp, $byteoffset, SEEK_SET); $this->fseek($byteoffset);
// First get substring // First get substring
$substring = fread($this->getid3->fp, 9); // header is 7 bytes (or 9 if CRC is present) $substring = $this->fread(9); // header is 7 bytes (or 9 if CRC is present)
$substringlength = strlen($substring); $substringlength = strlen($substring);
if ($substringlength != 9) { if ($substringlength != 9) {
$info['error'][] = 'Failed to read 7 bytes at offset '.(ftell($this->getid3->fp) - $substringlength).' (only read '.$substringlength.' bytes)'; $info['error'][] = 'Failed to read 7 bytes at offset '.($this->ftell() - $substringlength).' (only read '.$substringlength.' bytes)';
return false; return false;
} }
// this would be easier with 64-bit math, but split it up to allow for 32-bit: // this would be easier with 64-bit math, but split it up to allow for 32-bit:
@ -329,7 +330,7 @@ class getid3_aac extends getid3_handler
$info['aac']['header']['raw']['syncword'] = ($header1 & 0xFFF0) >> 4; $info['aac']['header']['raw']['syncword'] = ($header1 & 0xFFF0) >> 4;
if ($info['aac']['header']['raw']['syncword'] != 0x0FFF) { if ($info['aac']['header']['raw']['syncword'] != 0x0FFF) {
$info['error'][] = 'Synch pattern (0x0FFF) not found at offset '.(ftell($this->getid3->fp) - $substringlength).' (found 0x0'.strtoupper(dechex($info['aac']['header']['raw']['syncword'])).' instead)'; $info['error'][] = 'Synch pattern (0x0FFF) not found at offset '.($this->ftell() - $substringlength).' (found 0x0'.strtoupper(dechex($info['aac']['header']['raw']['syncword'])).' instead)';
//if ($info['fileformat'] == 'aac') { //if ($info['fileformat'] == 'aac') {
// return true; // return true;
//} //}
@ -510,6 +511,3 @@ class getid3_aac extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -16,9 +17,10 @@
class getid3_ac3 extends getid3_handler class getid3_ac3 extends getid3_handler
{ {
private $AC3header = ''; private $AC3header = array();
private $BSIoffset = 0; private $BSIoffset = 0;
const syncword = "\x0B\x77";
public function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
@ -32,7 +34,7 @@ class getid3_ac3 extends getid3_handler
// http://www.atsc.org/standards/a_52a.pdf // http://www.atsc.org/standards/a_52a.pdf
$info['fileformat'] = 'ac3'; $info['fileformat'] = 'ac3';
// An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames
// Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256 // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256
@ -45,21 +47,6 @@ class getid3_ac3 extends getid3_handler
// //
// syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
$this->AC3header['syncinfo'] = fread($this->getid3->fp, 5);
$thisfile_ac3_raw['synchinfo']['synchword'] = substr($this->AC3header['syncinfo'], 0, 2);
$magic = "\x0B\x77";
if ($thisfile_ac3_raw['synchinfo']['synchword'] != $magic) {
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_ac3_raw['synchinfo']['synchword']).'"';
unset($info['fileformat'], $info['ac3']);
return false;
}
$info['audio']['dataformat'] = 'ac3';
$info['audio']['bitrate_mode'] = 'cbr';
$info['audio']['lossless'] = false;
// syncinfo() { // syncinfo() {
// syncword 16 // syncword 16
// crc1 16 // crc1 16
@ -67,21 +54,40 @@ class getid3_ac3 extends getid3_handler
// frmsizecod 6 // frmsizecod 6
// } /* end of syncinfo */ // } /* end of syncinfo */
$thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], 2, 2)); $this->fseek($info['avdataoffset']);
$ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], 4, 1)); $this->AC3header['syncinfo'] = $this->fread(5);
if (strpos($this->AC3header['syncinfo'], self::syncword) === 0) {
$thisfile_ac3_raw['synchinfo']['synchword'] = self::syncword;
$offset = 2;
} else {
if (!$this->isDependencyFor('matroska')) {
unset($info['fileformat'], $info['ac3']);
return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($this->AC3header['syncinfo'], 0, 2)).'"');
}
$offset = 0;
$this->fseek(-2, SEEK_CUR);
}
$info['audio']['dataformat'] = 'ac3';
$info['audio']['bitrate_mode'] = 'cbr';
$info['audio']['lossless'] = false;
$thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], $offset, 2));
$ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], ($offset + 2), 1));
$thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6; $thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6;
$thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F); $thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F);
$thisfile_ac3['sample_rate'] = $this->AC3sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']); $thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']);
if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) { if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) {
$info['audio']['sample_rate'] = $thisfile_ac3['sample_rate']; $info['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
} }
$thisfile_ac3['frame_length'] = $this->AC3frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']); $thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']);
$thisfile_ac3['bitrate'] = $this->AC3bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']); $thisfile_ac3['bitrate'] = self::bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']);
$info['audio']['bitrate'] = $thisfile_ac3['bitrate']; $info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
$this->AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($this->getid3->fp, 15)); $this->AC3header['bsi'] = getid3_lib::BigEndian2Bin($this->fread(15));
$ac3_bsi_offset = 0; $ac3_bsi_offset = 0;
$thisfile_ac3_raw_bsi['bsid'] = $this->readHeaderBSI(5); $thisfile_ac3_raw_bsi['bsid'] = $this->readHeaderBSI(5);
@ -89,16 +95,16 @@ class getid3_ac3 extends getid3_handler
// Decoders which can decode version 8 will thus be able to decode version numbers less than 8. // Decoders which can decode version 8 will thus be able to decode version numbers less than 8.
// If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used. // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used.
// Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8. // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8.
$info['error'][] = 'Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8'; $this->error('Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8');
unset($thisfile_ac3); unset($info['ac3']);
return false; return false;
} }
$thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3); $thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3);
$thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3); $thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3);
$thisfile_ac3['service_type'] = $this->AC3serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']); $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
$ac3_coding_mode = $this->AC3audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']); $ac3_coding_mode = self::audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
foreach($ac3_coding_mode as $key => $value) { foreach($ac3_coding_mode as $key => $value) {
$thisfile_ac3[$key] = $value; $thisfile_ac3[$key] = $value;
} }
@ -120,19 +126,19 @@ class getid3_ac3 extends getid3_handler
if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) { if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) {
// If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream. // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
$thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2); $thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2);
$thisfile_ac3['center_mix_level'] = $this->AC3centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']); $thisfile_ac3['center_mix_level'] = self::centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
} }
if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) { if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
// If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream. // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
$thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2); $thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2);
$thisfile_ac3['surround_mix_level'] = $this->AC3surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']); $thisfile_ac3['surround_mix_level'] = self::surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
} }
if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) { if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
// When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround. // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
$thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2); $thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2);
$thisfile_ac3['dolby_surround_mode'] = $this->AC3dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']); $thisfile_ac3['dolby_surround_mode'] = self::dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
} }
$thisfile_ac3_raw_bsi['lfeon'] = (bool) $this->readHeaderBSI(1); $thisfile_ac3_raw_bsi['lfeon'] = (bool) $this->readHeaderBSI(1);
@ -142,9 +148,9 @@ class getid3_ac3 extends getid3_handler
$info['audio']['channels'] .= '.1'; $info['audio']['channels'] .= '.1';
} }
$thisfile_ac3['channels_enabled'] = $this->AC3channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']); $thisfile_ac3['channels_enabled'] = self::channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']);
// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 131. // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
// The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
$thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5); $thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5);
$thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB'; $thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
@ -152,7 +158,7 @@ class getid3_ac3 extends getid3_handler
$thisfile_ac3_raw_bsi['compre_flag'] = (bool) $this->readHeaderBSI(1); $thisfile_ac3_raw_bsi['compre_flag'] = (bool) $this->readHeaderBSI(1);
if ($thisfile_ac3_raw_bsi['compre_flag']) { if ($thisfile_ac3_raw_bsi['compre_flag']) {
$thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8); $thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8);
$thisfile_ac3['heavy_compression'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr']); $thisfile_ac3['heavy_compression'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr']);
} }
$thisfile_ac3_raw_bsi['langcode_flag'] = (bool) $this->readHeaderBSI(1); $thisfile_ac3_raw_bsi['langcode_flag'] = (bool) $this->readHeaderBSI(1);
@ -166,7 +172,7 @@ class getid3_ac3 extends getid3_handler
$thisfile_ac3_raw_bsi['roomtyp'] = $this->readHeaderBSI(2); $thisfile_ac3_raw_bsi['roomtyp'] = $this->readHeaderBSI(2);
$thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB'; $thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
$thisfile_ac3['room_type'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']); $thisfile_ac3['room_type'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
} }
if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) { if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) {
@ -174,7 +180,7 @@ class getid3_ac3 extends getid3_handler
// are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case, // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case,
// a number of additional items are present in BSI or audblk to fully describe Ch2. // a number of additional items are present in BSI or audblk to fully describe Ch2.
// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 131. // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
// The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
$thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5); $thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5);
$thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB'; $thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';
@ -182,7 +188,7 @@ class getid3_ac3 extends getid3_handler
$thisfile_ac3_raw_bsi['compre_flag2'] = (bool) $this->readHeaderBSI(1); $thisfile_ac3_raw_bsi['compre_flag2'] = (bool) $this->readHeaderBSI(1);
if ($thisfile_ac3_raw_bsi['compre_flag2']) { if ($thisfile_ac3_raw_bsi['compre_flag2']) {
$thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8); $thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8);
$thisfile_ac3['heavy_compression2'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr2']); $thisfile_ac3['heavy_compression2'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr2']);
} }
$thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) $this->readHeaderBSI(1); $thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) $this->readHeaderBSI(1);
@ -196,7 +202,7 @@ class getid3_ac3 extends getid3_handler
$thisfile_ac3_raw_bsi['roomtyp2'] = $this->readHeaderBSI(2); $thisfile_ac3_raw_bsi['roomtyp2'] = $this->readHeaderBSI(2);
$thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB'; $thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB';
$thisfile_ac3['room_type2'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']); $thisfile_ac3['room_type2'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
} }
} }
@ -219,7 +225,7 @@ class getid3_ac3 extends getid3_handler
if ($thisfile_ac3_raw_bsi['addbsi_flag']) { if ($thisfile_ac3_raw_bsi['addbsi_flag']) {
$thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6); $thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6);
$this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin(fread($this->getid3->fp, $thisfile_ac3_raw_bsi['addbsi_length'])); $this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($thisfile_ac3_raw_bsi['addbsi_length']));
$thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8); $thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
$this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8; $this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
@ -235,93 +241,90 @@ class getid3_ac3 extends getid3_handler
return bindec($data); return bindec($data);
} }
public static function AC3sampleRateCodeLookup($fscod) { public static function sampleRateCodeLookup($fscod) {
static $AC3sampleRateCodeLookup = array( static $sampleRateCodeLookup = array(
0 => 48000, 0 => 48000,
1 => 44100, 1 => 44100,
2 => 32000, 2 => 32000,
3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute. 3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute.
); );
return (isset($AC3sampleRateCodeLookup[$fscod]) ? $AC3sampleRateCodeLookup[$fscod] : false); return (isset($sampleRateCodeLookup[$fscod]) ? $sampleRateCodeLookup[$fscod] : false);
} }
public static function AC3serviceTypeLookup($bsmod, $acmod) { public static function serviceTypeLookup($bsmod, $acmod) {
static $AC3serviceTypeLookup = array(); static $serviceTypeLookup = array();
if (empty($AC3serviceTypeLookup)) { if (empty($serviceTypeLookup)) {
for ($i = 0; $i <= 7; $i++) { for ($i = 0; $i <= 7; $i++) {
$AC3serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)'; $serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)';
$AC3serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)'; $serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)';
$AC3serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)'; $serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)';
$AC3serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)'; $serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)';
$AC3serviceTypeLookup[4][$i] = 'associated service: dialogue (D)'; $serviceTypeLookup[4][$i] = 'associated service: dialogue (D)';
$AC3serviceTypeLookup[5][$i] = 'associated service: commentary (C)'; $serviceTypeLookup[5][$i] = 'associated service: commentary (C)';
$AC3serviceTypeLookup[6][$i] = 'associated service: emergency (E)'; $serviceTypeLookup[6][$i] = 'associated service: emergency (E)';
} }
$AC3serviceTypeLookup[7][1] = 'associated service: voice over (VO)'; $serviceTypeLookup[7][1] = 'associated service: voice over (VO)';
for ($i = 2; $i <= 7; $i++) { for ($i = 2; $i <= 7; $i++) {
$AC3serviceTypeLookup[7][$i] = 'main audio service: karaoke'; $serviceTypeLookup[7][$i] = 'main audio service: karaoke';
} }
} }
return (isset($AC3serviceTypeLookup[$bsmod][$acmod]) ? $AC3serviceTypeLookup[$bsmod][$acmod] : false); return (isset($serviceTypeLookup[$bsmod][$acmod]) ? $serviceTypeLookup[$bsmod][$acmod] : false);
} }
public static function AC3audioCodingModeLookup($acmod) { public static function audioCodingModeLookup($acmod) {
static $AC3audioCodingModeLookup = array(); // array(channel configuration, # channels (not incl LFE), channel order)
if (empty($AC3audioCodingModeLookup)) { static $audioCodingModeLookup = array (
// array(channel configuration, # channels (not incl LFE), channel order) 0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'),
$AC3audioCodingModeLookup = array ( 1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'),
0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'), 2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'),
1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'), 3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'),
2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'), 4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'),
3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'), 5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'),
4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'), 6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'),
5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'), 7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR'),
6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'), );
7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR') return (isset($audioCodingModeLookup[$acmod]) ? $audioCodingModeLookup[$acmod] : false);
);
}
return (isset($AC3audioCodingModeLookup[$acmod]) ? $AC3audioCodingModeLookup[$acmod] : false);
} }
public static function AC3centerMixLevelLookup($cmixlev) { public static function centerMixLevelLookup($cmixlev) {
static $AC3centerMixLevelLookup; static $centerMixLevelLookup;
if (empty($AC3centerMixLevelLookup)) { if (empty($centerMixLevelLookup)) {
$AC3centerMixLevelLookup = array( $centerMixLevelLookup = array(
0 => pow(2, -3.0 / 6), // 0.707 (3.0 dB) 0 => pow(2, -3.0 / 6), // 0.707 (-3.0 dB)
1 => pow(2, -4.5 / 6), // 0.595 (4.5 dB) 1 => pow(2, -4.5 / 6), // 0.595 (-4.5 dB)
2 => pow(2, -6.0 / 6), // 0.500 (6.0 dB) 2 => pow(2, -6.0 / 6), // 0.500 (-6.0 dB)
3 => 'reserved' 3 => 'reserved'
); );
} }
return (isset($AC3centerMixLevelLookup[$cmixlev]) ? $AC3centerMixLevelLookup[$cmixlev] : false); return (isset($centerMixLevelLookup[$cmixlev]) ? $centerMixLevelLookup[$cmixlev] : false);
} }
public static function AC3surroundMixLevelLookup($surmixlev) { public static function surroundMixLevelLookup($surmixlev) {
static $AC3surroundMixLevelLookup; static $surroundMixLevelLookup;
if (empty($AC3surroundMixLevelLookup)) { if (empty($surroundMixLevelLookup)) {
$AC3surroundMixLevelLookup = array( $surroundMixLevelLookup = array(
0 => pow(2, -3.0 / 6), 0 => pow(2, -3.0 / 6),
1 => pow(2, -6.0 / 6), 1 => pow(2, -6.0 / 6),
2 => 0, 2 => 0,
3 => 'reserved' 3 => 'reserved'
); );
} }
return (isset($AC3surroundMixLevelLookup[$surmixlev]) ? $AC3surroundMixLevelLookup[$surmixlev] : false); return (isset($surroundMixLevelLookup[$surmixlev]) ? $surroundMixLevelLookup[$surmixlev] : false);
} }
public static function AC3dolbySurroundModeLookup($dsurmod) { public static function dolbySurroundModeLookup($dsurmod) {
static $AC3dolbySurroundModeLookup = array( static $dolbySurroundModeLookup = array(
0 => 'not indicated', 0 => 'not indicated',
1 => 'Not Dolby Surround encoded', 1 => 'Not Dolby Surround encoded',
2 => 'Dolby Surround encoded', 2 => 'Dolby Surround encoded',
3 => 'reserved' 3 => 'reserved'
); );
return (isset($AC3dolbySurroundModeLookup[$dsurmod]) ? $AC3dolbySurroundModeLookup[$dsurmod] : false); return (isset($dolbySurroundModeLookup[$dsurmod]) ? $dolbySurroundModeLookup[$dsurmod] : false);
} }
public static function AC3channelsEnabledLookup($acmod, $lfeon) { public static function channelsEnabledLookup($acmod, $lfeon) {
$AC3channelsEnabledLookup = array( $lookup = array(
'ch1'=>(bool) ($acmod == 0), 'ch1'=>(bool) ($acmod == 0),
'ch2'=>(bool) ($acmod == 0), 'ch2'=>(bool) ($acmod == 0),
'left'=>(bool) ($acmod > 1), 'left'=>(bool) ($acmod > 1),
@ -334,25 +337,25 @@ class getid3_ac3 extends getid3_handler
switch ($acmod) { switch ($acmod) {
case 4: case 4:
case 5: case 5:
$AC3channelsEnabledLookup['surround_mono'] = true; $lookup['surround_mono'] = true;
break; break;
case 6: case 6:
case 7: case 7:
$AC3channelsEnabledLookup['surround_left'] = true; $lookup['surround_left'] = true;
$AC3channelsEnabledLookup['surround_right'] = true; $lookup['surround_right'] = true;
break; break;
} }
return $AC3channelsEnabledLookup; return $lookup;
} }
public static function AC3heavyCompression($compre) { public static function heavyCompression($compre) {
// The first four bits indicate gain changes in 6.02dB increments which can be // The first four bits indicate gain changes in 6.02dB increments which can be
// implemented with an arithmetic shift operation. The following four bits // implemented with an arithmetic shift operation. The following four bits
// indicate linear gain changes, and require a 5-bit multiply. // indicate linear gain changes, and require a 5-bit multiply.
// We will represent the two 4-bit fields of compr as follows: // We will represent the two 4-bit fields of compr as follows:
// X0 X1 X2 X3 . Y4 Y5 Y6 Y7 // X0 X1 X2 X3 . Y4 Y5 Y6 Y7
// The meaning of the X values is most simply described by considering X to represent a 4-bit // The meaning of the X values is most simply described by considering X to represent a 4-bit
// signed integer with values from 8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The // signed integer with values from -8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The
// following table shows this in detail. // following table shows this in detail.
// Meaning of 4 msb of compr // Meaning of 4 msb of compr
@ -365,13 +368,13 @@ class getid3_ac3 extends getid3_handler
// 1 +12.04 dB // 1 +12.04 dB
// 0 +6.02 dB // 0 +6.02 dB
// -1 0 dB // -1 0 dB
// -2 6.02 dB // -2 -6.02 dB
// -3 12.04 dB // -3 -12.04 dB
// -4 18.06 dB // -4 -18.06 dB
// -5 24.08 dB // -5 -24.08 dB
// -6 30.10 dB // -6 -30.10 dB
// -7 36.12 dB // -7 -36.12 dB
// -8 42.14 dB // -8 -42.14 dB
$fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT); $fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
if ($fourbit{0} == '1') { if ($fourbit{0} == '1') {
@ -381,37 +384,37 @@ class getid3_ac3 extends getid3_handler
} }
$log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2); $log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2);
// The value of Y is a linear representation of a gain change of up to 6 dB. Y is considered to // The value of Y is a linear representation of a gain change of up to -6 dB. Y is considered to
// be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can // be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can
// represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain // represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain
// changes from 0.28 dB to 6.02 dB. // changes from -0.28 dB to -6.02 dB.
$lin_gain = (16 + ($compre & 0x0F)) / 32; $lin_gain = (16 + ($compre & 0x0F)) / 32;
// The combination of X and Y values allows compr to indicate gain changes from // The combination of X and Y values allows compr to indicate gain changes from
// 48.16 0.28 = +47.89 dB, to // 48.16 - 0.28 = +47.89 dB, to
// 42.14 6.02 = 48.16 dB. // -42.14 - 6.02 = -48.16 dB.
return $log_gain - $lin_gain; return $log_gain - $lin_gain;
} }
public static function AC3roomTypeLookup($roomtyp) { public static function roomTypeLookup($roomtyp) {
static $AC3roomTypeLookup = array( static $roomTypeLookup = array(
0 => 'not indicated', 0 => 'not indicated',
1 => 'large room, X curve monitor', 1 => 'large room, X curve monitor',
2 => 'small room, flat monitor', 2 => 'small room, flat monitor',
3 => 'reserved' 3 => 'reserved'
); );
return (isset($AC3roomTypeLookup[$roomtyp]) ? $AC3roomTypeLookup[$roomtyp] : false); return (isset($roomTypeLookup[$roomtyp]) ? $roomTypeLookup[$roomtyp] : false);
} }
public static function AC3frameSizeLookup($frmsizecod, $fscod) { public static function frameSizeLookup($frmsizecod, $fscod) {
$padding = (bool) ($frmsizecod % 2); $padding = (bool) ($frmsizecod % 2);
$framesizeid = floor($frmsizecod / 2); $framesizeid = floor($frmsizecod / 2);
static $AC3frameSizeLookup = array(); static $frameSizeLookup = array();
if (empty($AC3frameSizeLookup)) { if (empty($frameSizeLookup)) {
$AC3frameSizeLookup = array ( $frameSizeLookup = array (
0 => array(128, 138, 192), 0 => array(128, 138, 192),
1 => array(40, 160, 174, 240), 1 => array(40, 160, 174, 240),
2 => array(48, 192, 208, 288), 2 => array(48, 192, 208, 288),
@ -435,15 +438,15 @@ class getid3_ac3 extends getid3_handler
} }
if (($fscod == 1) && $padding) { if (($fscod == 1) && $padding) {
// frame lengths are padded by 1 word (16 bits) at 44100 // frame lengths are padded by 1 word (16 bits) at 44100
$AC3frameSizeLookup[$frmsizecod] += 2; $frameSizeLookup[$frmsizecod] += 2;
} }
return (isset($AC3frameSizeLookup[$framesizeid][$fscod]) ? $AC3frameSizeLookup[$framesizeid][$fscod] : false); return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] : false);
} }
public static function AC3bitrateLookup($frmsizecod) { public static function bitrateLookup($frmsizecod) {
$framesizeid = floor($frmsizecod / 2); $framesizeid = floor($frmsizecod / 2);
static $AC3bitrateLookup = array( static $bitrateLookup = array(
0 => 32000, 0 => 32000,
1 => 40000, 1 => 40000,
2 => 48000, 2 => 48000,
@ -464,10 +467,8 @@ class getid3_ac3 extends getid3_handler
17 => 576000, 17 => 576000,
18 => 640000 18 => 640000
); );
return (isset($AC3bitrateLookup[$framesizeid]) ? $AC3bitrateLookup[$framesizeid] : false); return (isset($bitrateLookup[$framesizeid]) ? $bitrateLookup[$framesizeid] : false);
} }
} }
?>

View file

@ -0,0 +1,97 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio.aa.php //
// module for analyzing Audible Audiobook files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_amr extends getid3_handler
{
public function Analyze() {
$info = &$this->getid3->info;
$this->fseek($info['avdataoffset']);
$AMRheader = $this->fread(6);
$magic = '#!AMR'."\x0A";
if (substr($AMRheader, 0, 6) != $magic) {
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AMRheader, 0, 6)).'"';
return false;
}
// shortcut
$info['amr'] = array();
$thisfile_amr = &$info['amr'];
$info['fileformat'] = 'amr';
$info['audio']['dataformat'] = 'amr';
$info['audio']['bitrate_mode'] = 'vbr'; // within a small predefined range: 4.75kbps to 12.2kbps
$info['audio']['bits_per_sample'] = 13; // http://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec: "Sampling frequency 8 kHz/13-bit (160 samples for 20 ms frames), filtered to 2003400 Hz"
$info['audio']['sample_rate'] = 8000; // http://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec: "Sampling frequency 8 kHz/13-bit (160 samples for 20 ms frames), filtered to 2003400 Hz"
$info['audio']['channels'] = 1;
$thisfile_amr['frame_mode_count'] = array(0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0, 7=>0);
$buffer = '';
do {
if ((strlen($buffer) < $this->getid3->fread_buffer_size()) && !feof($this->getid3->fp)) {
$buffer .= $this->fread($this->getid3->fread_buffer_size() * 2);
}
$AMR_frame_header = ord(substr($buffer, 0, 1));
$codec_mode_request = ($AMR_frame_header & 0x78) >> 3; // The 2nd bit through 5th bit (counting the most significant bit as the first bit) comprise the CMR (Codec Mode Request), values 0-7 being valid for AMR. The top bit of the CMR can actually be ignored, though it is used when AMR forms RTP payloads. The lower 3-bits of the header are reserved and are not used. Viewing the header from most significant bit to least significant bit, the encoding is XCCCCXXX, where Xs are reserved (typically 0) and the Cs are the CMR.
if ($codec_mode_request > 7) {
$info['error'][] = '';
break;
}
$thisfile_amr['frame_mode_count'][$codec_mode_request]++;
$buffer = substr($buffer, $this->amr_mode_bytes_per_frame($codec_mode_request));
} while (strlen($buffer) > 0);
$info['playtime_seconds'] = array_sum($thisfile_amr['frame_mode_count']) * 0.020; // each frame contain 160 samples and is 20 milliseconds long
$info['audio']['bitrate'] = (8 * ($info['avdataend'] - $info['avdataoffset'])) / $info['playtime_seconds']; // bitrate could be calculated from average bitrate by distributation of frame types. That would give effective audio bitrate, this gives overall file bitrate which will be a little bit higher since every frame will waste 8 bits for header, plus a few bits for octet padding
$info['bitrate'] = $info['audio']['bitrate'];
return true;
}
public function amr_mode_bitrate($key) {
static $amr_mode_bitrate = array(
0 => 4750,
1 => 5150,
2 => 5900,
3 => 6700,
4 => 7400,
5 => 7950,
6 => 10200,
7 => 12200,
);
return (isset($amr_mode_bitrate[$key]) ? $amr_mode_bitrate[$key] : false);
}
public function amr_mode_bytes_per_frame($key) {
static $amr_mode_bitrate = array(
0 => 13, // 1-byte frame header + 95 bits [padded to: 12 bytes] audio data
1 => 14, // 1-byte frame header + 103 bits [padded to: 13 bytes] audio data
2 => 16, // 1-byte frame header + 118 bits [padded to: 15 bytes] audio data
3 => 18, // 1-byte frame header + 134 bits [padded to: 17 bytes] audio data
4 => 20, // 1-byte frame header + 148 bits [padded to: 19 bytes] audio data
5 => 21, // 1-byte frame header + 159 bits [padded to: 20 bytes] audio data
6 => 27, // 1-byte frame header + 204 bits [padded to: 26 bytes] audio data
7 => 32, // 1-byte frame header + 244 bits [padded to: 31 bytes] audio data
);
return (isset($amr_mode_bitrate[$key]) ? $amr_mode_bitrate[$key] : false);
}
}

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,11 +18,11 @@
class getid3_au extends getid3_handler class getid3_au extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$AUheader = fread($this->getid3->fp, 8); $AUheader = $this->fread(8);
$magic = '.snd'; $magic = '.snd';
if (substr($AUheader, 0, 4) != $magic) { if (substr($AUheader, 0, 4) != $magic) {
@ -39,7 +40,7 @@ class getid3_au extends getid3_handler
$thisfile_au['encoding'] = 'ISO-8859-1'; $thisfile_au['encoding'] = 'ISO-8859-1';
$thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4)); $thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4));
$AUheader .= fread($this->getid3->fp, $thisfile_au['header_length'] - 8); $AUheader .= $this->fread($thisfile_au['header_length'] - 8);
$info['avdataoffset'] += $thisfile_au['header_length']; $info['avdataoffset'] += $thisfile_au['header_length'];
$thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4)); $thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4));
@ -69,7 +70,7 @@ class getid3_au extends getid3_handler
return true; return true;
} }
function AUdataFormatNameLookup($id) { public function AUdataFormatNameLookup($id) {
static $AUdataFormatNameLookup = array( static $AUdataFormatNameLookup = array(
0 => 'unspecified format', 0 => 'unspecified format',
1 => '8-bit mu-law', 1 => '8-bit mu-law',
@ -103,7 +104,7 @@ class getid3_au extends getid3_handler
return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false); return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false);
} }
function AUdataFormatBitsPerSampleLookup($id) { public function AUdataFormatBitsPerSampleLookup($id) {
static $AUdataFormatBitsPerSampleLookup = array( static $AUdataFormatBitsPerSampleLookup = array(
1 => 8, 1 => 8,
2 => 8, 2 => 8,
@ -131,7 +132,7 @@ class getid3_au extends getid3_handler
return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false); return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false);
} }
function AUdataFormatUsedBitsPerSampleLookup($id) { public function AUdataFormatUsedBitsPerSampleLookup($id) {
static $AUdataFormatUsedBitsPerSampleLookup = array( static $AUdataFormatUsedBitsPerSampleLookup = array(
1 => 8, 1 => 8,
2 => 8, 2 => 8,
@ -160,6 +161,3 @@ class getid3_au extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,7 +18,7 @@
class getid3_avr extends getid3_handler class getid3_avr extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// http://cui.unige.ch/OSG/info/AudioFormats/ap11.html // http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
@ -62,8 +63,8 @@ class getid3_avr extends getid3_handler
$info['fileformat'] = 'avr'; $info['fileformat'] = 'avr';
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$AVRheader = fread($this->getid3->fp, 128); $AVRheader = $this->fread(128);
$info['avr']['raw']['magic'] = substr($AVRheader, 0, 4); $info['avr']['raw']['magic'] = substr($AVRheader, 0, 4);
$magic = '2BIT'; $magic = '2BIT';
@ -122,6 +123,3 @@ class getid3_avr extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -16,7 +17,7 @@
class getid3_bonk extends getid3_handler class getid3_bonk extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// shortcut // shortcut
@ -33,13 +34,13 @@ class getid3_bonk extends getid3_handler
} else { } else {
// scan-from-end method, for v0.6 and higher // scan-from-end method, for v0.6 and higher
fseek($this->getid3->fp, $thisfile_bonk['dataend'] - 8, SEEK_SET); $this->fseek($thisfile_bonk['dataend'] - 8);
$PossibleBonkTag = fread($this->getid3->fp, 8); $PossibleBonkTag = $this->fread(8);
while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) { while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) {
$BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4)); $BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4));
fseek($this->getid3->fp, 0 - $BonkTagSize, SEEK_CUR); $this->fseek(0 - $BonkTagSize, SEEK_CUR);
$BonkTagOffset = ftell($this->getid3->fp); $BonkTagOffset = $this->ftell();
$TagHeaderTest = fread($this->getid3->fp, 5); $TagHeaderTest = $this->fread(5);
if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) { if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes("\x00".strtoupper(substr($PossibleBonkTag, 4, 4))).'" at offset '.$BonkTagOffset.', found "'.getid3_lib::PrintHexBytes($TagHeaderTest).'"'; $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes("\x00".strtoupper(substr($PossibleBonkTag, 4, 4))).'" at offset '.$BonkTagOffset.', found "'.getid3_lib::PrintHexBytes($TagHeaderTest).'"';
return false; return false;
@ -56,17 +57,17 @@ class getid3_bonk extends getid3_handler
} }
return true; return true;
} }
fseek($this->getid3->fp, $NextTagEndOffset, SEEK_SET); $this->fseek($NextTagEndOffset);
$PossibleBonkTag = fread($this->getid3->fp, 8); $PossibleBonkTag = $this->fread(8);
} }
} }
// seek-from-beginning method for v0.4 and v0.5 // seek-from-beginning method for v0.4 and v0.5
if (empty($thisfile_bonk['BONK'])) { if (empty($thisfile_bonk['BONK'])) {
fseek($this->getid3->fp, $thisfile_bonk['dataoffset'], SEEK_SET); $this->fseek($thisfile_bonk['dataoffset']);
do { do {
$TagHeaderTest = fread($this->getid3->fp, 5); $TagHeaderTest = $this->fread(5);
switch ($TagHeaderTest) { switch ($TagHeaderTest) {
case "\x00".'BONK': case "\x00".'BONK':
if (empty($info['audio']['encoder'])) { if (empty($info['audio']['encoder'])) {
@ -91,8 +92,8 @@ class getid3_bonk extends getid3_handler
// parse META block for v0.6 - v0.8 // parse META block for v0.6 - v0.8
if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) { if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) {
fseek($this->getid3->fp, $thisfile_bonk['META']['tags']['info'], SEEK_SET); $this->fseek($thisfile_bonk['META']['tags']['info']);
$TagHeaderTest = fread($this->getid3->fp, 5); $TagHeaderTest = $this->fread(5);
if ($TagHeaderTest == "\x00".'INFO') { if ($TagHeaderTest == "\x00".'INFO') {
$info['audio']['encoder'] = 'Extended BONK v0.6 - v0.8'; $info['audio']['encoder'] = 'Extended BONK v0.6 - v0.8';
@ -113,14 +114,14 @@ class getid3_bonk extends getid3_handler
} }
function HandleBonkTags($BonkTagName) { public function HandleBonkTags($BonkTagName) {
$info = &$this->getid3->info; $info = &$this->getid3->info;
switch ($BonkTagName) { switch ($BonkTagName) {
case 'BONK': case 'BONK':
// shortcut // shortcut
$thisfile_bonk_BONK = &$info['bonk']['BONK']; $thisfile_bonk_BONK = &$info['bonk']['BONK'];
$BonkData = "\x00".'BONK'.fread($this->getid3->fp, 17); $BonkData = "\x00".'BONK'.$this->fread(17);
$thisfile_bonk_BONK['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); $thisfile_bonk_BONK['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1));
$thisfile_bonk_BONK['number_samples'] = getid3_lib::LittleEndian2Int(substr($BonkData, 6, 4)); $thisfile_bonk_BONK['number_samples'] = getid3_lib::LittleEndian2Int(substr($BonkData, 6, 4));
$thisfile_bonk_BONK['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BonkData, 10, 4)); $thisfile_bonk_BONK['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BonkData, 10, 4));
@ -154,18 +155,18 @@ class getid3_bonk extends getid3_handler
// shortcut // shortcut
$thisfile_bonk_INFO = &$info['bonk']['INFO']; $thisfile_bonk_INFO = &$info['bonk']['INFO'];
$thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int(fread($this->getid3->fp, 1)); $thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int($this->fread(1));
$thisfile_bonk_INFO['entries_count'] = 0; $thisfile_bonk_INFO['entries_count'] = 0;
$NextInfoDataPair = fread($this->getid3->fp, 5); $NextInfoDataPair = $this->fread(5);
if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
while (!feof($this->getid3->fp)) { while (!feof($this->getid3->fp)) {
//$CurrentSeekInfo['offset'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4)); //$CurrentSeekInfo['offset'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4));
//$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1)); //$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1));
//$thisfile_bonk_INFO[] = $CurrentSeekInfo; //$thisfile_bonk_INFO[] = $CurrentSeekInfo;
$NextInfoDataPair = fread($this->getid3->fp, 5); $NextInfoDataPair = $this->fread(5);
if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
fseek($this->getid3->fp, -5, SEEK_CUR); $this->fseek(-5, SEEK_CUR);
break; break;
} }
$thisfile_bonk_INFO['entries_count']++; $thisfile_bonk_INFO['entries_count']++;
@ -174,10 +175,10 @@ class getid3_bonk extends getid3_handler
break; break;
case 'META': case 'META':
$BonkData = "\x00".'META'.fread($this->getid3->fp, $info['bonk']['META']['size'] - 5); $BonkData = "\x00".'META'.$this->fread($info['bonk']['META']['size'] - 5);
$info['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); $info['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1));
$MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA $MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA
$offset = 6; $offset = 6;
for ($i = 0; $i < $MetaTagEntries; $i++) { for ($i = 0; $i < $MetaTagEntries; $i++) {
$MetaEntryTagName = substr($BonkData, $offset, 4); $MetaEntryTagName = substr($BonkData, $offset, 4);
@ -212,7 +213,7 @@ class getid3_bonk extends getid3_handler
} }
} }
static function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) { public static function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) {
static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META'); static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META');
foreach ($BonkIsValidTagName as $validtagname) { foreach ($BonkIsValidTagName as $validtagname) {
if ($validtagname == $PossibleBonkTag) { if ($validtagname == $PossibleBonkTag) {
@ -225,6 +226,3 @@ class getid3_bonk extends getid3_handler
} }
} }
?>

View file

@ -0,0 +1,93 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio.dss.php //
// module for analyzing Digital Speech Standard (DSS) files //
// dependencies: NONE //
// ///
/////////////////////////////////////////////////////////////////
class getid3_dss extends getid3_handler
{
public function Analyze() {
$info = &$this->getid3->info;
$this->fseek($info['avdataoffset']);
$DSSheader = $this->fread(1540);
if (!preg_match('#^(\x02|\x03)ds[s2]#', $DSSheader)) {
$info['error'][] = 'Expecting "[02-03] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"';
return false;
}
// some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
$info['encoding'] = 'ISO-8859-1'; // not certain, but assumed
$info['dss'] = array();
$info['fileformat'] = 'dss';
$info['mime_type'] = 'audio/x-'.substr($DSSheader, 1, 3); // "audio/x-dss" or "audio/x-ds2"
$info['audio']['dataformat'] = substr($DSSheader, 1, 3); // "dss" or "ds2"
$info['audio']['bitrate_mode'] = 'cbr';
$info['dss']['version'] = ord(substr($DSSheader, 0, 1));
$info['dss']['hardware'] = trim(substr($DSSheader, 12, 16)); // identification string for hardware used to create the file, e.g. "DPM 9600", "DS2400"
$info['dss']['unknown1'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 28, 4));
// 32-37 = "FE FF FE FF F7 FF" in all the sample files I've seen
$info['dss']['date_create'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
$info['dss']['date_complete'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
$info['dss']['playtime_sec'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2)); // approximate file playtime in HHMMSS
$info['dss']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 512, 4)); // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512
$info['dss']['priority'] = ord(substr($DSSheader, 793, 1));
$info['dss']['comments'] = trim(substr($DSSheader, 798, 100));
$info['dss']['sample_rate_index'] = ord(substr($DSSheader, 1538, 1)); // this isn't certain, this may or may not be where the sample rate info is stored, but it seems consistent on my small selection of sample files
$info['audio']['bits_per_sample'] = 16; // maybe, maybe not -- most compressed audio formats don't have a fixed bits-per-sample value, but this is a reasonable approximation
$info['audio']['sample_rate'] = $this->DSSsampleRateLookup($info['dss']['sample_rate_index']);
$info['audio']['channels'] = 1;
$info['playtime_seconds'] = $info['dss']['playtime_ms'] / 1000;
if (floor($info['dss']['playtime_ms'] / 1000) != $info['dss']['playtime_sec']) {
// *should* just be playtime_ms / 1000 but at least one sample file has playtime_ms at offset 530 instead of offset 512, so safety check
$info['playtime_seconds'] = $info['dss']['playtime_sec'];
$this->getid3->warning('playtime_ms ('.number_format($info['dss']['playtime_ms'] / 1000, 3).') does not match playtime_sec ('.number_format($info['dss']['playtime_sec']).') - using playtime_sec value');
}
$info['audio']['bitrate'] = ($info['filesize'] * 8) / $info['playtime_seconds'];
return true;
}
public function DSSdateStringToUnixDate($datestring) {
$y = substr($datestring, 0, 2);
$m = substr($datestring, 2, 2);
$d = substr($datestring, 4, 2);
$h = substr($datestring, 6, 2);
$i = substr($datestring, 8, 2);
$s = substr($datestring, 10, 2);
$y += (($y < 95) ? 2000 : 1900);
return mktime($h, $i, $s, $m, $d, $y);
}
public function DSSsampleRateLookup($sample_rate_index) {
static $dssSampleRateLookup = array(
0x0A => 16000,
0x0C => 11025,
0x0D => 12000,
0x15 => 8000,
);
if (!array_key_exists($sample_rate_index, $dssSampleRateLookup)) {
$this->getid3->warning('unknown sample_rate_index: '.$sample_rate_index);
return false;
}
return $dssSampleRateLookup[$sample_rate_index];
}
}

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -14,70 +15,112 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
/**
* @tutorial http://wiki.multimedia.cx/index.php?title=DTS
*/
class getid3_dts extends getid3_handler class getid3_dts extends getid3_handler
{ {
/**
* Default DTS syncword used in native .cpt or .dts formats
*/
const syncword = "\x7F\xFE\x80\x01";
private $readBinDataOffset = 0;
/**
* Possible syncwords indicating bitstream encoding
*/
public static $syncwords = array(
0 => "\x7F\xFE\x80\x01", // raw big-endian
1 => "\xFE\x7F\x01\x80", // raw little-endian
2 => "\x1F\xFF\xE8\x00", // 14-bit big-endian
3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian
public function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// Specs taken from "DTS Coherent Acoustics;Core and Extensions, ETSI TS 102 114 V1.2.1 (2002-12)"
// (http://pda.etsi.org/pda/queryform.asp)
// With thanks to Gambit <macteam@users.sourceforge.net> http://mac.sourceforge.net/atl/
$info['fileformat'] = 'dts'; $info['fileformat'] = 'dts';
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$DTSheader = fread($this->getid3->fp, 16); $DTSheader = $this->fread(20); // we only need 2 words magic + 6 words frame header, but these words may be normal 16-bit words OR 14-bit words with 2 highest bits set to zero, so 8 words can be either 8*16/8 = 16 bytes OR 8*16*(16/14)/8 = 18.3 bytes
$info['dts']['raw']['magic'] = substr($DTSheader, 0, 4);
// check syncword
$sync = substr($DTSheader, 0, 4);
if (($encoding = array_search($sync, self::$syncwords)) !== false) {
$info['dts']['raw']['magic'] = $sync;
$this->readBinDataOffset = 32;
} elseif ($this->isDependencyFor('matroska')) {
// Matroska contains DTS without syncword encoded as raw big-endian format
$encoding = 0;
$this->readBinDataOffset = 0;
} else {
unset($info['fileformat']);
return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"');
$magic = "\x7F\xFE\x80\x01";
if ($info['dts']['raw']['magic'] != $magic) {
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['dts']['raw']['magic']).'"';
unset($info['fileformat'], $info['dts']);
return false;
} }
$fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, 4, 12)); // decode header
$bsOffset = 0; $fhBS = '';
$info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, $bsOffset, 1); for ($word_offset = 0; $word_offset <= strlen($DTSheader); $word_offset += 2) {
$info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, $bsOffset, 5); switch ($encoding) {
$info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); case 0: // raw big-endian
$info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, $bsOffset, 7); $fhBS .= getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) );
$info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, $bsOffset, 14); break;
$info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, $bsOffset, 6); case 1: // raw little-endian
$info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, $bsOffset, 4); $fhBS .= getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2)));
$info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, $bsOffset, 5); break;
$info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); case 2: // 14-bit big-endian
$info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $fhBS .= substr(getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) ), 2, 14);
$info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); break;
$info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); case 3: // 14-bit little-endian
$info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $fhBS .= substr(getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))), 2, 14);
$info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, $bsOffset, 3); break;
$info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); }
$info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); }
$info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, $bsOffset, 2);
$info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, 1);
$info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, 5);
$info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, 7);
$info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, 14);
$info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, 6);
$info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, 4);
$info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, 5);
$info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, 3);
$info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, 2);
$info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, 1);
if ($info['dts']['flags']['crc_present']) { if ($info['dts']['flags']['crc_present']) {
$info['dts']['raw']['crc16'] = $this->readBinData($fhBS, $bsOffset, 16); $info['dts']['raw']['crc16'] = $this->readBinData($fhBS, 16);
} }
$info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, $bsOffset, 4); $info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, 4);
$info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, $bsOffset, 2); $info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, 2);
$info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, $bsOffset, 2); $info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, 2);
$info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, $bsOffset, 1); $info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
$info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, $bsOffset, 4); $info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, 4);
$info['dts']['bitrate'] = self::DTSbitrateLookup($info['dts']['raw']['bitrate']); $info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']);
$info['dts']['bits_per_sample'] = self::DTSbitPerSampleLookup($info['dts']['raw']['bits_per_sample']); $info['dts']['bits_per_sample'] = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']);
$info['dts']['sample_rate'] = self::DTSsampleRateLookup($info['dts']['raw']['sample_frequency']); $info['dts']['sample_rate'] = self::sampleRateLookup($info['dts']['raw']['sample_frequency']);
$info['dts']['dialog_normalization'] = self::DTSdialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']); $info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']);
$info['dts']['flags']['lossless'] = (($info['dts']['raw']['bitrate'] == 31) ? true : false); $info['dts']['flags']['lossless'] = (($info['dts']['raw']['bitrate'] == 31) ? true : false);
$info['dts']['bitrate_mode'] = (($info['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr'); $info['dts']['bitrate_mode'] = (($info['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr');
$info['dts']['channels'] = self::DTSnumChannelsLookup($info['dts']['raw']['channel_arrangement']); $info['dts']['channels'] = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']);
$info['dts']['channel_arrangement'] = self::DTSchannelArrangementLookup($info['dts']['raw']['channel_arrangement']); $info['dts']['channel_arrangement'] = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']);
$info['audio']['dataformat'] = 'dts'; $info['audio']['dataformat'] = 'dts';
$info['audio']['lossless'] = $info['dts']['flags']['lossless']; $info['audio']['lossless'] = $info['dts']['flags']['lossless'];
@ -86,21 +129,25 @@ class getid3_dts extends getid3_handler
$info['audio']['sample_rate'] = $info['dts']['sample_rate']; $info['audio']['sample_rate'] = $info['dts']['sample_rate'];
$info['audio']['channels'] = $info['dts']['channels']; $info['audio']['channels'] = $info['dts']['channels'];
$info['audio']['bitrate'] = $info['dts']['bitrate']; $info['audio']['bitrate'] = $info['dts']['bitrate'];
if (isset($info['avdataend'])) { if (isset($info['avdataend']) && !empty($info['dts']['bitrate']) && is_numeric($info['dts']['bitrate'])) {
$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8); $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8);
if (($encoding == 2) || ($encoding == 3)) {
// 14-bit data packed into 16-bit words, so the playtime is wrong because only (14/16) of the bytes in the data portion of the file are used at the specified bitrate
$info['playtime_seconds'] *= (14 / 16);
}
} }
return true; return true;
} }
private function readBinData($bin, &$offset, $length) { private function readBinData($bin, $length) {
$data = substr($bin, $offset, $length); $data = substr($bin, $this->readBinDataOffset, $length);
$offset += $length; $this->readBinDataOffset += $length;
return bindec($data); return bindec($data);
} }
private static function DTSbitrateLookup($index) { public static function bitrateLookup($index) {
$DTSbitrateLookup = array( static $lookup = array(
0 => 32000, 0 => 32000,
1 => 56000, 1 => 56000,
2 => 64000, 2 => 64000,
@ -132,13 +179,13 @@ class getid3_dts extends getid3_handler
28 => 3840000, 28 => 3840000,
29 => 'open', 29 => 'open',
30 => 'variable', 30 => 'variable',
31 => 'lossless' 31 => 'lossless',
); );
return (isset($DTSbitrateLookup[$index]) ? $DTSbitrateLookup[$index] : false); return (isset($lookup[$index]) ? $lookup[$index] : false);
} }
private static function DTSsampleRateLookup($index) { public static function sampleRateLookup($index) {
$DTSsampleRateLookup = array( static $lookup = array(
0 => 'invalid', 0 => 'invalid',
1 => 8000, 1 => 8000,
2 => 16000, 2 => 16000,
@ -154,22 +201,22 @@ class getid3_dts extends getid3_handler
12 => 24000, 12 => 24000,
13 => 48000, 13 => 48000,
14 => 'invalid', 14 => 'invalid',
15 => 'invalid' 15 => 'invalid',
); );
return (isset($DTSsampleRateLookup[$index]) ? $DTSsampleRateLookup[$index] : false); return (isset($lookup[$index]) ? $lookup[$index] : false);
} }
private static function DTSbitPerSampleLookup($index) { public static function bitPerSampleLookup($index) {
$DTSbitPerSampleLookup = array( static $lookup = array(
0 => 16, 0 => 16,
1 => 20, 1 => 20,
2 => 24, 2 => 24,
3 => 24, 3 => 24,
); );
return (isset($DTSbitPerSampleLookup[$index]) ? $DTSbitPerSampleLookup[$index] : false); return (isset($lookup[$index]) ? $lookup[$index] : false);
} }
private static function DTSnumChannelsLookup($index) { public static function numChannelsLookup($index) {
switch ($index) { switch ($index) {
case 0: case 0:
return 1; return 1;
@ -207,8 +254,8 @@ class getid3_dts extends getid3_handler
return false; return false;
} }
private static function DTSchannelArrangementLookup($index) { public static function channelArrangementLookup($index) {
$DTSchannelArrangementLookup = array( static $lookup = array(
0 => 'A', 0 => 'A',
1 => 'A + B (dual mono)', 1 => 'A + B (dual mono)',
2 => 'L + R (stereo)', 2 => 'L + R (stereo)',
@ -226,10 +273,10 @@ class getid3_dts extends getid3_handler
14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2', 14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2',
15 => 'CL + C+ CR + L + R + SL + S + SR', 15 => 'CL + C+ CR + L + R + SL + S + SR',
); );
return (isset($DTSchannelArrangementLookup[$index]) ? $DTSchannelArrangementLookup[$index] : 'user-defined'); return (isset($lookup[$index]) ? $lookup[$index] : 'user-defined');
} }
private static function DTSdialogNormalization($index, $version) { public static function dialogNormalization($index, $version) {
switch ($version) { switch ($version) {
case 7: case 7:
return 0 - $index; return 0 - $index;
@ -242,5 +289,3 @@ class getid3_dts extends getid3_handler
} }
} }
?>

View file

@ -0,0 +1,453 @@
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// See readme.txt for more details //
/////////////////////////////////////////////////////////////////
// //
// module.audio.flac.php //
// module for analyzing FLAC and OggFLAC audio files //
// dependencies: module.audio.ogg.php //
// ///
/////////////////////////////////////////////////////////////////
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
/**
* @tutorial http://flac.sourceforge.net/format.html
*/
class getid3_flac extends getid3_handler
{
const syncword = 'fLaC';
public function Analyze() {
$info = &$this->getid3->info;
$this->fseek($info['avdataoffset']);
$StreamMarker = $this->fread(4);
if ($StreamMarker != self::syncword) {
return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($StreamMarker).'"');
}
$info['fileformat'] = 'flac';
$info['audio']['dataformat'] = 'flac';
$info['audio']['bitrate_mode'] = 'vbr';
$info['audio']['lossless'] = true;
// parse flac container
return $this->parseMETAdata();
}
public function parseMETAdata() {
$info = &$this->getid3->info;
do {
$BlockOffset = $this->ftell();
$BlockHeader = $this->fread(4);
$LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1));
$LastBlockFlag = (bool) ($LBFBT & 0x80);
$BlockType = ($LBFBT & 0x7F);
$BlockLength = getid3_lib::BigEndian2Int(substr($BlockHeader, 1, 3));
$BlockTypeText = self::metaBlockTypeLookup($BlockType);
if (($BlockOffset + 4 + $BlockLength) > $info['avdataend']) {
$this->error('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file');
break;
}
if ($BlockLength < 1) {
$this->error('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockLength.') at offset '.$BlockOffset.' is invalid');
break;
}
$info['flac'][$BlockTypeText]['raw'] = array();
$BlockTypeText_raw = &$info['flac'][$BlockTypeText]['raw'];
$BlockTypeText_raw['offset'] = $BlockOffset;
$BlockTypeText_raw['last_meta_block'] = $LastBlockFlag;
$BlockTypeText_raw['block_type'] = $BlockType;
$BlockTypeText_raw['block_type_text'] = $BlockTypeText;
$BlockTypeText_raw['block_length'] = $BlockLength;
if ($BlockTypeText_raw['block_type'] != 0x06) { // do not read attachment data automatically
$BlockTypeText_raw['block_data'] = $this->fread($BlockLength);
}
switch ($BlockTypeText) {
case 'STREAMINFO': // 0x00
if (!$this->parseSTREAMINFO($BlockTypeText_raw['block_data'])) {
return false;
}
break;
case 'PADDING': // 0x01
unset($info['flac']['PADDING']); // ignore
break;
case 'APPLICATION': // 0x02
if (!$this->parseAPPLICATION($BlockTypeText_raw['block_data'])) {
return false;
}
break;
case 'SEEKTABLE': // 0x03
if (!$this->parseSEEKTABLE($BlockTypeText_raw['block_data'])) {
return false;
}
break;
case 'VORBIS_COMMENT': // 0x04
if (!$this->parseVORBIS_COMMENT($BlockTypeText_raw['block_data'])) {
return false;
}
break;
case 'CUESHEET': // 0x05
if (!$this->parseCUESHEET($BlockTypeText_raw['block_data'])) {
return false;
}
break;
case 'PICTURE': // 0x06
if (!$this->parsePICTURE()) {
return false;
}
break;
default:
$this->warning('Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockType.') at offset '.$BlockOffset);
}
unset($info['flac'][$BlockTypeText]['raw']);
$info['avdataoffset'] = $this->ftell();
}
while ($LastBlockFlag === false);
// handle tags
if (!empty($info['flac']['VORBIS_COMMENT']['comments'])) {
$info['flac']['comments'] = $info['flac']['VORBIS_COMMENT']['comments'];
}
if (!empty($info['flac']['VORBIS_COMMENT']['vendor'])) {
$info['audio']['encoder'] = str_replace('reference ', '', $info['flac']['VORBIS_COMMENT']['vendor']);
}
// copy attachments to 'comments' array if nesesary
if (isset($info['flac']['PICTURE']) && ($this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE)) {
foreach ($info['flac']['PICTURE'] as $entry) {
if (!empty($entry['data'])) {
if (!isset($info['flac']['comments']['picture'])) {
$info['flac']['comments']['picture'] = array();
}
$comments_picture_data = array();
foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
if (isset($entry[$picture_key])) {
$comments_picture_data[$picture_key] = $entry[$picture_key];
}
}
$info['flac']['comments']['picture'][] = $comments_picture_data;
unset($comments_picture_data);
}
}
}
if (isset($info['flac']['STREAMINFO'])) {
if (!$this->isDependencyFor('matroska')) {
$info['flac']['compressed_audio_bytes'] = $info['avdataend'] - $info['avdataoffset'];
}
$info['flac']['uncompressed_audio_bytes'] = $info['flac']['STREAMINFO']['samples_stream'] * $info['flac']['STREAMINFO']['channels'] * ($info['flac']['STREAMINFO']['bits_per_sample'] / 8);
if ($info['flac']['uncompressed_audio_bytes'] == 0) {
return $this->error('Corrupt FLAC file: uncompressed_audio_bytes == zero');
}
if (!empty($info['flac']['compressed_audio_bytes'])) {
$info['flac']['compression_ratio'] = $info['flac']['compressed_audio_bytes'] / $info['flac']['uncompressed_audio_bytes'];
}
}
// set md5_data_source - built into flac 0.5+
if (isset($info['flac']['STREAMINFO']['audio_signature'])) {
if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
$this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
}
else {
$info['md5_data_source'] = '';
$md5 = $info['flac']['STREAMINFO']['audio_signature'];
for ($i = 0; $i < strlen($md5); $i++) {
$info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT);
}
if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
unset($info['md5_data_source']);
}
}
}
if (isset($info['flac']['STREAMINFO']['bits_per_sample'])) {
$info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
if ($info['audio']['bits_per_sample'] == 8) {
// special case
// must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
// MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
$this->warning('FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file');
}
}
return true;
}
private function parseSTREAMINFO($BlockData) {
$info = &$this->getid3->info;
$info['flac']['STREAMINFO'] = array();
$streaminfo = &$info['flac']['STREAMINFO'];
$streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2));
$streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2));
$streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3));
$streaminfo['max_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 7, 3));
$SRCSBSS = getid3_lib::BigEndian2Bin(substr($BlockData, 10, 8));
$streaminfo['sample_rate'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 0, 20));
$streaminfo['channels'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 20, 3)) + 1;
$streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1;
$streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36));
$streaminfo['audio_signature'] = substr($BlockData, 18, 16);
if (!empty($streaminfo['sample_rate'])) {
$info['audio']['bitrate_mode'] = 'vbr';
$info['audio']['sample_rate'] = $streaminfo['sample_rate'];
$info['audio']['channels'] = $streaminfo['channels'];
$info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample'];
$info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate'];
if ($info['playtime_seconds'] > 0) {
if (!$this->isDependencyFor('matroska')) {
$info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
}
else {
$this->warning('Cannot determine audio bitrate because total stream size is unknown');
}
}
} else {
return $this->error('Corrupt METAdata block: STREAMINFO');
}
return true;
}
private function parseAPPLICATION($BlockData) {
$info = &$this->getid3->info;
$ApplicationID = getid3_lib::BigEndian2Int(substr($BlockData, 0, 4));
$info['flac']['APPLICATION'][$ApplicationID]['name'] = self::applicationIDLookup($ApplicationID);
$info['flac']['APPLICATION'][$ApplicationID]['data'] = substr($BlockData, 4);
return true;
}
private function parseSEEKTABLE($BlockData) {
$info = &$this->getid3->info;
$offset = 0;
$BlockLength = strlen($BlockData);
$placeholderpattern = str_repeat("\xFF", 8);
while ($offset < $BlockLength) {
$SampleNumberString = substr($BlockData, $offset, 8);
$offset += 8;
if ($SampleNumberString == $placeholderpattern) {
// placeholder point
getid3_lib::safe_inc($info['flac']['SEEKTABLE']['placeholders'], 1);
$offset += 10;
} else {
$SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
$info['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
$offset += 8;
$info['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 2));
$offset += 2;
}
}
return true;
}
private function parseVORBIS_COMMENT($BlockData) {
$info = &$this->getid3->info;
$getid3_ogg = new getid3_ogg($this->getid3);
if ($this->isDependencyFor('matroska')) {
$getid3_ogg->setStringMode($this->data_string);
}
$getid3_ogg->ParseVorbisComments();
if (isset($info['ogg'])) {
unset($info['ogg']['comments_raw']);
$info['flac']['VORBIS_COMMENT'] = $info['ogg'];
unset($info['ogg']);
}
unset($getid3_ogg);
return true;
}
private function parseCUESHEET($BlockData) {
$info = &$this->getid3->info;
$offset = 0;
$info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($BlockData, $offset, 128), "\0");
$offset += 128;
$info['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
$offset += 8;
$info['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)) & 0x80);
$offset += 1;
$offset += 258; // reserved
$info['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
$offset += 1;
for ($track = 0; $track < $info['flac']['CUESHEET']['number_tracks']; $track++) {
$TrackSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
$offset += 8;
$TrackNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
$offset += 1;
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($BlockData, $offset, 12);
$offset += 12;
$TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
$offset += 1;
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
$offset += 13; // reserved
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
$offset += 1;
for ($index = 0; $index < $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
$IndexSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
$offset += 8;
$IndexNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
$offset += 1;
$offset += 3; // reserved
$info['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
}
}
return true;
}
/**
* Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
* External usage: audio.ogg
*/
public function parsePICTURE() {
$info = &$this->getid3->info;
$picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['picturetype'] = self::pictureTypeLookup($picture['typeid']);
$picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4)));
$descr_length = getid3_lib::BigEndian2Int($this->fread(4));
if ($descr_length) {
$picture['description'] = $this->fread($descr_length);
}
$picture['image_width'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['image_height'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['datalength'] = getid3_lib::BigEndian2Int($this->fread(4));
if ($picture['image_mime'] == '-->') {
$picture['data'] = $this->fread($picture['datalength']);
} else {
$picture['data'] = $this->saveAttachment(
str_replace('/', '_', $picture['picturetype']).'_'.$this->ftell(),
$this->ftell(),
$picture['datalength'],
$picture['image_mime']);
}
$info['flac']['PICTURE'][] = $picture;
return true;
}
public static function metaBlockTypeLookup($blocktype) {
static $lookup = array(
0 => 'STREAMINFO',
1 => 'PADDING',
2 => 'APPLICATION',
3 => 'SEEKTABLE',
4 => 'VORBIS_COMMENT',
5 => 'CUESHEET',
6 => 'PICTURE',
);
return (isset($lookup[$blocktype]) ? $lookup[$blocktype] : 'reserved');
}
public static function applicationIDLookup($applicationid) {
// http://flac.sourceforge.net/id.html
static $lookup = array(
0x41544348 => 'FlacFile', // "ATCH"
0x42534F4C => 'beSolo', // "BSOL"
0x42554753 => 'Bugs Player', // "BUGS"
0x43756573 => 'GoldWave cue points (specification)', // "Cues"
0x46696361 => 'CUE Splitter', // "Fica"
0x46746F6C => 'flac-tools', // "Ftol"
0x4D4F5442 => 'MOTB MetaCzar', // "MOTB"
0x4D505345 => 'MP3 Stream Editor', // "MPSE"
0x4D754D4C => 'MusicML: Music Metadata Language', // "MuML"
0x52494646 => 'Sound Devices RIFF chunk storage', // "RIFF"
0x5346464C => 'Sound Font FLAC', // "SFFL"
0x534F4E59 => 'Sony Creative Software', // "SONY"
0x5351455A => 'flacsqueeze', // "SQEZ"
0x54745776 => 'TwistedWave', // "TtWv"
0x55495453 => 'UITS Embedding tools', // "UITS"
0x61696666 => 'FLAC AIFF chunk storage', // "aiff"
0x696D6167 => 'flac-image application for storing arbitrary files in APPLICATION metadata blocks', // "imag"
0x7065656D => 'Parseable Embedded Extensible Metadata (specification)', // "peem"
0x71667374 => 'QFLAC Studio', // "qfst"
0x72696666 => 'FLAC RIFF chunk storage', // "riff"
0x74756E65 => 'TagTuner', // "tune"
0x78626174 => 'XBAT', // "xbat"
0x786D6364 => 'xmcd', // "xmcd"
);
return (isset($lookup[$applicationid]) ? $lookup[$applicationid] : 'reserved');
}
public static function pictureTypeLookup($type_id) {
static $lookup = array (
0 => 'Other',
1 => '32x32 pixels \'file icon\' (PNG only)',
2 => 'Other file icon',
3 => 'Cover (front)',
4 => 'Cover (back)',
5 => 'Leaflet page',
6 => 'Media (e.g. label side of CD)',
7 => 'Lead artist/lead performer/soloist',
8 => 'Artist/performer',
9 => 'Conductor',
10 => 'Band/Orchestra',
11 => 'Composer',
12 => 'Lyricist/text writer',
13 => 'Recording Location',
14 => 'During recording',
15 => 'During performance',
16 => 'Movie/video screen capture',
17 => 'A bright coloured fish',
18 => 'Illustration',
19 => 'Band/artist logotype',
20 => 'Publisher/Studio logotype',
);
return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
}
}

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -18,12 +19,12 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php',
class getid3_la extends getid3_handler class getid3_la extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$offset = 0; $offset = 0;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$rawdata = fread($this->getid3->fp, $this->getid3->fread_buffer_size()); $rawdata = $this->fread($this->getid3->fread_buffer_size());
switch (substr($rawdata, $offset, 4)) { switch (substr($rawdata, $offset, 4)) {
case 'LA02': case 'LA02':
@ -112,7 +113,7 @@ class getid3_la extends getid3_handler
$info['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); $info['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
$offset += 4; $offset += 4;
// mikeØbevin*de // mikeØbevin*de
// Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16 // Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16
// in earlier versions. A seekpoint is added every blocksize * seekevery // in earlier versions. A seekpoint is added every blocksize * seekevery
// samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should // samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should
@ -166,8 +167,8 @@ class getid3_la extends getid3_handler
$RIFFdata .= substr($rawdata, 16, 24); $RIFFdata .= substr($rawdata, 16, 24);
} }
if ($info['la']['footerstart'] < $info['avdataend']) { if ($info['la']['footerstart'] < $info['avdataend']) {
fseek($this->getid3->fp, $info['la']['footerstart'], SEEK_SET); $this->fseek($info['la']['footerstart']);
$RIFFdata .= fread($this->getid3->fp, $info['avdataend'] - $info['la']['footerstart']); $RIFFdata .= $this->fread($info['avdataend'] - $info['la']['footerstart']);
} }
$RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata; $RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata;
fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata)); fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata));
@ -193,7 +194,6 @@ class getid3_la extends getid3_handler
$info['avdataend'] = $info['avdataoffset'] + $info['la']['footerstart']; $info['avdataend'] = $info['avdataoffset'] + $info['la']['footerstart'];
$info['avdataoffset'] = $info['avdataoffset'] + $offset; $info['avdataoffset'] = $info['avdataoffset'] + $offset;
//$info['la']['codec'] = RIFFwFormatTagLookup($info['la']['raw']['format']);
$info['la']['compression_ratio'] = (float) (($info['avdataend'] - $info['avdataoffset']) / $info['la']['uncompressed_size']); $info['la']['compression_ratio'] = (float) (($info['avdataend'] - $info['avdataoffset']) / $info['la']['uncompressed_size']);
$info['playtime_seconds'] = (float) ($info['la']['samples'] / $info['la']['sample_rate']) / $info['la']['channels']; $info['playtime_seconds'] = (float) ($info['la']['samples'] / $info['la']['sample_rate']) / $info['la']['channels'];
if ($info['playtime_seconds'] == 0) { if ($info['playtime_seconds'] == 0) {
@ -224,6 +224,3 @@ class getid3_la extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -18,11 +19,11 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php',
class getid3_lpac extends getid3_handler class getid3_lpac extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$LPACheader = fread($this->getid3->fp, 14); $LPACheader = $this->fread(14);
if (substr($LPACheader, 0, 4) != 'LPAC') { if (substr($LPACheader, 0, 4) != 'LPAC') {
$info['error'][] = 'Expected "LPAC" at offset '.$info['avdataoffset'].', found "'.$StreamMarker.'"'; $info['error'][] = 'Expected "LPAC" at offset '.$info['avdataoffset'].', found "'.$StreamMarker.'"';
return false; return false;
@ -125,6 +126,3 @@ class getid3_lpac extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -18,9 +19,9 @@ define('GETID3_MIDI_MAGIC_MTRK', 'MTrk'); // MIDI track header magic
class getid3_midi extends getid3_handler class getid3_midi extends getid3_handler
{ {
var $scanwholefile = true; public $scanwholefile = true;
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// shortcut // shortcut
@ -31,8 +32,8 @@ class getid3_midi extends getid3_handler
$info['fileformat'] = 'midi'; $info['fileformat'] = 'midi';
$info['audio']['dataformat'] = 'midi'; $info['audio']['dataformat'] = 'midi';
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$MIDIdata = fread($this->getid3->fp, $this->getid3->fread_buffer_size()); $MIDIdata = $this->fread($this->getid3->fread_buffer_size());
$offset = 0; $offset = 0;
$MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd' $MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd'
if ($MIDIheaderID != GETID3_MIDI_MAGIC_MTHD) { if ($MIDIheaderID != GETID3_MIDI_MAGIC_MTHD) {
@ -52,14 +53,20 @@ class getid3_midi extends getid3_handler
for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) { for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) {
while ((strlen($MIDIdata) - $offset) < 8) { while ((strlen($MIDIdata) - $offset) < 8) {
$MIDIdata .= fread($this->getid3->fp, $this->getid3->fread_buffer_size()); if ($buffer = $this->fread($this->getid3->fread_buffer_size())) {
$MIDIdata .= $buffer;
} else {
$info['warning'][] = 'only processed '.($i - 1).' of '.$thisfile_midi_raw['tracks'].' tracks';
$info['error'][] = 'Unabled to read more file data at '.$this->ftell().' (trying to seek to : '.$offset.'), was expecting at least 8 more bytes';
return false;
}
} }
$trackID = substr($MIDIdata, $offset, 4); $trackID = substr($MIDIdata, $offset, 4);
$offset += 4; $offset += 4;
if ($trackID == GETID3_MIDI_MAGIC_MTRK) { if ($trackID == GETID3_MIDI_MAGIC_MTRK) {
$tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4)); $tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
$offset += 4; $offset += 4;
// $thisfile_midi['tracks'][$i]['size'] = $tracksize; //$thisfile_midi['tracks'][$i]['size'] = $tracksize;
$trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize); $trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize);
$offset += $tracksize; $offset += $tracksize;
} else { } else {
@ -322,7 +329,7 @@ class getid3_midi extends getid3_handler
return true; return true;
} }
function GeneralMIDIinstrumentLookup($instrumentid) { public function GeneralMIDIinstrumentLookup($instrumentid) {
$begin = __LINE__; $begin = __LINE__;
@ -462,7 +469,7 @@ class getid3_midi extends getid3_handler
return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument'); return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument');
} }
function GeneralMIDIpercussionLookup($instrumentid) { public function GeneralMIDIpercussionLookup($instrumentid) {
$begin = __LINE__; $begin = __LINE__;
@ -521,6 +528,3 @@ class getid3_midi extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,10 +18,10 @@
class getid3_mod extends getid3_handler class getid3_mod extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$fileheader = fread($this->getid3->fp, 1088); $fileheader = $this->fread(1088);
if (preg_match('#^IMPM#', $fileheader)) { if (preg_match('#^IMPM#', $fileheader)) {
return $this->getITheaderFilepointer(); return $this->getITheaderFilepointer();
} elseif (preg_match('#^Extended Module#', $fileheader)) { } elseif (preg_match('#^Extended Module#', $fileheader)) {
@ -35,10 +36,10 @@ class getid3_mod extends getid3_handler
} }
function getMODheaderFilepointer() { public function getMODheaderFilepointer() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'] + 1080); $this->fseek($info['avdataoffset'] + 1080);
$FormatID = fread($this->getid3->fp, 4); $FormatID = $this->fread(4);
if (!preg_match('#^(M.K.|[5-9]CHN|[1-3][0-9]CH)$#', $FormatID)) { if (!preg_match('#^(M.K.|[5-9]CHN|[1-3][0-9]CH)$#', $FormatID)) {
$info['error'][] = 'This is not a known type of MOD file'; $info['error'][] = 'This is not a known type of MOD file';
return false; return false;
@ -50,10 +51,10 @@ class getid3_mod extends getid3_handler
return false; return false;
} }
function getXMheaderFilepointer() { public function getXMheaderFilepointer() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset']); $this->fseek($info['avdataoffset']);
$FormatID = fread($this->getid3->fp, 15); $FormatID = $this->fread(15);
if (!preg_match('#^Extended Module$#', $FormatID)) { if (!preg_match('#^Extended Module$#', $FormatID)) {
$info['error'][] = 'This is not a known type of XM-MOD file'; $info['error'][] = 'This is not a known type of XM-MOD file';
return false; return false;
@ -65,10 +66,10 @@ class getid3_mod extends getid3_handler
return false; return false;
} }
function getS3MheaderFilepointer() { public function getS3MheaderFilepointer() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'] + 44); $this->fseek($info['avdataoffset'] + 44);
$FormatID = fread($this->getid3->fp, 4); $FormatID = $this->fread(4);
if (!preg_match('#^SCRM$#', $FormatID)) { if (!preg_match('#^SCRM$#', $FormatID)) {
$info['error'][] = 'This is not a ScreamTracker MOD file'; $info['error'][] = 'This is not a ScreamTracker MOD file';
return false; return false;
@ -80,10 +81,10 @@ class getid3_mod extends getid3_handler
return false; return false;
} }
function getITheaderFilepointer() { public function getITheaderFilepointer() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset']); $this->fseek($info['avdataoffset']);
$FormatID = fread($this->getid3->fp, 4); $FormatID = $this->fread(4);
if (!preg_match('#^IMPM$#', $FormatID)) { if (!preg_match('#^IMPM$#', $FormatID)) {
$info['error'][] = 'This is not an ImpulseTracker MOD file'; $info['error'][] = 'This is not an ImpulseTracker MOD file';
return false; return false;
@ -96,6 +97,3 @@ class getid3_mod extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,10 +18,10 @@
class getid3_monkey extends getid3_handler class getid3_monkey extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// based loosely on code from TMonkey by Jurgen Faul <jfaulØgmx*de> // based loosely on code from TMonkey by Jurgen Faul <jfaulØgmx*de>
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
$info['fileformat'] = 'mac'; $info['fileformat'] = 'mac';
@ -32,8 +33,8 @@ class getid3_monkey extends getid3_handler
$thisfile_monkeysaudio = &$info['monkeys_audio']; $thisfile_monkeysaudio = &$info['monkeys_audio'];
$thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw']; $thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw'];
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$MACheaderData = fread($this->getid3->fp, 74); $MACheaderData = $this->fread(74);
$thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4); $thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4);
$magic = 'MAC '; $magic = 'MAC ';
@ -176,7 +177,7 @@ class getid3_monkey extends getid3_handler
return true; return true;
} }
function MonkeyCompressionLevelNameLookup($compressionlevel) { public function MonkeyCompressionLevelNameLookup($compressionlevel) {
static $MonkeyCompressionLevelNameLookup = array( static $MonkeyCompressionLevelNameLookup = array(
0 => 'unknown', 0 => 'unknown',
1000 => 'fast', 1000 => 'fast',
@ -188,7 +189,7 @@ class getid3_monkey extends getid3_handler
return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid'); return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid');
} }
function MonkeySamplesPerFrame($versionid, $compressionlevel) { public function MonkeySamplesPerFrame($versionid, $compressionlevel) {
if ($versionid >= 3950) { if ($versionid >= 3950) {
return 73728 * 4; return 73728 * 4;
} elseif ($versionid >= 3900) { } elseif ($versionid >= 3900) {
@ -201,5 +202,3 @@ class getid3_monkey extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -24,9 +25,9 @@ define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
class getid3_mp3 extends getid3_handler class getid3_mp3 extends getid3_handler
{ {
var $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$initialOffset = $info['avdataoffset']; $initialOffset = $info['avdataoffset'];
@ -95,8 +96,8 @@ class getid3_mp3 extends getid3_handler
// Not sure what version of LAME this is - look in padding of last frame for longer version string // Not sure what version of LAME this is - look in padding of last frame for longer version string
$PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength; $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
fseek($this->getid3->fp, $PossibleLAMEversionStringOffset); $this->fseek($PossibleLAMEversionStringOffset);
$PossiblyLongerLAMEversion_Data = fread($this->getid3->fp, $PossiblyLongerLAMEversion_FrameLength); $PossiblyLongerLAMEversion_Data = $this->fread($PossiblyLongerLAMEversion_FrameLength);
switch (substr($CurrentDataLAMEversionString, -1)) { switch (substr($CurrentDataLAMEversionString, -1)) {
case 'a': case 'a':
case 'b': case 'b':
@ -160,7 +161,7 @@ class getid3_mp3 extends getid3_handler
} }
function GuessEncoderOptions() { public function GuessEncoderOptions() {
// shortcuts // shortcuts
$info = &$this->getid3->info; $info = &$this->getid3->info;
if (!empty($info['mpeg']['audio'])) { if (!empty($info['mpeg']['audio'])) {
@ -404,7 +405,7 @@ class getid3_mp3 extends getid3_handler
} }
function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) { public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
static $MPEGaudioVersionLookup; static $MPEGaudioVersionLookup;
static $MPEGaudioLayerLookup; static $MPEGaudioLayerLookup;
static $MPEGaudioBitrateLookup; static $MPEGaudioBitrateLookup;
@ -413,21 +414,21 @@ class getid3_mp3 extends getid3_handler
static $MPEGaudioModeExtensionLookup; static $MPEGaudioModeExtensionLookup;
static $MPEGaudioEmphasisLookup; static $MPEGaudioEmphasisLookup;
if (empty($MPEGaudioVersionLookup)) { if (empty($MPEGaudioVersionLookup)) {
$MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
$MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
$MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
$MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
$MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
$MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
$MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
} }
if (fseek($this->getid3->fp, $offset, SEEK_SET) != 0) { if ($this->fseek($offset) != 0) {
$info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset; $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
return false; return false;
} }
//$headerstring = fread($this->getid3->fp, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame //$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
$headerstring = fread($this->getid3->fp, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data $headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
// MP3 audio frame structure: // MP3 audio frame structure:
// $aa $aa $aa $aa [$bb $bb] $cc... // $aa $aa $aa $aa [$bb $bb] $cc...
@ -441,14 +442,14 @@ class getid3_mp3 extends getid3_handler
if (isset($MPEGaudioHeaderDecodeCache[$head4])) { if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
$MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
} else { } else {
$MPEGheaderRawArray = getid3_mp3::MPEGaudioHeaderDecode($head4); $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
$MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
} }
static $MPEGaudioHeaderValidCache = array(); static $MPEGaudioHeaderValidCache = array();
if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
//$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1) //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false); $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
} }
// shortcut // shortcut
@ -533,7 +534,7 @@ class getid3_mp3 extends getid3_handler
if ($info['audio']['sample_rate'] > 0) { if ($info['audio']['sample_rate'] > 0) {
$thisfile_mpeg_audio['framelength'] = getid3_mp3::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']); $thisfile_mpeg_audio['framelength'] = self::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']);
} }
$nextframetestoffset = $offset + 1; $nextframetestoffset = $offset + 1;
@ -594,7 +595,7 @@ class getid3_mp3 extends getid3_handler
// Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36) // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
// depending on MPEG layer and number of channels // depending on MPEG layer and number of channels
$VBRidOffset = getid3_mp3::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']); $VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
$SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4); $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) { if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
@ -720,7 +721,7 @@ class getid3_mp3 extends getid3_handler
$thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4; $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
$thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F; $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F;
$thisfile_mpeg_audio_lame['vbr_method'] = getid3_mp3::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']); $thisfile_mpeg_audio_lame['vbr_method'] = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
$thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr' $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr'
// byte $A6 Lowpass filter value // byte $A6 Lowpass filter value
@ -819,9 +820,9 @@ class getid3_mp3 extends getid3_handler
$thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5; $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
$thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6; $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6;
$thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping']; $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
$thisfile_mpeg_audio_lame['stereo_mode'] = getid3_mp3::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']); $thisfile_mpeg_audio_lame['stereo_mode'] = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
$thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality']; $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
$thisfile_mpeg_audio_lame['source_sample_freq'] = getid3_mp3::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']); $thisfile_mpeg_audio_lame['source_sample_freq'] = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
// byte $B5 MP3 Gain // byte $B5 MP3 Gain
$thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true); $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
@ -832,9 +833,9 @@ class getid3_mp3 extends getid3_handler
$PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2)); $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
// Reserved = ($PresetSurroundBytes & 0xC000); // Reserved = ($PresetSurroundBytes & 0xC000);
$thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800); $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
$thisfile_mpeg_audio_lame['surround_info'] = getid3_mp3::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']); $thisfile_mpeg_audio_lame['surround_info'] = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
$thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF); $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF);
$thisfile_mpeg_audio_lame['preset_used'] = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame); $thisfile_mpeg_audio_lame['preset_used'] = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) { if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
$info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org'; $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
} }
@ -858,7 +859,7 @@ class getid3_mp3 extends getid3_handler
if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
$thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
$thisfile_mpeg_audio['bitrate'] = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']); $thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
$info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
//if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) { //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
// $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min']; // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
@ -890,19 +891,21 @@ class getid3_mp3 extends getid3_handler
if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) { if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) { if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
if (isset($info['fileformat']) && ($info['fileformat'] == 'riff')) { if ($this->isDependencyFor('matroska') || $this->isDependencyFor('riff')) {
// ignore, audio data is broken into chunks so will always be data "missing" // ignore, audio data is broken into chunks so will always be data "missing"
} elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) { }
$info['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'; elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
} else { $this->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)');
$info['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)'; }
else {
$this->warning('Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)');
} }
} else { } else {
if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) { if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
// $prenullbytefileoffset = ftell($this->getid3->fp); // $prenullbytefileoffset = $this->ftell();
// fseek($this->getid3->fp, $info['avdataend'], SEEK_SET); // $this->fseek($info['avdataend']);
// $PossibleNullByte = fread($this->getid3->fp, 1); // $PossibleNullByte = $this->fread(1);
// fseek($this->getid3->fp, $prenullbytefileoffset, SEEK_SET); // $this->fseek($prenullbytefileoffset);
// if ($PossibleNullByte === "\x00") { // if ($PossibleNullByte === "\x00") {
$info['avdataend']--; $info['avdataend']--;
// $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; // $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
@ -1069,7 +1072,7 @@ class getid3_mp3 extends getid3_handler
return true; return true;
} }
function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) { public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
$this->decodeMPEGaudioHeader($offset, $firstframetestarray, false); $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
@ -1115,11 +1118,11 @@ class getid3_mp3 extends getid3_handler
return true; return true;
} }
function FreeFormatFrameLength($offset, $deepscan=false) { public function FreeFormatFrameLength($offset, $deepscan=false) {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $offset, SEEK_SET); $this->fseek($offset);
$MPEGaudioData = fread($this->getid3->fp, 32768); $MPEGaudioData = $this->fread(32768);
$SyncPattern1 = substr($MPEGaudioData, 0, 4); $SyncPattern1 = substr($MPEGaudioData, 0, 4);
// may be different pattern due to padding // may be different pattern due to padding
@ -1166,8 +1169,8 @@ class getid3_mp3 extends getid3_handler
$ActualFrameLengthValues = array(); $ActualFrameLengthValues = array();
$nextoffset = $offset + $framelength; $nextoffset = $offset + $framelength;
while ($nextoffset < ($info['avdataend'] - 6)) { while ($nextoffset < ($info['avdataend'] - 6)) {
fseek($this->getid3->fp, $nextoffset - 1, SEEK_SET); $this->fseek($nextoffset - 1);
$NextSyncPattern = fread($this->getid3->fp, 6); $NextSyncPattern = $this->fread(6);
if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) { if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
// good - found where expected // good - found where expected
$ActualFrameLengthValues[] = $framelength; $ActualFrameLengthValues[] = $framelength;
@ -1192,17 +1195,17 @@ class getid3_mp3 extends getid3_handler
return $framelength; return $framelength;
} }
function getOnlyMPEGaudioInfoBruteForce() { public function getOnlyMPEGaudioInfoBruteForce() {
$MPEGaudioHeaderDecodeCache = array(); $MPEGaudioHeaderDecodeCache = array();
$MPEGaudioHeaderValidCache = array(); $MPEGaudioHeaderValidCache = array();
$MPEGaudioHeaderLengthCache = array(); $MPEGaudioHeaderLengthCache = array();
$MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
$MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
$MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
$MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
$MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
$MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
$MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
$LongMPEGversionLookup = array(); $LongMPEGversionLookup = array();
$LongMPEGlayerLookup = array(); $LongMPEGlayerLookup = array();
$LongMPEGbitrateLookup = array(); $LongMPEGbitrateLookup = array();
@ -1215,32 +1218,32 @@ class getid3_mp3 extends getid3_handler
$Distribution['padding'] = array(); $Distribution['padding'] = array();
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$max_frames_scan = 5000; $max_frames_scan = 5000;
$frames_scanned = 0; $frames_scanned = 0;
$previousvalidframe = $info['avdataoffset']; $previousvalidframe = $info['avdataoffset'];
while (ftell($this->getid3->fp) < $info['avdataend']) { while ($this->ftell() < $info['avdataend']) {
set_time_limit(30); set_time_limit(30);
$head4 = fread($this->getid3->fp, 4); $head4 = $this->fread(4);
if (strlen($head4) < 4) { if (strlen($head4) < 4) {
break; break;
} }
if ($head4{0} != "\xFF") { if ($head4{0} != "\xFF") {
for ($i = 1; $i < 4; $i++) { for ($i = 1; $i < 4; $i++) {
if ($head4{$i} == "\xFF") { if ($head4{$i} == "\xFF") {
fseek($this->getid3->fp, $i - 4, SEEK_CUR); $this->fseek($i - 4, SEEK_CUR);
continue 2; continue 2;
} }
} }
continue; continue;
} }
if (!isset($MPEGaudioHeaderDecodeCache[$head4])) { if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
$MPEGaudioHeaderDecodeCache[$head4] = getid3_mp3::MPEGaudioHeaderDecode($head4); $MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
} }
if (!isset($MPEGaudioHeaderValidCache[$head4])) { if (!isset($MPEGaudioHeaderValidCache[$head4])) {
$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false); $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
} }
if ($MPEGaudioHeaderValidCache[$head4]) { if ($MPEGaudioHeaderValidCache[$head4]) {
@ -1250,7 +1253,7 @@ class getid3_mp3 extends getid3_handler
$LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']]; $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
$LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding']; $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
$LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']]; $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
$MPEGaudioHeaderLengthCache[$head4] = getid3_mp3::MPEGaudioFrameLength( $MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
$LongMPEGbitrateLookup[$head4], $LongMPEGbitrateLookup[$head4],
$LongMPEGversionLookup[$head4], $LongMPEGversionLookup[$head4],
$LongMPEGlayerLookup[$head4], $LongMPEGlayerLookup[$head4],
@ -1258,18 +1261,18 @@ class getid3_mp3 extends getid3_handler
$LongMPEGfrequencyLookup[$head4]); $LongMPEGfrequencyLookup[$head4]);
} }
if ($MPEGaudioHeaderLengthCache[$head4] > 4) { if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
$WhereWeWere = ftell($this->getid3->fp); $WhereWeWere = $this->ftell();
fseek($this->getid3->fp, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR); $this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
$next4 = fread($this->getid3->fp, 4); $next4 = $this->fread(4);
if ($next4{0} == "\xFF") { if ($next4{0} == "\xFF") {
if (!isset($MPEGaudioHeaderDecodeCache[$next4])) { if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
$MPEGaudioHeaderDecodeCache[$next4] = getid3_mp3::MPEGaudioHeaderDecode($next4); $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
} }
if (!isset($MPEGaudioHeaderValidCache[$next4])) { if (!isset($MPEGaudioHeaderValidCache[$next4])) {
$MPEGaudioHeaderValidCache[$next4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false); $MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
} }
if ($MPEGaudioHeaderValidCache[$next4]) { if ($MPEGaudioHeaderValidCache[$next4]) {
fseek($this->getid3->fp, -4, SEEK_CUR); $this->fseek(-4, SEEK_CUR);
getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]); getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]); getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
@ -1277,7 +1280,7 @@ class getid3_mp3 extends getid3_handler
getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]); getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]); getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) { if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
$pct_data_scanned = (ftell($this->getid3->fp) - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']); $pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
$info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'; $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
foreach ($Distribution as $key1 => $value1) { foreach ($Distribution as $key1 => $value1) {
foreach ($value1 as $key2 => $value2) { foreach ($value1 as $key2 => $value2) {
@ -1290,7 +1293,7 @@ class getid3_mp3 extends getid3_handler
} }
} }
unset($next4); unset($next4);
fseek($this->getid3->fp, $WhereWeWere - 3, SEEK_SET); $this->fseek($WhereWeWere - 3);
} }
} }
@ -1340,7 +1343,7 @@ class getid3_mp3 extends getid3_handler
} }
function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) { public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
// looks for synch, decodes MPEG audio header // looks for synch, decodes MPEG audio header
$info = &$this->getid3->info; $info = &$this->getid3->info;
@ -1349,19 +1352,19 @@ class getid3_mp3 extends getid3_handler
static $MPEGaudioLayerLookup; static $MPEGaudioLayerLookup;
static $MPEGaudioBitrateLookup; static $MPEGaudioBitrateLookup;
if (empty($MPEGaudioVersionLookup)) { if (empty($MPEGaudioVersionLookup)) {
$MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
$MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
$MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
} }
fseek($this->getid3->fp, $avdataoffset, SEEK_SET); $this->fseek($avdataoffset);
$sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset); $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
if ($sync_seek_buffer_size <= 0) { if ($sync_seek_buffer_size <= 0) {
$info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset; $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
return false; return false;
} }
$header = fread($this->getid3->fp, $sync_seek_buffer_size); $header = $this->fread($sync_seek_buffer_size);
$sync_seek_buffer_size = strlen($header); $sync_seek_buffer_size = strlen($header);
$SynchSeekOffset = 0; $SynchSeekOffset = 0;
while ($SynchSeekOffset < $sync_seek_buffer_size) { while ($SynchSeekOffset < $sync_seek_buffer_size) {
@ -1473,7 +1476,7 @@ class getid3_mp3 extends getid3_handler
$dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
$synchstartoffset = $info['avdataoffset']; $synchstartoffset = $info['avdataoffset'];
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
// you can play with these numbers: // you can play with these numbers:
$max_frames_scan = 50000; $max_frames_scan = 50000;
@ -1488,13 +1491,13 @@ class getid3_mp3 extends getid3_handler
$pct_data_scanned = 0; $pct_data_scanned = 0;
for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) { for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
$frames_scanned_this_segment = 0; $frames_scanned_this_segment = 0;
if (ftell($this->getid3->fp) >= $info['avdataend']) { if ($this->ftell() >= $info['avdataend']) {
break; break;
} }
$scan_start_offset[$current_segment] = max(ftell($this->getid3->fp), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments))); $scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
if ($current_segment > 0) { if ($current_segment > 0) {
fseek($this->getid3->fp, $scan_start_offset[$current_segment], SEEK_SET); $this->fseek($scan_start_offset[$current_segment]);
$buffer_4k = fread($this->getid3->fp, 4096); $buffer_4k = $this->fread(4096);
for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) { for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) { if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
@ -1523,7 +1526,7 @@ class getid3_mp3 extends getid3_handler
} }
$frames_scanned++; $frames_scanned++;
if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) { if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
$this_pct_scanned = (ftell($this->getid3->fp) - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']); $this_pct_scanned = ($this->ftell() - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) { if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
// file likely contains < $max_frames_scan, just scan as one segment // file likely contains < $max_frames_scan, just scan as one segment
$max_scan_segments = 1; $max_scan_segments = 1;
@ -1620,17 +1623,17 @@ class getid3_mp3 extends getid3_handler
} }
static function MPEGaudioVersionArray() { public static function MPEGaudioVersionArray() {
static $MPEGaudioVersion = array('2.5', false, '2', '1'); static $MPEGaudioVersion = array('2.5', false, '2', '1');
return $MPEGaudioVersion; return $MPEGaudioVersion;
} }
static function MPEGaudioLayerArray() { public static function MPEGaudioLayerArray() {
static $MPEGaudioLayer = array(false, 3, 2, 1); static $MPEGaudioLayer = array(false, 3, 2, 1);
return $MPEGaudioLayer; return $MPEGaudioLayer;
} }
static function MPEGaudioBitrateArray() { public static function MPEGaudioBitrateArray() {
static $MPEGaudioBitrate; static $MPEGaudioBitrate;
if (empty($MPEGaudioBitrate)) { if (empty($MPEGaudioBitrate)) {
$MPEGaudioBitrate = array ( $MPEGaudioBitrate = array (
@ -1649,7 +1652,7 @@ class getid3_mp3 extends getid3_handler
return $MPEGaudioBitrate; return $MPEGaudioBitrate;
} }
static function MPEGaudioFrequencyArray() { public static function MPEGaudioFrequencyArray() {
static $MPEGaudioFrequency; static $MPEGaudioFrequency;
if (empty($MPEGaudioFrequency)) { if (empty($MPEGaudioFrequency)) {
$MPEGaudioFrequency = array ( $MPEGaudioFrequency = array (
@ -1661,12 +1664,12 @@ class getid3_mp3 extends getid3_handler
return $MPEGaudioFrequency; return $MPEGaudioFrequency;
} }
static function MPEGaudioChannelModeArray() { public static function MPEGaudioChannelModeArray() {
static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono'); static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
return $MPEGaudioChannelMode; return $MPEGaudioChannelMode;
} }
static function MPEGaudioModeExtensionArray() { public static function MPEGaudioModeExtensionArray() {
static $MPEGaudioModeExtension; static $MPEGaudioModeExtension;
if (empty($MPEGaudioModeExtension)) { if (empty($MPEGaudioModeExtension)) {
$MPEGaudioModeExtension = array ( $MPEGaudioModeExtension = array (
@ -1678,16 +1681,16 @@ class getid3_mp3 extends getid3_handler
return $MPEGaudioModeExtension; return $MPEGaudioModeExtension;
} }
static function MPEGaudioEmphasisArray() { public static function MPEGaudioEmphasisArray() {
static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17'); static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
return $MPEGaudioEmphasis; return $MPEGaudioEmphasis;
} }
static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) { public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
return getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode($head4), false, $allowBitrate15); return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
} }
static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) { public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) { if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
return false; return false;
} }
@ -1700,13 +1703,13 @@ class getid3_mp3 extends getid3_handler
static $MPEGaudioModeExtensionLookup; static $MPEGaudioModeExtensionLookup;
static $MPEGaudioEmphasisLookup; static $MPEGaudioEmphasisLookup;
if (empty($MPEGaudioVersionLookup)) { if (empty($MPEGaudioVersionLookup)) {
$MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
$MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
$MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
$MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
$MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
$MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
$MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
} }
if (isset($MPEGaudioVersionLookup[$rawarray['version']])) { if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
@ -1759,7 +1762,7 @@ class getid3_mp3 extends getid3_handler
return true; return true;
} }
static function MPEGaudioHeaderDecode($Header4Bytes) { public static function MPEGaudioHeaderDecode($Header4Bytes) {
// AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
// A - Frame sync (all bits set) // A - Frame sync (all bits set)
// B - MPEG Audio version ID // B - MPEG Audio version ID
@ -1796,7 +1799,7 @@ class getid3_mp3 extends getid3_handler
return $MPEGrawHeader; return $MPEGrawHeader;
} }
static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) { public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
static $AudioFrameLengthCache = array(); static $AudioFrameLengthCache = array();
if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) { if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
@ -1857,7 +1860,7 @@ class getid3_mp3 extends getid3_handler
return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate]; return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
} }
static function ClosestStandardMP3Bitrate($bit_rate) { public static function ClosestStandardMP3Bitrate($bit_rate) {
static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000); static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
static $bit_rate_table = array (0=>'-'); static $bit_rate_table = array (0=>'-');
$round_bit_rate = intval(round($bit_rate, -3)); $round_bit_rate = intval(round($bit_rate, -3));
@ -1877,7 +1880,7 @@ class getid3_mp3 extends getid3_handler
return $bit_rate_table[$round_bit_rate]; return $bit_rate_table[$round_bit_rate];
} }
static function XingVBRidOffset($version, $channelmode) { public static function XingVBRidOffset($version, $channelmode) {
static $XingVBRidOffsetCache = array(); static $XingVBRidOffsetCache = array();
if (empty($XingVBRidOffset)) { if (empty($XingVBRidOffset)) {
$XingVBRidOffset = array ( $XingVBRidOffset = array (
@ -1903,7 +1906,7 @@ class getid3_mp3 extends getid3_handler
return $XingVBRidOffset[$version][$channelmode]; return $XingVBRidOffset[$version][$channelmode];
} }
static function LAMEvbrMethodLookup($VBRmethodID) { public static function LAMEvbrMethodLookup($VBRmethodID) {
static $LAMEvbrMethodLookup = array( static $LAMEvbrMethodLookup = array(
0x00 => 'unknown', 0x00 => 'unknown',
0x01 => 'cbr', 0x01 => 'cbr',
@ -1919,7 +1922,7 @@ class getid3_mp3 extends getid3_handler
return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : ''); return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
} }
static function LAMEmiscStereoModeLookup($StereoModeID) { public static function LAMEmiscStereoModeLookup($StereoModeID) {
static $LAMEmiscStereoModeLookup = array( static $LAMEmiscStereoModeLookup = array(
0 => 'mono', 0 => 'mono',
1 => 'stereo', 1 => 'stereo',
@ -1933,7 +1936,7 @@ class getid3_mp3 extends getid3_handler
return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : ''); return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
} }
static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) { public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
static $LAMEmiscSourceSampleFrequencyLookup = array( static $LAMEmiscSourceSampleFrequencyLookup = array(
0 => '<= 32 kHz', 0 => '<= 32 kHz',
1 => '44.1 kHz', 1 => '44.1 kHz',
@ -1943,7 +1946,7 @@ class getid3_mp3 extends getid3_handler
return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : ''); return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
} }
static function LAMEsurroundInfoLookup($SurroundInfoID) { public static function LAMEsurroundInfoLookup($SurroundInfoID) {
static $LAMEsurroundInfoLookup = array( static $LAMEsurroundInfoLookup = array(
0 => 'no surround info', 0 => 'no surround info',
1 => 'DPL encoding', 1 => 'DPL encoding',
@ -1953,7 +1956,7 @@ class getid3_mp3 extends getid3_handler
return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved'); return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
} }
static function LAMEpresetUsedLookup($LAMEtag) { public static function LAMEpresetUsedLookup($LAMEtag) {
if ($LAMEtag['preset_used_id'] == 0) { if ($LAMEtag['preset_used_id'] == 0) {
// no preset used (LAME >=3.93) // no preset used (LAME >=3.93)
@ -2007,5 +2010,3 @@ class getid3_mp3 extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,7 +18,7 @@
class getid3_mpc extends getid3_handler class getid3_mpc extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['mpc']['header'] = array(); $info['mpc']['header'] = array();
@ -29,8 +30,8 @@ class getid3_mpc extends getid3_handler
$info['audio']['channels'] = 2; // up to SV7 the format appears to have been hardcoded for stereo only $info['audio']['channels'] = 2; // up to SV7 the format appears to have been hardcoded for stereo only
$info['audio']['lossless'] = false; $info['audio']['lossless'] = false;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$MPCheaderData = fread($this->getid3->fp, 4); $MPCheaderData = $this->fread(4);
$info['mpc']['header']['preamble'] = substr($MPCheaderData, 0, 4); // should be 'MPCK' (SV8) or 'MP+' (SV7), otherwise possible stream data (SV4-SV6) $info['mpc']['header']['preamble'] = substr($MPCheaderData, 0, 4); // should be 'MPCK' (SV8) or 'MP+' (SV7), otherwise possible stream data (SV4-SV6)
if (preg_match('#^MPCK#', $info['mpc']['header']['preamble'])) { if (preg_match('#^MPCK#', $info['mpc']['header']['preamble'])) {
@ -59,7 +60,7 @@ class getid3_mpc extends getid3_handler
} }
function ParseMPCsv8() { public function ParseMPCsv8() {
// this is SV8 // this is SV8
// http://trac.musepack.net/trac/wiki/SV8Specification // http://trac.musepack.net/trac/wiki/SV8Specification
@ -69,7 +70,7 @@ class getid3_mpc extends getid3_handler
$keyNameSize = 2; $keyNameSize = 2;
$maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10" $maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10"
$offset = ftell($this->getid3->fp); $offset = $this->ftell();
while ($offset < $info['avdataend']) { while ($offset < $info['avdataend']) {
$thisPacket = array(); $thisPacket = array();
$thisPacket['offset'] = $offset; $thisPacket['offset'] = $offset;
@ -77,7 +78,7 @@ class getid3_mpc extends getid3_handler
// Size is a variable-size field, could be 1-4 bytes (possibly more?) // Size is a variable-size field, could be 1-4 bytes (possibly more?)
// read enough data in and figure out the exact size later // read enough data in and figure out the exact size later
$MPCheaderData = fread($this->getid3->fp, $keyNameSize + $maxHandledPacketLength); $MPCheaderData = $this->fread($keyNameSize + $maxHandledPacketLength);
$packet_offset += $keyNameSize; $packet_offset += $keyNameSize;
$thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize); $thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize);
$thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']); $thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']);
@ -98,7 +99,7 @@ class getid3_mpc extends getid3_handler
case 'SH': // Stream Header case 'SH': // Stream Header
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength; $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
if ($moreBytesToRead > 0) { if ($moreBytesToRead > 0) {
$MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead); $MPCheaderData .= $this->fread($moreBytesToRead);
} }
$thisPacket['crc'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4)); $thisPacket['crc'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
$packet_offset += 4; $packet_offset += 4;
@ -136,7 +137,7 @@ class getid3_mpc extends getid3_handler
case 'RG': // Replay Gain case 'RG': // Replay Gain
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength; $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
if ($moreBytesToRead > 0) { if ($moreBytesToRead > 0) {
$MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead); $MPCheaderData .= $this->fread($moreBytesToRead);
} }
$thisPacket['replaygain_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1)); $thisPacket['replaygain_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
$packet_offset += 1; $packet_offset += 1;
@ -158,7 +159,7 @@ class getid3_mpc extends getid3_handler
case 'EI': // Encoder Info case 'EI': // Encoder Info
$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength; $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
if ($moreBytesToRead > 0) { if ($moreBytesToRead > 0) {
$MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead); $MPCheaderData .= $this->fread($moreBytesToRead);
} }
$profile_pns = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1)); $profile_pns = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
$packet_offset += 1; $packet_offset += 1;
@ -201,13 +202,13 @@ class getid3_mpc extends getid3_handler
if (!empty($thisPacket)) { if (!empty($thisPacket)) {
$info['mpc']['packets'][] = $thisPacket; $info['mpc']['packets'][] = $thisPacket;
} }
fseek($this->getid3->fp, $offset); $this->fseek($offset);
} }
$thisfile_mpc_header['size'] = $offset; $thisfile_mpc_header['size'] = $offset;
return true; return true;
} }
function ParseMPCsv7() { public function ParseMPCsv7() {
// this is SV7 // this is SV7
// http://www.uni-jena.de/~pfk/mpp/sv8/header.html // http://www.uni-jena.de/~pfk/mpp/sv8/header.html
@ -217,7 +218,7 @@ class getid3_mpc extends getid3_handler
$thisfile_mpc_header['size'] = 28; $thisfile_mpc_header['size'] = 28;
$MPCheaderData = $info['mpc']['header']['preamble']; $MPCheaderData = $info['mpc']['header']['preamble'];
$MPCheaderData .= fread($this->getid3->fp, $thisfile_mpc_header['size'] - strlen($info['mpc']['header']['preamble'])); $MPCheaderData .= $this->fread($thisfile_mpc_header['size'] - strlen($info['mpc']['header']['preamble']));
$offset = strlen('MP+'); $offset = strlen('MP+');
$StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1)); $StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
@ -321,7 +322,7 @@ class getid3_mpc extends getid3_handler
return true; return true;
} }
function ParseMPCsv6() { public function ParseMPCsv6() {
// this is SV4 - SV6 // this is SV4 - SV6
$info = &$this->getid3->info; $info = &$this->getid3->info;
@ -329,8 +330,8 @@ class getid3_mpc extends getid3_handler
$offset = 0; $offset = 0;
$thisfile_mpc_header['size'] = 8; $thisfile_mpc_header['size'] = 8;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$MPCheaderData = fread($this->getid3->fp, $thisfile_mpc_header['size']); $MPCheaderData = $this->fread($thisfile_mpc_header['size']);
// add size of file header to avdataoffset - calc bitrate correctly + MD5 data // add size of file header to avdataoffset - calc bitrate correctly + MD5 data
$info['avdataoffset'] += $thisfile_mpc_header['size']; $info['avdataoffset'] += $thisfile_mpc_header['size'];
@ -397,7 +398,7 @@ class getid3_mpc extends getid3_handler
} }
function MPCprofileNameLookup($profileid) { public function MPCprofileNameLookup($profileid) {
static $MPCprofileNameLookup = array( static $MPCprofileNameLookup = array(
0 => 'no profile', 0 => 'no profile',
1 => 'Experimental', 1 => 'Experimental',
@ -419,7 +420,7 @@ class getid3_mpc extends getid3_handler
return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid'); return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid');
} }
function MPCfrequencyLookup($frequencyid) { public function MPCfrequencyLookup($frequencyid) {
static $MPCfrequencyLookup = array( static $MPCfrequencyLookup = array(
0 => 44100, 0 => 44100,
1 => 48000, 1 => 48000,
@ -429,14 +430,14 @@ class getid3_mpc extends getid3_handler
return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid'); return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid');
} }
function MPCpeakDBLookup($intvalue) { public function MPCpeakDBLookup($intvalue) {
if ($intvalue > 0) { if ($intvalue > 0) {
return ((log10($intvalue) / log10(2)) - 15) * 6; return ((log10($intvalue) / log10(2)) - 15) * 6;
} }
return false; return false;
} }
function MPCencoderVersionLookup($encoderversion) { public function MPCencoderVersionLookup($encoderversion) {
//Encoder version * 100 (106 = 1.06) //Encoder version * 100 (106 = 1.06)
//EncoderVersion % 10 == 0 Release (1.0) //EncoderVersion % 10 == 0 Release (1.0)
//EncoderVersion % 2 == 0 Beta (1.06) //EncoderVersion % 2 == 0 Beta (1.06)
@ -463,7 +464,7 @@ class getid3_mpc extends getid3_handler
return number_format($encoderversion / 100, 2).' alpha'; return number_format($encoderversion / 100, 2).' alpha';
} }
function SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength=9) { public function SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength=9) {
$packet_size = 0; $packet_size = 0;
for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) { for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) {
// variable-length size field: // variable-length size field:
@ -487,7 +488,7 @@ class getid3_mpc extends getid3_handler
return $packet_size; return $packet_size;
} }
function MPCsv8PacketName($packetKey) { public function MPCsv8PacketName($packetKey) {
static $MPCsv8PacketName = array(); static $MPCsv8PacketName = array();
if (empty($MPCsv8PacketName)) { if (empty($MPCsv8PacketName)) {
$MPCsv8PacketName = array( $MPCsv8PacketName = array(
@ -504,6 +505,3 @@ class getid3_mpc extends getid3_handler
return (isset($MPCsv8PacketName[$packetKey]) ? $MPCsv8PacketName[$packetKey] : $packetKey); return (isset($MPCsv8PacketName[$packetKey]) ? $MPCsv8PacketName[$packetKey] : $packetKey);
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,9 +18,8 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE
class getid3_ogg extends getid3_handler class getid3_ogg extends getid3_handler
{ {
var $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory // http://xiph.org/vorbis/doc/Vorbis_I_spec.html
public function Analyze() {
function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'ogg'; $info['fileformat'] = 'ogg';
@ -38,19 +38,19 @@ class getid3_ogg extends getid3_handler
// Page 1 - Stream Header // Page 1 - Stream Header
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$oggpageinfo = $this->ParseOggPageHeader(); $oggpageinfo = $this->ParseOggPageHeader();
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
if (ftell($this->getid3->fp) >= $this->getid3->fread_buffer_size()) { if ($this->ftell() >= $this->getid3->fread_buffer_size()) {
$info['error'][] = 'Could not find start of Ogg page in the first '.$this->getid3->fread_buffer_size().' bytes (this might not be an Ogg-Vorbis file?)'; $info['error'][] = 'Could not find start of Ogg page in the first '.$this->getid3->fread_buffer_size().' bytes (this might not be an Ogg-Vorbis file?)';
unset($info['fileformat']); unset($info['fileformat']);
unset($info['ogg']); unset($info['ogg']);
return false; return false;
} }
$filedata = fread($this->getid3->fp, $oggpageinfo['page_length']); $filedata = $this->fread($oggpageinfo['page_length']);
$filedataoffset = 0; $filedataoffset = 0;
if (substr($filedata, 0, 4) == 'fLaC') { if (substr($filedata, 0, 4) == 'fLaC') {
@ -63,6 +63,12 @@ class getid3_ogg extends getid3_handler
$this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo); $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
} elseif (substr($filedata, 0, 8) == 'OpusHead') {
if( $this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) == false ) {
return false;
}
} elseif (substr($filedata, 0, 8) == 'Speex ') { } elseif (substr($filedata, 0, 8) == 'Speex ') {
// http://www.speex.org/manual/node10.html // http://www.speex.org/manual/node10.html
@ -115,6 +121,66 @@ class getid3_ogg extends getid3_handler
$info['audio']['bitrate_mode'] = 'vbr'; $info['audio']['bitrate_mode'] = 'vbr';
} }
} elseif (substr($filedata, 0, 7) == "\x80".'theora') {
// http://www.theora.org/doc/Theora.pdf (section 6.2)
$info['ogg']['pageheader']['theora']['theora_magic'] = substr($filedata, $filedataoffset, 7); // hard-coded to "\x80.'theora'
$filedataoffset += 7;
$info['ogg']['pageheader']['theora']['version_major'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['version_minor'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['version_revision'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['frame_width_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
$filedataoffset += 2;
$info['ogg']['pageheader']['theora']['frame_height_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
$filedataoffset += 2;
$info['ogg']['pageheader']['theora']['resolution_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
$filedataoffset += 3;
$info['ogg']['pageheader']['theora']['resolution_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
$filedataoffset += 3;
$info['ogg']['pageheader']['theora']['picture_offset_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['picture_offset_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['frame_rate_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$info['ogg']['pageheader']['theora']['frame_rate_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
$info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
$filedataoffset += 3;
$info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
$filedataoffset += 3;
$info['ogg']['pageheader']['theora']['color_space_id'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
$info['ogg']['pageheader']['theora']['nominal_bitrate'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3));
$filedataoffset += 3;
$info['ogg']['pageheader']['theora']['flags'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2));
$filedataoffset += 2;
$info['ogg']['pageheader']['theora']['quality'] = ($info['ogg']['pageheader']['theora']['flags'] & 0xFC00) >> 10;
$info['ogg']['pageheader']['theora']['kfg_shift'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x03E0) >> 5;
$info['ogg']['pageheader']['theora']['pixel_format_id'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x0018) >> 3;
$info['ogg']['pageheader']['theora']['reserved'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x0007) >> 0; // should be 0
$info['ogg']['pageheader']['theora']['color_space'] = self::TheoraColorSpace($info['ogg']['pageheader']['theora']['color_space_id']);
$info['ogg']['pageheader']['theora']['pixel_format'] = self::TheoraPixelFormat($info['ogg']['pageheader']['theora']['pixel_format_id']);
$info['video']['dataformat'] = 'theora';
$info['mime_type'] = 'video/ogg';
//$info['audio']['bitrate_mode'] = 'abr';
//$info['audio']['lossless'] = false;
$info['video']['resolution_x'] = $info['ogg']['pageheader']['theora']['resolution_x'];
$info['video']['resolution_y'] = $info['ogg']['pageheader']['theora']['resolution_y'];
if ($info['ogg']['pageheader']['theora']['frame_rate_denominator'] > 0) {
$info['video']['frame_rate'] = (float) $info['ogg']['pageheader']['theora']['frame_rate_numerator'] / $info['ogg']['pageheader']['theora']['frame_rate_denominator'];
}
if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) {
$info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'];
}
$info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable';
} elseif (substr($filedata, 0, 8) == "fishead\x00") { } elseif (substr($filedata, 0, 8) == "fishead\x00") {
@ -146,8 +212,8 @@ class getid3_ogg extends getid3_handler
do { do {
$oggpageinfo = $this->ParseOggPageHeader(); $oggpageinfo = $this->ParseOggPageHeader();
$info['ogg']['pageheader'][$oggpageinfo['page_seqno'].'.'.$counter++] = $oggpageinfo; $info['ogg']['pageheader'][$oggpageinfo['page_seqno'].'.'.$counter++] = $oggpageinfo;
$filedata = fread($this->getid3->fp, $oggpageinfo['page_length']); $filedata = $this->fread($oggpageinfo['page_length']);
fseek($this->getid3->fp, $oggpageinfo['page_end_offset'], SEEK_SET); $this->fseek($oggpageinfo['page_end_offset']);
if (substr($filedata, 0, 8) == "fisbone\x00") { if (substr($filedata, 0, 8) == "fisbone\x00") {
@ -173,30 +239,29 @@ class getid3_ogg extends getid3_handler
} elseif (substr($filedata, 1, 6) == 'theora') { } elseif (substr($filedata, 1, 6) == 'theora') {
$info['video']['dataformat'] = 'theora'; $info['video']['dataformat'] = 'theora1';
$info['error'][] = 'Ogg Theora not correctly handled in this version of getID3 ['.$this->getid3->version().']'; $info['error'][] = 'Ogg Theora (v1) not correctly handled in this version of getID3 ['.$this->getid3->version().']';
//break; //break;
} elseif (substr($filedata, 1, 6) == 'vorbis') { } elseif (substr($filedata, 1, 6) == 'vorbis') {
$this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo); $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
} else { } else {
$info['error'][] = 'unexpected'; $info['error'][] = 'unexpected';
//break; //break;
} }
//} while ($oggpageinfo['page_seqno'] == 0); //} while ($oggpageinfo['page_seqno'] == 0);
} while (($oggpageinfo['page_seqno'] == 0) && (substr($filedata, 0, 8) != "fisbone\x00")); } while (($oggpageinfo['page_seqno'] == 0) && (substr($filedata, 0, 8) != "fisbone\x00"));
fseek($this->getid3->fp, $oggpageinfo['page_start_offset'], SEEK_SET);
$this->fseek($oggpageinfo['page_start_offset']);
$info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']'; $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']';
//return false; //return false;
} else { } else {
$info['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"'; $info['error'][] = 'Expecting either "Speex ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"';
unset($info['ogg']); unset($info['ogg']);
unset($info['mime_type']); unset($info['mime_type']);
return false; return false;
@ -209,44 +274,52 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
switch ($info['audio']['dataformat']) { switch ($info['audio']['dataformat']) {
case 'vorbis': case 'vorbis':
$filedata = fread($this->getid3->fp, $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); $filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1)); $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis' $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis'
$this->ParseVorbisCommentsFilepointer(); $this->ParseVorbisComments();
break; break;
case 'flac': case 'flac':
$getid3_flac = new getid3_flac($this->getid3); $flac = new getid3_flac($this->getid3);
if (!$getid3_flac->FLACparseMETAdata()) { if (!$flac->parseMETAdata()) {
$info['error'][] = 'Failed to parse FLAC headers'; $info['error'][] = 'Failed to parse FLAC headers';
return false; return false;
} }
unset($getid3_flac); unset($flac);
break; break;
case 'speex': case 'speex':
fseek($this->getid3->fp, $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR); $this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
$this->ParseVorbisCommentsFilepointer(); $this->ParseVorbisComments();
break;
case 'opus':
$filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 0, 8); // hard-coded to 'OpusTags'
if(substr($filedata, 0, 8) != 'OpusTags') {
$info['error'][] = 'Expected "OpusTags" as header but got "'.substr($filedata, 0, 8).'"';
return false;
}
$this->ParseVorbisComments();
break; break;
} }
// Last Page - Number of Samples // Last Page - Number of Samples
if (!getid3_lib::intValueSupported($info['avdataend'])) { if (!getid3_lib::intValueSupported($info['avdataend'])) {
$info['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)'; $info['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)';
} else { } else {
fseek($this->getid3->fp, max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0), SEEK_SET); $this->fseek(max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0));
$LastChunkOfOgg = strrev(fread($this->getid3->fp, $this->getid3->fread_buffer_size())); $LastChunkOfOgg = strrev($this->fread($this->getid3->fread_buffer_size()));
if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) { if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
fseek($this->getid3->fp, $info['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET); $this->fseek($info['avdataend'] - ($LastOggSpostion + strlen('SggO')));
$info['avdataend'] = ftell($this->getid3->fp); $info['avdataend'] = $this->ftell();
$info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader(); $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader();
$info['ogg']['samples'] = $info['ogg']['pageheader']['eos']['pcm_abs_position']; $info['ogg']['samples'] = $info['ogg']['pageheader']['eos']['pcm_abs_position'];
if ($info['ogg']['samples'] == 0) { if ($info['ogg']['samples'] == 0) {
@ -305,7 +378,7 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
return true; return true;
} }
function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) { public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['audio']['dataformat'] = 'vorbis'; $info['audio']['dataformat'] = 'vorbis';
$info['audio']['lossless'] = false; $info['audio']['lossless'] = false;
@ -353,19 +426,70 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
return true; return true;
} }
function ParseOggPageHeader() { // http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
// http://xiph.org/ogg/vorbis/doc/framing.html public function ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
$oggheader['page_start_offset'] = ftell($this->getid3->fp); // where we started from in the file $info = &$this->getid3->info;
$info['audio']['dataformat'] = 'opus';
$info['mime_type'] = 'audio/ogg; codecs=opus';
$filedata = fread($this->getid3->fp, $this->getid3->fread_buffer_size()); /** @todo find a usable way to detect abr (vbr that is padded to be abr) */
$info['audio']['bitrate_mode'] = 'vbr';
$info['audio']['lossless'] = false;
$info['ogg']['pageheader']['opus']['opus_magic'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'OpusHead'
$filedataoffset += 8;
$info['ogg']['pageheader']['opus']['version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
if ($info['ogg']['pageheader']['opus']['version'] < 1 || $info['ogg']['pageheader']['opus']['version'] > 15) {
$info['error'][] = 'Unknown opus version number (only accepting 1-15)';
return false;
}
$info['ogg']['pageheader']['opus']['out_channel_count'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
if ($info['ogg']['pageheader']['opus']['out_channel_count'] == 0) {
$info['error'][] = 'Invalid channel count in opus header (must not be zero)';
return false;
}
$info['ogg']['pageheader']['opus']['pre_skip'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
$filedataoffset += 2;
$info['ogg']['pageheader']['opus']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
//$info['ogg']['pageheader']['opus']['output_gain'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
//$filedataoffset += 2;
//$info['ogg']['pageheader']['opus']['channel_mapping_family'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
//$filedataoffset += 1;
$info['opus']['opus_version'] = $info['ogg']['pageheader']['opus']['version'];
$info['opus']['sample_rate'] = $info['ogg']['pageheader']['opus']['sample_rate'];
$info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count'];
$info['audio']['channels'] = $info['opus']['out_channel_count'];
$info['audio']['sample_rate'] = $info['opus']['sample_rate'];
return true;
}
public function ParseOggPageHeader() {
// http://xiph.org/ogg/vorbis/doc/framing.html
$oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
$filedata = $this->fread($this->getid3->fread_buffer_size());
$filedataoffset = 0; $filedataoffset = 0;
while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) { while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
if ((ftell($this->getid3->fp) - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) { if (($this->ftell() - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) {
// should be found before here // should be found before here
return false; return false;
} }
if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) { if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
if (feof($this->getid3->fp) || (($filedata .= fread($this->getid3->fp, $this->getid3->fread_buffer_size())) === false)) { if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === false)) {
// get some more data, unless eof, in which case fail // get some more data, unless eof, in which case fail
return false; return false;
} }
@ -399,45 +523,45 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
} }
$oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset; $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
$oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length']; $oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length'];
fseek($this->getid3->fp, $oggheader['header_end_offset'], SEEK_SET); $this->fseek($oggheader['header_end_offset']);
return $oggheader; return $oggheader;
} }
// http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
function ParseVorbisCommentsFilepointer() { public function ParseVorbisComments() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$OriginalOffset = ftell($this->getid3->fp); $OriginalOffset = $this->ftell();
$commentdataoffset = 0; $commentdataoffset = 0;
$VorbisCommentPage = 1; $VorbisCommentPage = 1;
switch ($info['audio']['dataformat']) { switch ($info['audio']['dataformat']) {
case 'vorbis': case 'vorbis':
case 'speex':
case 'opus':
$CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
fseek($this->getid3->fp, $CommentStartOffset, SEEK_SET); $this->fseek($CommentStartOffset);
$commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
$commentdata = fread($this->getid3->fp, getid3_ogg::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); $commentdata = $this->fread(self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
if ($info['audio']['dataformat'] == 'vorbis') {
$commentdataoffset += (strlen('vorbis') + 1);
}
else if ($info['audio']['dataformat'] == 'opus') {
$commentdataoffset += strlen('OpusTags');
}
$commentdataoffset += (strlen('vorbis') + 1);
break; break;
case 'flac': case 'flac':
$CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4; $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4;
fseek($this->getid3->fp, $CommentStartOffset, SEEK_SET); $this->fseek($CommentStartOffset);
$commentdata = fread($this->getid3->fp, $info['flac']['VORBIS_COMMENT']['raw']['block_length']); $commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']);
break;
case 'speex':
$CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
fseek($this->getid3->fp, $CommentStartOffset, SEEK_SET);
$commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
$commentdata = fread($this->getid3->fp, getid3_ogg::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
break; break;
default: default:
return false; return false;
break;
} }
$VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
@ -454,9 +578,15 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
$ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw']; $ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw'];
for ($i = 0; $i < $CommentsCount; $i++) { for ($i = 0; $i < $CommentsCount; $i++) {
if ($i >= 10000) {
// https://github.com/owncloud/music/issues/212#issuecomment-43082336
$info['warning'][] = 'Unexpectedly large number ('.$CommentsCount.') of Ogg comments - breaking after reading '.$i.' comments';
break;
}
$ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset; $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
if (ftell($this->getid3->fp) < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) { if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) {
if ($oggpageinfo = $this->ParseOggPageHeader()) { if ($oggpageinfo = $this->ParseOggPageHeader()) {
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
@ -475,8 +605,8 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
// Finally, stick the unused data back on the end // Finally, stick the unused data back on the end
$commentdata .= $AsYetUnusedData; $commentdata .= $AsYetUnusedData;
//$commentdata .= fread($this->getid3->fp, $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
$commentdata .= fread($this->getid3->fp, $this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1)); $commentdata .= $this->fread($this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1));
} }
} }
@ -510,17 +640,17 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
// Finally, stick the unused data back on the end // Finally, stick the unused data back on the end
$commentdata .= $AsYetUnusedData; $commentdata .= $AsYetUnusedData;
//$commentdata .= fread($this->getid3->fp, $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) { if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) {
$info['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($this->getid3->fp); $info['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell();
break; break;
} }
$readlength = getid3_ogg::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1); $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1);
if ($readlength <= 0) { if ($readlength <= 0) {
$info['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.ftell($this->getid3->fp); $info['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell();
break; break;
} }
$commentdata .= fread($this->getid3->fp, $readlength); $commentdata .= $this->fread($readlength);
//$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset']; //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
} }
@ -536,75 +666,57 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
} elseif (strstr($commentstring, '=')) { } elseif (strstr($commentstring, '=')) {
$commentexploded = explode('=', $commentstring, 2); $commentexploded = explode('=', $commentstring, 2);
$ThisFileInfo_ogg_comments_raw[$i]['key'] = strtoupper($commentexploded[0]); $ThisFileInfo_ogg_comments_raw[$i]['key'] = strtoupper($commentexploded[0]);
$ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : ''); $ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : '');
$ThisFileInfo_ogg_comments_raw[$i]['data'] = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']);
$ThisFileInfo_ogg_comments_raw[$i]['data_length'] = strlen($ThisFileInfo_ogg_comments_raw[$i]['data']);
if (preg_match('#^(BM|GIF|\xFF\xD8\xFF|\x89\x50\x4E\x47\x0D\x0A\x1A\x0A|II\x2A\x00|MM\x00\x2A)#s', $ThisFileInfo_ogg_comments_raw[$i]['data'])) { if ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'METADATA_BLOCK_PICTURE') {
$imageinfo = array();
$imagechunkcheck = getid3_lib::GetDataImageSize($ThisFileInfo_ogg_comments_raw[$i]['data'], $imageinfo); // http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE
unset($imageinfo); // The unencoded format is that of the FLAC picture block. The fields are stored in big endian order as in FLAC, picture data is stored according to the relevant standard.
if (!empty($imagechunkcheck)) { // http://flac.sourceforge.net/format.html#metadata_block_picture
$ThisFileInfo_ogg_comments_raw[$i]['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); $flac = new getid3_flac($this->getid3);
if ($ThisFileInfo_ogg_comments_raw[$i]['image_mime'] && ($ThisFileInfo_ogg_comments_raw[$i]['image_mime'] != 'application/octet-stream')) { $flac->setStringMode(base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']));
unset($ThisFileInfo_ogg_comments_raw[$i]['value']); $flac->parsePICTURE();
} $info['ogg']['comments']['picture'][] = $flac->getid3->info['flac']['PICTURE'][0];
unset($flac);
} elseif ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'COVERART') {
$data = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']);
$this->notice('Found deprecated COVERART tag, it should be replaced in honor of METADATA_BLOCK_PICTURE structure');
/** @todo use 'coverartmime' where available */
$imageinfo = getid3_lib::GetDataImageSize($data);
if ($imageinfo === false || !isset($imageinfo['mime'])) {
$this->warning('COVERART vorbiscomment tag contains invalid image');
continue;
} }
}
if (isset($ThisFileInfo_ogg_comments_raw[$i]['value'])) { $ogg = new self($this->getid3);
unset($ThisFileInfo_ogg_comments_raw[$i]['data']); $ogg->setStringMode($data);
$info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value']; $info['ogg']['comments']['picture'][] = array(
'image_mime' => $imageinfo['mime'],
'datalength' => strlen($data),
'picturetype' => 'cover art',
'image_height' => $imageinfo['height'],
'image_width' => $imageinfo['width'],
'data' => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']),
);
unset($ogg);
} else { } else {
do {
if ($this->inline_attachments === false) { $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value'];
// skip entirely
unset($ThisFileInfo_ogg_comments_raw[$i]['data']);
break;
}
if ($this->inline_attachments === true) {
// great
} elseif (is_int($this->inline_attachments)) {
if ($this->inline_attachments < $ThisFileInfo_ogg_comments_raw[$i]['data_length']) {
// too big, skip
$info['warning'][] = 'attachment at '.$ThisFileInfo_ogg_comments_raw[$i]['offset'].' is too large to process inline ('.number_format($ThisFileInfo_ogg_comments_raw[$i]['data_length']).' bytes)';
unset($ThisFileInfo_ogg_comments_raw[$i]['data']);
break;
}
} elseif (is_string($this->inline_attachments)) {
$this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
// cannot write, skip
$info['warning'][] = 'attachment at '.$ThisFileInfo_ogg_comments_raw[$i]['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)';
unset($ThisFileInfo_ogg_comments_raw[$i]['data']);
break;
}
}
// if we get this far, must be OK
if (is_string($this->inline_attachments)) {
$destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$ThisFileInfo_ogg_comments_raw[$i]['offset'];
if (!file_exists($destination_filename) || is_writable($destination_filename)) {
file_put_contents($destination_filename, $ThisFileInfo_ogg_comments_raw[$i]['data']);
} else {
$info['warning'][] = 'attachment at '.$ThisFileInfo_ogg_comments_raw[$i]['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)';
}
$ThisFileInfo_ogg_comments_raw[$i]['data_filename'] = $destination_filename;
unset($ThisFileInfo_ogg_comments_raw[$i]['data']);
} else {
$info['ogg']['comments']['picture'][] = array('data'=>$ThisFileInfo_ogg_comments_raw[$i]['data'], 'image_mime'=>$ThisFileInfo_ogg_comments_raw[$i]['image_mime']);
}
} while (false);
} }
} else { } else {
$info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring; $info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
} }
unset($ThisFileInfo_ogg_comments_raw[$i]);
} }
unset($ThisFileInfo_ogg_comments_raw);
// Replay Gain Adjustment // Replay Gain Adjustment
@ -647,12 +759,12 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
} }
} }
fseek($this->getid3->fp, $OriginalOffset, SEEK_SET); $this->fseek($OriginalOffset);
return true; return true;
} }
static function SpeexBandModeLookup($mode) { public static function SpeexBandModeLookup($mode) {
static $SpeexBandModeLookup = array(); static $SpeexBandModeLookup = array();
if (empty($SpeexBandModeLookup)) { if (empty($SpeexBandModeLookup)) {
$SpeexBandModeLookup[0] = 'narrow'; $SpeexBandModeLookup[0] = 'narrow';
@ -663,7 +775,7 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
} }
static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) { public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
for ($i = 0; $i < $SegmentNumber; $i++) { for ($i = 0; $i < $SegmentNumber; $i++) {
$segmentlength = 0; $segmentlength = 0;
foreach ($OggInfoArray['segment_table'] as $key => $value) { foreach ($OggInfoArray['segment_table'] as $key => $value) {
@ -677,7 +789,7 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
} }
static function get_quality_from_nominal_bitrate($nominal_bitrate) { public static function get_quality_from_nominal_bitrate($nominal_bitrate) {
// decrease precision // decrease precision
$nominal_bitrate = $nominal_bitrate / 1000; $nominal_bitrate = $nominal_bitrate / 1000;
@ -700,6 +812,28 @@ $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3
return round($qval, 1); // 5 or 4.9 return round($qval, 1); // 5 or 4.9
} }
} public static function TheoraColorSpace($colorspace_id) {
// http://www.theora.org/doc/Theora.pdf (table 6.3)
static $TheoraColorSpaceLookup = array();
if (empty($TheoraColorSpaceLookup)) {
$TheoraColorSpaceLookup[0] = 'Undefined';
$TheoraColorSpaceLookup[1] = 'Rec. 470M';
$TheoraColorSpaceLookup[2] = 'Rec. 470BG';
$TheoraColorSpaceLookup[3] = 'Reserved';
}
return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null);
}
?> public static function TheoraPixelFormat($pixelformat_id) {
// http://www.theora.org/doc/Theora.pdf (table 6.4)
static $TheoraPixelFormatLookup = array();
if (empty($TheoraPixelFormatLookup)) {
$TheoraPixelFormatLookup[0] = '4:2:0';
$TheoraPixelFormatLookup[1] = 'Reserved';
$TheoraPixelFormatLookup[2] = '4:2:2';
$TheoraPixelFormatLookup[3] = '4:4:4';
}
return (isset($TheoraPixelFormatLookup[$pixelformat_id]) ? $TheoraPixelFormatLookup[$pixelformat_id] : null);
}
}

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -18,7 +19,7 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php',
class getid3_optimfrog extends getid3_handler class getid3_optimfrog extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'ofr'; $info['fileformat'] = 'ofr';
@ -26,8 +27,8 @@ class getid3_optimfrog extends getid3_handler
$info['audio']['bitrate_mode'] = 'vbr'; $info['audio']['bitrate_mode'] = 'vbr';
$info['audio']['lossless'] = true; $info['audio']['lossless'] = true;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$OFRheader = fread($this->getid3->fp, 8); $OFRheader = $this->fread(8);
if (substr($OFRheader, 0, 5) == '*RIFF') { if (substr($OFRheader, 0, 5) == '*RIFF') {
return $this->ParseOptimFROGheader42(); return $this->ParseOptimFROGheader42();
@ -44,12 +45,12 @@ class getid3_optimfrog extends getid3_handler
} }
function ParseOptimFROGheader42() { public function ParseOptimFROGheader42() {
// for fileformat of v4.21 and older // for fileformat of v4.21 and older
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$OptimFROGheaderData = fread($this->getid3->fp, 45); $OptimFROGheaderData = $this->fread(45);
$info['avdataoffset'] = 45; $info['avdataoffset'] = 45;
$OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1)); $OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1));
@ -61,8 +62,8 @@ class getid3_optimfrog extends getid3_handler
if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
$info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize); $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
fseek($this->getid3->fp, $info['avdataend'], SEEK_SET); $this->fseek($info['avdataend']);
$RIFFdata .= fread($this->getid3->fp, $OrignalRIFFheaderSize - $OrignalRIFFdataSize); $RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
} }
// move the data chunk after all other chunks (if any) // move the data chunk after all other chunks (if any)
@ -91,15 +92,15 @@ class getid3_optimfrog extends getid3_handler
} }
function ParseOptimFROGheader45() { public function ParseOptimFROGheader45() {
// for fileformat of v4.50a and higher // for fileformat of v4.50a and higher
$info = &$this->getid3->info; $info = &$this->getid3->info;
$RIFFdata = ''; $RIFFdata = '';
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
while (!feof($this->getid3->fp) && (ftell($this->getid3->fp) < $info['avdataend'])) { while (!feof($this->getid3->fp) && ($this->ftell() < $info['avdataend'])) {
$BlockOffset = ftell($this->getid3->fp); $BlockOffset = $this->ftell();
$BlockData = fread($this->getid3->fp, 8); $BlockData = $this->fread(8);
$offset = 8; $offset = 8;
$BlockName = substr($BlockData, 0, 4); $BlockName = substr($BlockData, 0, 4);
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
@ -130,7 +131,7 @@ class getid3_optimfrog extends getid3_handler
$info['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)'; $info['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)';
break; break;
} }
$BlockData .= fread($this->getid3->fp, $BlockSize); $BlockData .= $this->fread($BlockSize);
$thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6)); $thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6));
$offset += 6; $offset += 6;
@ -186,8 +187,8 @@ class getid3_optimfrog extends getid3_handler
} }
// Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
$BlockData .= fread($this->getid3->fp, 14); $BlockData .= $this->fread(14);
fseek($this->getid3->fp, $BlockSize - 14, SEEK_CUR); $this->fseek($BlockSize - 14, SEEK_CUR);
$COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); $COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
$offset += 4; $offset += 4;
@ -224,7 +225,7 @@ class getid3_optimfrog extends getid3_handler
$thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['offset'] = $BlockOffset;
$thisfile_ofr_thisblock['size'] = $BlockSize; $thisfile_ofr_thisblock['size'] = $BlockSize;
$RIFFdata .= fread($this->getid3->fp, $BlockSize); $RIFFdata .= $this->fread($BlockSize);
break; break;
case 'TAIL': case 'TAIL':
@ -232,7 +233,7 @@ class getid3_optimfrog extends getid3_handler
$thisfile_ofr_thisblock['size'] = $BlockSize; $thisfile_ofr_thisblock['size'] = $BlockSize;
if ($BlockSize > 0) { if ($BlockSize > 0) {
$RIFFdata .= fread($this->getid3->fp, $BlockSize); $RIFFdata .= $this->fread($BlockSize);
} }
break; break;
@ -242,7 +243,7 @@ class getid3_optimfrog extends getid3_handler
$thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['offset'] = $BlockOffset;
$thisfile_ofr_thisblock['size'] = $BlockSize; $thisfile_ofr_thisblock['size'] = $BlockSize;
fseek($this->getid3->fp, $BlockSize, SEEK_CUR); $this->fseek($BlockSize, SEEK_CUR);
break; break;
@ -253,7 +254,7 @@ class getid3_optimfrog extends getid3_handler
$thisfile_ofr_thisblock['size'] = $BlockSize; $thisfile_ofr_thisblock['size'] = $BlockSize;
$info['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.$this->getid3->version().') of getID3()'; $info['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.$this->getid3->version().') of getID3()';
fseek($this->getid3->fp, $BlockSize, SEEK_CUR); $this->fseek($BlockSize, SEEK_CUR);
break; break;
@ -265,14 +266,14 @@ class getid3_optimfrog extends getid3_handler
if ($BlockSize == 16) { if ($BlockSize == 16) {
$thisfile_ofr_thisblock['md5_binary'] = fread($this->getid3->fp, $BlockSize); $thisfile_ofr_thisblock['md5_binary'] = $this->fread($BlockSize);
$thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false); $thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false);
$info['md5_data_source'] = $thisfile_ofr_thisblock['md5_string']; $info['md5_data_source'] = $thisfile_ofr_thisblock['md5_string'];
} else { } else {
$info['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead'; $info['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead';
fseek($this->getid3->fp, $BlockSize, SEEK_CUR); $this->fseek($BlockSize, SEEK_CUR);
} }
break; break;
@ -283,7 +284,7 @@ class getid3_optimfrog extends getid3_handler
$thisfile_ofr_thisblock['size'] = $BlockSize; $thisfile_ofr_thisblock['size'] = $BlockSize;
$info['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset']; $info['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset'];
fseek($this->getid3->fp, $BlockSize, SEEK_CUR); $this->fseek($BlockSize, SEEK_CUR);
break; break;
} }
} }
@ -313,7 +314,7 @@ class getid3_optimfrog extends getid3_handler
} }
static function OptimFROGsampleTypeLookup($SampleType) { public static function OptimFROGsampleTypeLookup($SampleType) {
static $OptimFROGsampleTypeLookup = array( static $OptimFROGsampleTypeLookup = array(
0 => 'unsigned int (8-bit)', 0 => 'unsigned int (8-bit)',
1 => 'signed int (8-bit)', 1 => 'signed int (8-bit)',
@ -330,7 +331,7 @@ class getid3_optimfrog extends getid3_handler
return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false); return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false);
} }
static function OptimFROGbitsPerSampleTypeLookup($SampleType) { public static function OptimFROGbitsPerSampleTypeLookup($SampleType) {
static $OptimFROGbitsPerSampleTypeLookup = array( static $OptimFROGbitsPerSampleTypeLookup = array(
0 => 8, 0 => 8,
1 => 8, 1 => 8,
@ -347,7 +348,7 @@ class getid3_optimfrog extends getid3_handler
return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false); return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false);
} }
static function OptimFROGchannelConfigurationLookup($ChannelConfiguration) { public static function OptimFROGchannelConfigurationLookup($ChannelConfiguration) {
static $OptimFROGchannelConfigurationLookup = array( static $OptimFROGchannelConfigurationLookup = array(
0 => 'mono', 0 => 'mono',
1 => 'stereo' 1 => 'stereo'
@ -355,7 +356,7 @@ class getid3_optimfrog extends getid3_handler
return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false); return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false);
} }
static function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) { public static function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) {
static $OptimFROGchannelConfigNumChannelsLookup = array( static $OptimFROGchannelConfigNumChannelsLookup = array(
0 => 1, 0 => 1,
1 => 2 1 => 2
@ -371,7 +372,7 @@ class getid3_optimfrog extends getid3_handler
// } // }
static function OptimFROGencoderNameLookup($EncoderID) { public static function OptimFROGencoderNameLookup($EncoderID) {
// version = (encoderID >> 4) + 4500 // version = (encoderID >> 4) + 4500
// system = encoderID & 0xF // system = encoderID & 0xF
@ -386,7 +387,7 @@ class getid3_optimfrog extends getid3_handler
return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')'; return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')';
} }
static function OptimFROGcompressionLookup($CompressionID) { public static function OptimFROGcompressionLookup($CompressionID) {
// mode = compression >> 3 // mode = compression >> 3
// speedup = compression & 0x07 // speedup = compression & 0x07
@ -408,7 +409,7 @@ class getid3_optimfrog extends getid3_handler
return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')'); return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')');
} }
static function OptimFROGspeedupLookup($CompressionID) { public static function OptimFROGspeedupLookup($CompressionID) {
// mode = compression >> 3 // mode = compression >> 3
// speedup = compression & 0x07 // speedup = compression & 0x07
@ -424,6 +425,3 @@ class getid3_optimfrog extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,11 +18,11 @@
class getid3_rkau extends getid3_handler class getid3_rkau extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$RKAUHeader = fread($this->getid3->fp, 20); $RKAUHeader = $this->fread(20);
$magic = 'RKA'; $magic = 'RKA';
if (substr($RKAUHeader, 0, 3) != $magic) { if (substr($RKAUHeader, 0, 3) != $magic) {
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($RKAUHeader, 0, 3)).'"'; $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($RKAUHeader, 0, 3)).'"';
@ -76,7 +77,7 @@ class getid3_rkau extends getid3_handler
} }
function RKAUqualityLookup(&$RKAUdata) { public function RKAUqualityLookup(&$RKAUdata) {
$level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4; $level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4;
$quality = $RKAUdata['raw']['quality'] & 0x0F; $quality = $RKAUdata['raw']['quality'] & 0x0F;
@ -90,5 +91,3 @@ class getid3_rkau extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,12 +18,12 @@
class getid3_shorten extends getid3_handler class getid3_shorten extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$ShortenHeader = fread($this->getid3->fp, 8); $ShortenHeader = $this->fread(8);
$magic = 'ajkg'; $magic = 'ajkg';
if (substr($ShortenHeader, 0, 4) != $magic) { if (substr($ShortenHeader, 0, 4) != $magic) {
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($ShortenHeader, 0, 4)).'"'; $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($ShortenHeader, 0, 4)).'"';
@ -35,14 +36,14 @@ class getid3_shorten extends getid3_handler
$info['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1)); $info['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1));
fseek($this->getid3->fp, $info['avdataend'] - 12, SEEK_SET); $this->fseek($info['avdataend'] - 12);
$SeekTableSignatureTest = fread($this->getid3->fp, 12); $SeekTableSignatureTest = $this->fread(12);
$info['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK'); $info['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK');
if ($info['shn']['seektable']['present']) { if ($info['shn']['seektable']['present']) {
$info['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4)); $info['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4));
$info['shn']['seektable']['offset'] = $info['avdataend'] - $info['shn']['seektable']['length']; $info['shn']['seektable']['offset'] = $info['avdataend'] - $info['shn']['seektable']['length'];
fseek($this->getid3->fp, $info['shn']['seektable']['offset'], SEEK_SET); $this->fseek($info['shn']['seektable']['offset']);
$SeekTableMagic = fread($this->getid3->fp, 4); $SeekTableMagic = $this->fread(4);
$magic = 'SEEK'; $magic = 'SEEK';
if ($SeekTableMagic != $magic) { if ($SeekTableMagic != $magic) {
@ -67,7 +68,7 @@ class getid3_shorten extends getid3_handler
// long Offset1[4]; // long Offset1[4];
// }TSeekEntry; // }TSeekEntry;
$SeekTableData = fread($this->getid3->fp, $info['shn']['seektable']['length'] - 16); $SeekTableData = $this->fread($info['shn']['seektable']['length'] - 16);
$info['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80); $info['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80);
//$info['shn']['seektable']['entries'] = array(); //$info['shn']['seektable']['entries'] = array();
//$SeekTableOffset = 0; //$SeekTableOffset = 0;
@ -150,7 +151,7 @@ class getid3_shorten extends getid3_handler
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
$fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4)); $fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4));
$DecodedWAVFORMATEX = getid3_riff::RIFFparseWAVEFORMATex(substr($output, 20, $fmt_size)); $DecodedWAVFORMATEX = getid3_riff::parseWAVEFORMATex(substr($output, 20, $fmt_size));
$info['audio']['channels'] = $DecodedWAVFORMATEX['channels']; $info['audio']['channels'] = $DecodedWAVFORMATEX['channels'];
$info['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample']; $info['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample'];
$info['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate']; $info['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate'];
@ -179,5 +180,3 @@ class getid3_shorten extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,7 +18,7 @@
class getid3_tta extends getid3_handler class getid3_tta extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'tta'; $info['fileformat'] = 'tta';
@ -25,8 +26,8 @@ class getid3_tta extends getid3_handler
$info['audio']['lossless'] = true; $info['audio']['lossless'] = true;
$info['audio']['bitrate_mode'] = 'vbr'; $info['audio']['bitrate_mode'] = 'vbr';
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$ttaheader = fread($this->getid3->fp, 26); $ttaheader = $this->fread(26);
$info['tta']['magic'] = substr($ttaheader, 0, 3); $info['tta']['magic'] = substr($ttaheader, 0, 3);
$magic = 'TTA'; $magic = 'TTA';
@ -77,7 +78,7 @@ class getid3_tta extends getid3_handler
$info['tta']['major_version'] = 3; $info['tta']['major_version'] = 3;
$info['avdataoffset'] += 26; $info['avdataoffset'] += 26;
$info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::RIFFwFormatTagLookup() $info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::wFormatTagLookup()
$info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); $info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
$info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); $info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2));
$info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4)); $info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4));
@ -104,6 +105,3 @@ class getid3_tta extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,12 +18,12 @@
class getid3_voc extends getid3_handler class getid3_voc extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$OriginalAVdataOffset = $info['avdataoffset']; $OriginalAVdataOffset = $info['avdataoffset'];
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$VOCheader = fread($this->getid3->fp, 26); $VOCheader = $this->fread(26);
$magic = 'Creative Voice File'; $magic = 'Creative Voice File';
if (substr($VOCheader, 0, 19) != $magic) { if (substr($VOCheader, 0, 19) != $magic) {
@ -56,8 +57,8 @@ class getid3_voc extends getid3_handler
do { do {
$BlockOffset = ftell($this->getid3->fp); $BlockOffset = $this->ftell();
$BlockData = fread($this->getid3->fp, 4); $BlockData = $this->fread(4);
$BlockType = ord($BlockData{0}); $BlockType = ord($BlockData{0});
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3)); $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3));
$ThisBlock = array(); $ThisBlock = array();
@ -69,11 +70,11 @@ class getid3_voc extends getid3_handler
break; break;
case 1: // Sound data case 1: // Sound data
$BlockData .= fread($this->getid3->fp, 2); $BlockData .= $this->fread(2);
if ($info['avdataoffset'] <= $OriginalAVdataOffset) { if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
$info['avdataoffset'] = ftell($this->getid3->fp); $info['avdataoffset'] = $this->ftell();
} }
fseek($this->getid3->fp, $BlockSize - 2, SEEK_CUR); $this->fseek($BlockSize - 2, SEEK_CUR);
$ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1)); $ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1));
$ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1)); $ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1));
@ -96,11 +97,11 @@ class getid3_voc extends getid3_handler
case 6: // Repeat case 6: // Repeat
case 7: // End repeat case 7: // End repeat
// nothing useful, just skip // nothing useful, just skip
fseek($this->getid3->fp, $BlockSize, SEEK_CUR); $this->fseek($BlockSize, SEEK_CUR);
break; break;
case 8: // Extended case 8: // Extended
$BlockData .= fread($this->getid3->fp, 4); $BlockData .= $this->fread(4);
//00-01 Time Constant: //00-01 Time Constant:
// Mono: 65536 - (256000000 / sample_rate) // Mono: 65536 - (256000000 / sample_rate)
@ -114,11 +115,11 @@ class getid3_voc extends getid3_handler
break; break;
case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit
$BlockData .= fread($this->getid3->fp, 12); $BlockData .= $this->fread(12);
if ($info['avdataoffset'] <= $OriginalAVdataOffset) { if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
$info['avdataoffset'] = ftell($this->getid3->fp); $info['avdataoffset'] = $this->ftell();
} }
fseek($this->getid3->fp, $BlockSize - 12, SEEK_CUR); $this->fseek($BlockSize - 12, SEEK_CUR);
$ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); $ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
$ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1)); $ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1));
@ -137,7 +138,7 @@ class getid3_voc extends getid3_handler
default: default:
$info['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset; $info['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset;
fseek($this->getid3->fp, $BlockSize, SEEK_CUR); $this->fseek($BlockSize, SEEK_CUR);
break; break;
} }
@ -151,7 +152,7 @@ class getid3_voc extends getid3_handler
} while (!feof($this->getid3->fp) && ($BlockType != 0)); } while (!feof($this->getid3->fp) && ($BlockType != 0));
// Terminator block doesn't have size field, so seek back 3 spaces // Terminator block doesn't have size field, so seek back 3 spaces
fseek($this->getid3->fp, -3, SEEK_CUR); $this->fseek(-3, SEEK_CUR);
ksort($thisfile_voc['blocktypes']); ksort($thisfile_voc['blocktypes']);
@ -163,7 +164,7 @@ class getid3_voc extends getid3_handler
return true; return true;
} }
function VOCcompressionTypeLookup($index) { public function VOCcompressionTypeLookup($index) {
static $VOCcompressionTypeLookup = array( static $VOCcompressionTypeLookup = array(
0 => '8-bit', 0 => '8-bit',
1 => '4-bit', 1 => '4-bit',
@ -173,7 +174,7 @@ class getid3_voc extends getid3_handler
return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels'); return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels');
} }
function VOCwFormatLookup($index) { public function VOCwFormatLookup($index) {
static $VOCwFormatLookup = array( static $VOCwFormatLookup = array(
0x0000 => '8-bit unsigned PCM', 0x0000 => '8-bit unsigned PCM',
0x0001 => 'Creative 8-bit to 4-bit ADPCM', 0x0001 => 'Creative 8-bit to 4-bit ADPCM',
@ -187,21 +188,18 @@ class getid3_voc extends getid3_handler
return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
} }
function VOCwFormatActualBitsPerSampleLookup($index) { public function VOCwFormatActualBitsPerSampleLookup($index) {
static $VOCwFormatLookup = array( static $VOCwFormatLookup = array(
0x0000 => 8, 0x0000 => 8,
0x0001 => 4, 0x0001 => 4,
0x0002 => 3, 0x0002 => 3,
0x0003 => 2, 0x0003 => 2,
0x0004 => 16, 0x0004 => 16,
0x0006 => 8, 0x0006 => 8,
0x0007 => 8, 0x0007 => 8,
0x2000 => 4 0x2000 => 4
); );
return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -16,10 +17,10 @@
class getid3_vqf extends getid3_handler class getid3_vqf extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de> // based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de>
// http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
$info['fileformat'] = 'vqf'; $info['fileformat'] = 'vqf';
@ -32,8 +33,8 @@ class getid3_vqf extends getid3_handler
$thisfile_vqf = &$info['vqf']; $thisfile_vqf = &$info['vqf'];
$thisfile_vqf_raw = &$thisfile_vqf['raw']; $thisfile_vqf_raw = &$thisfile_vqf['raw'];
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$VQFheaderData = fread($this->getid3->fp, 16); $VQFheaderData = $this->fread(16);
$offset = 0; $offset = 0;
$thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4); $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4);
@ -50,11 +51,11 @@ class getid3_vqf extends getid3_handler
$thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4)); $thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4));
$offset += 4; $offset += 4;
while (ftell($this->getid3->fp) < $info['avdataend']) { while ($this->ftell() < $info['avdataend']) {
$ChunkBaseOffset = ftell($this->getid3->fp); $ChunkBaseOffset = $this->ftell();
$chunkoffset = 0; $chunkoffset = 0;
$ChunkData = fread($this->getid3->fp, 8); $ChunkData = $this->fread(8);
$ChunkName = substr($ChunkData, $chunkoffset, 4); $ChunkName = substr($ChunkData, $chunkoffset, 4);
if ($ChunkName == 'DATA') { if ($ChunkName == 'DATA') {
$info['avdataoffset'] = $ChunkBaseOffset; $info['avdataoffset'] = $ChunkBaseOffset;
@ -63,12 +64,12 @@ class getid3_vqf extends getid3_handler
$chunkoffset += 4; $chunkoffset += 4;
$ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
$chunkoffset += 4; $chunkoffset += 4;
if ($ChunkSize > ($info['avdataend'] - ftell($this->getid3->fp))) { if ($ChunkSize > ($info['avdataend'] - $this->ftell())) {
$info['error'][] = 'Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset; $info['error'][] = 'Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset;
break; break;
} }
if ($ChunkSize > 0) { if ($ChunkSize > 0) {
$ChunkData .= fread($this->getid3->fp, $ChunkSize); $ChunkData .= $this->fread($ChunkSize);
} }
switch ($ChunkName) { switch ($ChunkName) {
@ -135,7 +136,7 @@ class getid3_vqf extends getid3_handler
return true; return true;
} }
function VQFchannelFrequencyLookup($frequencyid) { public function VQFchannelFrequencyLookup($frequencyid) {
static $VQFchannelFrequencyLookup = array( static $VQFchannelFrequencyLookup = array(
11 => 11025, 11 => 11025,
22 => 22050, 22 => 22050,
@ -144,7 +145,7 @@ class getid3_vqf extends getid3_handler
return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000); return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000);
} }
function VQFcommentNiceNameLookup($shortname) { public function VQFcommentNiceNameLookup($shortname) {
static $VQFcommentNiceNameLookup = array( static $VQFcommentNiceNameLookup = array(
'NAME' => 'title', 'NAME' => 'title',
'AUTH' => 'artist', 'AUTH' => 'artist',
@ -157,6 +158,3 @@ class getid3_vqf extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,16 +18,16 @@
class getid3_wavpack extends getid3_handler class getid3_wavpack extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
while (true) { while (true) {
$wavpackheader = fread($this->getid3->fp, 32); $wavpackheader = $this->fread(32);
if (ftell($this->getid3->fp) >= $info['avdataend']) { if ($this->ftell() >= $info['avdataend']) {
break; break;
} elseif (feof($this->getid3->fp)) { } elseif (feof($this->getid3->fp)) {
break; break;
@ -40,7 +41,7 @@ class getid3_wavpack extends getid3_handler
break; break;
} }
$blockheader_offset = ftell($this->getid3->fp) - 32; $blockheader_offset = $this->ftell() - 32;
$blockheader_magic = substr($wavpackheader, 0, 4); $blockheader_magic = substr($wavpackheader, 0, 4);
$blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4)); $blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4));
@ -143,10 +144,10 @@ class getid3_wavpack extends getid3_handler
$info['audio']['lossless'] = !$info['wavpack']['blockheader']['flags']['hybrid']; $info['audio']['lossless'] = !$info['wavpack']['blockheader']['flags']['hybrid'];
} }
while (!feof($this->getid3->fp) && (ftell($this->getid3->fp) < ($blockheader_offset + $blockheader_size + 8))) { while (!feof($this->getid3->fp) && ($this->ftell() < ($blockheader_offset + $blockheader_size + 8))) {
$metablock = array('offset'=>ftell($this->getid3->fp)); $metablock = array('offset'=>$this->ftell());
$metablockheader = fread($this->getid3->fp, 2); $metablockheader = $this->fread(2);
if (feof($this->getid3->fp)) { if (feof($this->getid3->fp)) {
break; break;
} }
@ -166,7 +167,7 @@ class getid3_wavpack extends getid3_handler
$metablock['padded_data'] = (bool) ($metablock['id'] & 0x40); $metablock['padded_data'] = (bool) ($metablock['id'] & 0x40);
$metablock['large_block'] = (bool) ($metablock['id'] & 0x80); $metablock['large_block'] = (bool) ($metablock['id'] & 0x80);
if ($metablock['large_block']) { if ($metablock['large_block']) {
$metablockheader .= fread($this->getid3->fp, 2); $metablockheader .= $this->fread(2);
} }
$metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words $metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words
$metablock['data'] = null; $metablock['data'] = null;
@ -180,7 +181,7 @@ class getid3_wavpack extends getid3_handler
case 0x24: // ID_CUESHEET case 0x24: // ID_CUESHEET
case 0x25: // ID_CONFIG_BLOCK case 0x25: // ID_CONFIG_BLOCK
case 0x26: // ID_MD5_CHECKSUM case 0x26: // ID_MD5_CHECKSUM
$metablock['data'] = fread($this->getid3->fp, $metablock['size']); $metablock['data'] = $this->fread($metablock['size']);
if ($metablock['padded_data']) { if ($metablock['padded_data']) {
// padded to the nearest even byte // padded to the nearest even byte
@ -203,12 +204,12 @@ class getid3_wavpack extends getid3_handler
case 0x0B: // ID_WVC_BITSTREAM case 0x0B: // ID_WVC_BITSTREAM
case 0x0C: // ID_WVX_BITSTREAM case 0x0C: // ID_WVX_BITSTREAM
case 0x0D: // ID_CHANNEL_INFO case 0x0D: // ID_CHANNEL_INFO
fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); $this->fseek($metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size']);
break; break;
default: default:
$info['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset']; $info['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset'];
fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); $this->fseek($metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size']);
break; break;
} }
@ -242,12 +243,12 @@ class getid3_wavpack extends getid3_handler
$getid3_temp = new getID3(); $getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename); $getid3_temp->openfile($this->getid3->filename);
$getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_temp->info['fileformat'] = 'riff'; //$getid3_temp->info['fileformat'] = 'riff';
$getid3_riff = new getid3_riff($getid3_temp); $getid3_riff = new getid3_riff($getid3_temp);
$metablock['riff'] = $getid3_riff->ParseRIFF($startoffset, $startoffset + $metablock['size']); $metablock['riff'] = $getid3_riff->ParseRIFF($startoffset, $startoffset + $metablock['size']);
if (!empty($metablock['riff']['INFO'])) { if (!empty($metablock['riff']['INFO'])) {
$getid3_riff->RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']); getid3_riff::parseComments($metablock['riff']['INFO'], $metablock['comments']);
$info['tags']['riff'] = $metablock['comments']; $info['tags']['riff'] = $metablock['comments'];
} }
unset($getid3_temp, $getid3_riff); unset($getid3_temp, $getid3_riff);
@ -368,7 +369,7 @@ class getid3_wavpack extends getid3_handler
} }
function WavPackMetablockNameLookup(&$id) { public function WavPackMetablockNameLookup(&$id) {
static $WavPackMetablockNameLookup = array( static $WavPackMetablockNameLookup = array(
0x00 => 'Dummy', 0x00 => 'Dummy',
0x01 => 'Encoder Info', 0x01 => 'Encoder Info',
@ -395,6 +396,3 @@ class getid3_wavpack extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -16,10 +17,10 @@
class getid3_bmp extends getid3_handler class getid3_bmp extends getid3_handler
{ {
var $ExtractPalette = false; public $ExtractPalette = false;
var $ExtractData = false; public $ExtractData = false;
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// shortcuts // shortcuts
@ -36,9 +37,9 @@ class getid3_bmp extends getid3_handler
// WORD bfReserved2; // WORD bfReserved2;
// DWORD bfOffBits; // DWORD bfOffBits;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$offset = 0; $offset = 0;
$BMPheader = fread($this->getid3->fp, 14 + 40); $BMPheader = $this->fread(14 + 40);
$thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2); $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2);
$offset += 2; $offset += 2;
@ -220,7 +221,7 @@ class getid3_bmp extends getid3_handler
if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) { if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) {
// should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen
$BMPheader .= fread($this->getid3->fp, 44); $BMPheader .= $this->fread(44);
// BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
// Win95+, WinNT4.0+ // Win95+, WinNT4.0+
@ -262,7 +263,7 @@ class getid3_bmp extends getid3_handler
} }
if ($thisfile_bmp['type_version'] >= 5) { if ($thisfile_bmp['type_version'] >= 5) {
$BMPheader .= fread($this->getid3->fp, 16); $BMPheader .= $this->fread(16);
// BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
// Win98+, Win2000+ // Win98+, Win2000+
@ -296,7 +297,7 @@ class getid3_bmp extends getid3_handler
$PaletteEntries = $thisfile_bmp_header_raw['colors_used']; $PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
} }
if ($PaletteEntries > 0) { if ($PaletteEntries > 0) {
$BMPpalette = fread($this->getid3->fp, 4 * $PaletteEntries); $BMPpalette = $this->fread(4 * $PaletteEntries);
$paletteoffset = 0; $paletteoffset = 0;
for ($i = 0; $i < $PaletteEntries; $i++) { for ($i = 0; $i < $PaletteEntries; $i++) {
// RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp // RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
@ -318,9 +319,9 @@ class getid3_bmp extends getid3_handler
} }
if ($this->ExtractData) { if ($this->ExtractData) {
fseek($this->getid3->fp, $thisfile_bmp_header_raw['data_offset'], SEEK_SET); $this->fseek($thisfile_bmp_header_raw['data_offset']);
$RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry
$BMPpixelData = fread($this->getid3->fp, $thisfile_bmp_header_raw['height'] * $RowByteLength); $BMPpixelData = $this->fread($thisfile_bmp_header_raw['height'] * $RowByteLength);
$pixeldataoffset = 0; $pixeldataoffset = 0;
$thisfile_bmp_header_raw['compression'] = (isset($thisfile_bmp_header_raw['compression']) ? $thisfile_bmp_header_raw['compression'] : ''); $thisfile_bmp_header_raw['compression'] = (isset($thisfile_bmp_header_raw['compression']) ? $thisfile_bmp_header_raw['compression'] : '');
switch ($thisfile_bmp_header_raw['compression']) { switch ($thisfile_bmp_header_raw['compression']) {
@ -625,7 +626,7 @@ class getid3_bmp extends getid3_handler
} }
function PlotBMP(&$BMPinfo) { public function PlotBMP(&$BMPinfo) {
$starttime = time(); $starttime = time();
if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) { if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) {
echo 'ERROR: no pixel data<BR>'; echo 'ERROR: no pixel data<BR>';
@ -661,7 +662,7 @@ class getid3_bmp extends getid3_handler
return false; return false;
} }
function BMPcompressionWindowsLookup($compressionid) { public function BMPcompressionWindowsLookup($compressionid) {
static $BMPcompressionWindowsLookup = array( static $BMPcompressionWindowsLookup = array(
0 => 'BI_RGB', 0 => 'BI_RGB',
1 => 'BI_RLE8', 1 => 'BI_RLE8',
@ -673,7 +674,7 @@ class getid3_bmp extends getid3_handler
return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid'); return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid');
} }
function BMPcompressionOS2Lookup($compressionid) { public function BMPcompressionOS2Lookup($compressionid) {
static $BMPcompressionOS2Lookup = array( static $BMPcompressionOS2Lookup = array(
0 => 'BI_RGB', 0 => 'BI_RGB',
1 => 'BI_RLE8', 1 => 'BI_RLE8',
@ -685,6 +686,3 @@ class getid3_bmp extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,11 +18,11 @@
class getid3_efax extends getid3_handler class getid3_efax extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$efaxheader = fread($this->getid3->fp, 1024); $efaxheader = $this->fread(1024);
$info['efax']['header']['magic'] = substr($efaxheader, 0, 2); $info['efax']['header']['magic'] = substr($efaxheader, 0, 2);
if ($info['efax']['header']['magic'] != "\xDC\xFE") { if ($info['efax']['header']['magic'] != "\xDC\xFE") {
@ -48,6 +49,3 @@ return false;
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,7 +18,7 @@
class getid3_gif extends getid3_handler class getid3_gif extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'gif'; $info['fileformat'] = 'gif';
@ -25,8 +26,8 @@ class getid3_gif extends getid3_handler
$info['video']['lossless'] = true; $info['video']['lossless'] = true;
$info['video']['pixel_aspect_ratio'] = (float) 1; $info['video']['pixel_aspect_ratio'] = (float) 1;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$GIFheader = fread($this->getid3->fp, 13); $GIFheader = $this->fread(13);
$offset = 0; $offset = 0;
$info['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3); $info['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3);
@ -78,7 +79,7 @@ class getid3_gif extends getid3_handler
} }
// if ($info['gif']['header']['flags']['global_color_table']) { // if ($info['gif']['header']['flags']['global_color_table']) {
// $GIFcolorTable = fread($this->getid3->fp, 3 * $info['gif']['header']['global_color_size']); // $GIFcolorTable = $this->fread(3 * $info['gif']['header']['global_color_size']);
// $offset = 0; // $offset = 0;
// for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) { // for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) {
// $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); // $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
@ -90,12 +91,12 @@ class getid3_gif extends getid3_handler
// //
// // Image Descriptor // // Image Descriptor
// while (!feof($this->getid3->fp)) { // while (!feof($this->getid3->fp)) {
// $NextBlockTest = fread($this->getid3->fp, 1); // $NextBlockTest = $this->fread(1);
// switch ($NextBlockTest) { // switch ($NextBlockTest) {
// //
// case ',': // ',' - Image separator character // case ',': // ',' - Image separator character
// //
// $ImageDescriptorData = $NextBlockTest.fread($this->getid3->fp, 9); // $ImageDescriptorData = $NextBlockTest.$this->fread(9);
// $ImageDescriptor = array(); // $ImageDescriptor = array();
// $ImageDescriptor['image_left'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2)); // $ImageDescriptor['image_left'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2));
// $ImageDescriptor['image_top'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2)); // $ImageDescriptor['image_top'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2));
@ -112,10 +113,10 @@ class getid3_gif extends getid3_handler
// return true; // return true;
// //
// } // }
//echo 'Start of raster data: '.ftell($this->getid3->fp).'<BR>'; //echo 'Start of raster data: '.$this->ftell().'<BR>';
// $RasterData = array(); // $RasterData = array();
// $RasterData['code_size'] = getid3_lib::LittleEndian2Int(fread($this->getid3->fp, 1)); // $RasterData['code_size'] = getid3_lib::LittleEndian2Int($this->fread(1));
// $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int(fread($this->getid3->fp, 1)); // $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int($this->fread(1));
// $info['gif']['raster_data'][count($info['gif']['image_descriptor']) - 1] = $RasterData; // $info['gif']['raster_data'][count($info['gif']['image_descriptor']) - 1] = $RasterData;
// //
// $CurrentCodeSize = $RasterData['code_size'] + 1; // $CurrentCodeSize = $RasterData['code_size'] + 1;
@ -143,16 +144,16 @@ class getid3_gif extends getid3_handler
// //
// case '!': // case '!':
// // GIF Extension Block // // GIF Extension Block
// $ExtensionBlockData = $NextBlockTest.fread($this->getid3->fp, 2); // $ExtensionBlockData = $NextBlockTest.$this->fread(2);
// $ExtensionBlock = array(); // $ExtensionBlock = array();
// $ExtensionBlock['function_code'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1)); // $ExtensionBlock['function_code'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1));
// $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1)); // $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1));
// $ExtensionBlock['data'] = fread($this->getid3->fp, $ExtensionBlock['byte_length']); // $ExtensionBlock['data'] = $this->fread($ExtensionBlock['byte_length']);
// $info['gif']['extension_blocks'][] = $ExtensionBlock; // $info['gif']['extension_blocks'][] = $ExtensionBlock;
// break; // break;
// //
// case ';': // case ';':
// $info['gif']['terminator_offset'] = ftell($this->getid3->fp) - 1; // $info['gif']['terminator_offset'] = $this->ftell() - 1;
// // GIF Terminator // // GIF Terminator
// break; // break;
// //
@ -167,10 +168,10 @@ class getid3_gif extends getid3_handler
} }
function GetLSBits($bits) { public function GetLSBits($bits) {
static $bitbuffer = ''; static $bitbuffer = '';
while (strlen($bitbuffer) < $bits) { while (strlen($bitbuffer) < $bits) {
$bitbuffer = str_pad(decbin(ord(fread($this->getid3->fp, 1))), 8, '0', STR_PAD_LEFT).$bitbuffer; $bitbuffer = str_pad(decbin(ord($this->fread(1))), 8, '0', STR_PAD_LEFT).$bitbuffer;
} }
$value = bindec(substr($bitbuffer, 0 - $bits)); $value = bindec(substr($bitbuffer, 0 - $bits));
$bitbuffer = substr($bitbuffer, 0, 0 - $bits); $bitbuffer = substr($bitbuffer, 0, 0 - $bits);
@ -179,6 +180,3 @@ class getid3_gif extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -19,7 +20,7 @@ class getid3_jpg extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'jpg'; $info['fileformat'] = 'jpg';
@ -28,10 +29,11 @@ class getid3_jpg extends getid3_handler
$info['video']['bits_per_sample'] = 24; $info['video']['bits_per_sample'] = 24;
$info['video']['pixel_aspect_ratio'] = (float) 1; $info['video']['pixel_aspect_ratio'] = (float) 1;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$imageinfo = array(); $imageinfo = array();
list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($this->getid3->fp, $info['filesize']), $imageinfo); //list($width, $height, $type) = getid3_lib::GetDataImageSize($this->fread($info['filesize']), $imageinfo);
list($width, $height, $type) = getimagesize($info['filenamepath'], $imageinfo); // http://www.getid3.org/phpBB3/viewtopic.php?t=1474
if (isset($imageinfo['APP13'])) { if (isset($imageinfo['APP13'])) {
@ -45,10 +47,10 @@ class getid3_jpg extends getid3_handler
foreach ($iptc_values as $key => $value) { foreach ($iptc_values as $key => $value) {
$IPTCrecordName = $this->IPTCrecordName($iptc_record); $IPTCrecordName = $this->IPTCrecordName($iptc_record);
$IPTCrecordTagName = $this->IPTCrecordTagName($iptc_record, $iptc_tagkey); $IPTCrecordTagName = $this->IPTCrecordTagName($iptc_record, $iptc_tagkey);
if (isset($info['iptc'][$IPTCrecordName][$IPTCrecordTagName])) { if (isset($info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName])) {
$info['iptc'][$IPTCrecordName][$IPTCrecordTagName][] = $value; $info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName][] = $value;
} else { } else {
$info['iptc'][$IPTCrecordName][$IPTCrecordTagName] = array($value); $info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName] = array($value);
} }
} }
} }
@ -64,7 +66,9 @@ class getid3_jpg extends getid3_handler
if (isset($imageinfo['APP1'])) { if (isset($imageinfo['APP1'])) {
if (function_exists('exif_read_data')) { if (function_exists('exif_read_data')) {
if (substr($imageinfo['APP1'], 0, 4) == 'Exif') { if (substr($imageinfo['APP1'], 0, 4) == 'Exif') {
$info['jpg']['exif'] = @exif_read_data($info['filenamepath'], '', true, false); //$info['warning'][] = 'known issue: https://bugs.php.net/bug.php?id=62523';
//return false;
$info['jpg']['exif'] = exif_read_data($info['filenamepath'], null, true, false);
} else { } else {
$info['warning'][] = 'exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "'.substr($imageinfo['APP1'], 0, 4).'")'; $info['warning'][] = 'exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "'.substr($imageinfo['APP1'], 0, 4).'")';
} }
@ -105,19 +109,13 @@ class getid3_jpg extends getid3_handler
$computed_time[3] = (isset($explodedGPSDateStamp[1]) ? $explodedGPSDateStamp[1] : ''); $computed_time[3] = (isset($explodedGPSDateStamp[1]) ? $explodedGPSDateStamp[1] : '');
$computed_time[4] = (isset($explodedGPSDateStamp[2]) ? $explodedGPSDateStamp[2] : ''); $computed_time[4] = (isset($explodedGPSDateStamp[2]) ? $explodedGPSDateStamp[2] : '');
if (function_exists('date_default_timezone_set')) {
date_default_timezone_set('UTC');
} else {
ini_set('date.timezone', 'UTC');
}
$computed_time = array(0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0); $computed_time = array(0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0);
if (isset($info['jpg']['exif']['GPS']['GPSTimeStamp']) && is_array($info['jpg']['exif']['GPS']['GPSTimeStamp'])) { if (isset($info['jpg']['exif']['GPS']['GPSTimeStamp']) && is_array($info['jpg']['exif']['GPS']['GPSTimeStamp'])) {
foreach ($info['jpg']['exif']['GPS']['GPSTimeStamp'] as $key => $value) { foreach ($info['jpg']['exif']['GPS']['GPSTimeStamp'] as $key => $value) {
$computed_time[$key] = getid3_lib::DecimalizeFraction($value); $computed_time[$key] = getid3_lib::DecimalizeFraction($value);
} }
} }
$info['jpg']['exif']['GPS']['computed']['timestamp'] = mktime($computed_time[0], $computed_time[1], $computed_time[2], $computed_time[3], $computed_time[4], $computed_time[5]); $info['jpg']['exif']['GPS']['computed']['timestamp'] = gmmktime($computed_time[0], $computed_time[1], $computed_time[2], $computed_time[3], $computed_time[4], $computed_time[5]);
} }
if (isset($info['jpg']['exif']['GPS']['GPSLatitude']) && is_array($info['jpg']['exif']['GPS']['GPSLatitude'])) { if (isset($info['jpg']['exif']['GPS']['GPSLatitude']) && is_array($info['jpg']['exif']['GPS']['GPSLatitude'])) {
@ -135,22 +133,27 @@ class getid3_jpg extends getid3_handler
} }
$info['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + ($computed_longitude[1] / 60) + ($computed_longitude[2] / 3600)); $info['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + ($computed_longitude[1] / 60) + ($computed_longitude[2] / 3600));
} }
if (isset($info['jpg']['exif']['GPS']['GPSAltitudeRef'])) {
$info['jpg']['exif']['GPS']['GPSAltitudeRef'] = ord($info['jpg']['exif']['GPS']['GPSAltitudeRef']); // 0 = above sea level; 1 = below sea level
}
if (isset($info['jpg']['exif']['GPS']['GPSAltitude'])) { if (isset($info['jpg']['exif']['GPS']['GPSAltitude'])) {
$direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSAltitudeRef']) && ($info['jpg']['exif']['GPS']['GPSAltitudeRef'] === chr(1))) ? -1 : 1); $direction_multiplier = (!empty($info['jpg']['exif']['GPS']['GPSAltitudeRef']) ? -1 : 1); // 0 = above sea level; 1 = below sea level
$info['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * getid3_lib::DecimalizeFraction($info['jpg']['exif']['GPS']['GPSAltitude']); $info['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * getid3_lib::DecimalizeFraction($info['jpg']['exif']['GPS']['GPSAltitude']);
} }
} }
if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.xmp.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.xmp.php', __FILE__, true);
if (isset($info['filenamepath'])) { if (isset($info['filenamepath'])) {
$image_xmp = new Image_XMP($info['filenamepath']); $image_xmp = new Image_XMP($info['filenamepath']);
$xmp_raw = $image_xmp->getAllTags(); $xmp_raw = $image_xmp->getAllTags();
foreach ($xmp_raw as $key => $value) { foreach ($xmp_raw as $key => $value) {
if (strpos($key, ':')) {
list($subsection, $tagname) = explode(':', $key); list($subsection, $tagname) = explode(':', $key);
$info['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value); $info['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value);
} else {
$info['warning'][] = 'XMP: expecting "<subsection>:<tagname>", found "'.$key.'"';
} }
} }
} }
@ -163,7 +166,7 @@ class getid3_jpg extends getid3_handler
} }
function CastAsAppropriate($value) { public function CastAsAppropriate($value) {
if (is_array($value)) { if (is_array($value)) {
return $value; return $value;
} elseif (preg_match('#^[0-9]+/[0-9]+$#', $value)) { } elseif (preg_match('#^[0-9]+/[0-9]+$#', $value)) {
@ -177,7 +180,7 @@ class getid3_jpg extends getid3_handler
} }
function IPTCrecordName($iptc_record) { public function IPTCrecordName($iptc_record) {
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
static $IPTCrecordName = array(); static $IPTCrecordName = array();
if (empty($IPTCrecordName)) { if (empty($IPTCrecordName)) {
@ -194,7 +197,7 @@ class getid3_jpg extends getid3_handler
} }
function IPTCrecordTagName($iptc_record, $iptc_tagkey) { public function IPTCrecordTagName($iptc_record, $iptc_tagkey) {
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
static $IPTCrecordTagName = array(); static $IPTCrecordTagName = array();
if (empty($IPTCrecordTagName)) { if (empty($IPTCrecordTagName)) {
@ -333,6 +336,3 @@ class getid3_jpg extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -16,9 +17,9 @@
class getid3_pcd extends getid3_handler class getid3_pcd extends getid3_handler
{ {
var $ExtractData = 0; public $ExtractData = 0;
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'pcd'; $info['fileformat'] = 'pcd';
@ -26,9 +27,9 @@ class getid3_pcd extends getid3_handler
$info['video']['lossless'] = false; $info['video']['lossless'] = false;
fseek($this->getid3->fp, $info['avdataoffset'] + 72, SEEK_SET); $this->fseek($info['avdataoffset'] + 72);
$PCDflags = fread($this->getid3->fp, 1); $PCDflags = $this->fread(1);
$PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false); $PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false);
@ -56,19 +57,19 @@ class getid3_pcd extends getid3_handler
list($PCD_width, $PCD_height, $PCD_dataOffset) = $PCD_levels[3]; list($PCD_width, $PCD_height, $PCD_dataOffset) = $PCD_levels[3];
fseek($this->getid3->fp, $info['avdataoffset'] + $PCD_dataOffset, SEEK_SET); $this->fseek($info['avdataoffset'] + $PCD_dataOffset);
for ($y = 0; $y < $PCD_height; $y += 2) { for ($y = 0; $y < $PCD_height; $y += 2) {
// The image-data of these subtypes start at the respective offsets of 02000h, 0b800h and 30000h. // The image-data of these subtypes start at the respective offsets of 02000h, 0b800h and 30000h.
// To decode the YcbYr to the more usual RGB-code, three lines of data have to be read, each // To decode the YcbYr to the more usual RGB-code, three lines of data have to be read, each
// consisting of w bytes, where w is the width of the image-subtype. The first w bytes and // consisting of ‘w’ bytes, where ‘w’ is the width of the image-subtype. The first ‘w’ bytes and
// the first half of the third w bytes contain data for the first RGB-line, the second w bytes // the first half of the third ‘w’ bytes contain data for the first RGB-line, the second ‘w’ bytes
// and the second half of the third w bytes contain data for a second RGB-line. // and the second half of the third ‘w’ bytes contain data for a second RGB-line.
$PCD_data_Y1 = fread($this->getid3->fp, $PCD_width); $PCD_data_Y1 = $this->fread($PCD_width);
$PCD_data_Y2 = fread($this->getid3->fp, $PCD_width); $PCD_data_Y2 = $this->fread($PCD_width);
$PCD_data_Cb = fread($this->getid3->fp, intval(round($PCD_width / 2))); $PCD_data_Cb = $this->fread(intval(round($PCD_width / 2)));
$PCD_data_Cr = fread($this->getid3->fp, intval(round($PCD_width / 2))); $PCD_data_Cr = $this->fread(intval(round($PCD_width / 2)));
for ($x = 0; $x < $PCD_width; $x++) { for ($x = 0; $x < $PCD_width; $x++) {
if ($PCDisVertical) { if ($PCDisVertical) {
@ -98,7 +99,7 @@ class getid3_pcd extends getid3_handler
} }
function YCbCr2RGB($Y, $Cb, $Cr) { public function YCbCr2RGB($Y, $Cb, $Cr) {
static $YCbCr_constants = array(); static $YCbCr_constants = array();
if (empty($YCbCr_constants)) { if (empty($YCbCr_constants)) {
$YCbCr_constants['red']['Y'] = 0.0054980 * 256; $YCbCr_constants['red']['Y'] = 0.0054980 * 256;
@ -130,5 +131,3 @@ class getid3_pcd extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,7 +18,7 @@
class getid3_png extends getid3_handler class getid3_png extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// shortcut // shortcut
@ -28,8 +29,8 @@ class getid3_png extends getid3_handler
$info['video']['dataformat'] = 'png'; $info['video']['dataformat'] = 'png';
$info['video']['lossless'] = false; $info['video']['lossless'] = false;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$PNGfiledata = fread($this->getid3->fp, $this->getid3->fread_buffer_size()); $PNGfiledata = $this->fread($this->getid3->fread_buffer_size());
$offset = 0; $offset = 0;
$PNGidentifier = substr($PNGfiledata, $offset, 8); // $89 $50 $4E $47 $0D $0A $1A $0A $PNGidentifier = substr($PNGfiledata, $offset, 8); // $89 $50 $4E $47 $0D $0A $1A $0A
@ -41,11 +42,11 @@ class getid3_png extends getid3_handler
return false; return false;
} }
while (((ftell($this->getid3->fp) - (strlen($PNGfiledata) - $offset)) < $info['filesize'])) { while ((($this->ftell() - (strlen($PNGfiledata) - $offset)) < $info['filesize'])) {
$chunk['data_length'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4)); $chunk['data_length'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4));
$offset += 4; $offset += 4;
while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && (ftell($this->getid3->fp) < $info['filesize'])) { while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && ($this->ftell() < $info['filesize'])) {
$PNGfiledata .= fread($this->getid3->fp, $this->getid3->fread_buffer_size()); $PNGfiledata .= $this->fread($this->getid3->fread_buffer_size());
} }
$chunk['type_text'] = substr($PNGfiledata, $offset, 4); $chunk['type_text'] = substr($PNGfiledata, $offset, 4);
$offset += 4; $offset += 4;
@ -438,7 +439,7 @@ class getid3_png extends getid3_handler
return true; return true;
} }
function PNGsRGBintentLookup($sRGB) { public function PNGsRGBintentLookup($sRGB) {
static $PNGsRGBintentLookup = array( static $PNGsRGBintentLookup = array(
0 => 'Perceptual', 0 => 'Perceptual',
1 => 'Relative colorimetric', 1 => 'Relative colorimetric',
@ -448,14 +449,14 @@ class getid3_png extends getid3_handler
return (isset($PNGsRGBintentLookup[$sRGB]) ? $PNGsRGBintentLookup[$sRGB] : 'invalid'); return (isset($PNGsRGBintentLookup[$sRGB]) ? $PNGsRGBintentLookup[$sRGB] : 'invalid');
} }
function PNGcompressionMethodLookup($compressionmethod) { public function PNGcompressionMethodLookup($compressionmethod) {
static $PNGcompressionMethodLookup = array( static $PNGcompressionMethodLookup = array(
0 => 'deflate/inflate' 0 => 'deflate/inflate'
); );
return (isset($PNGcompressionMethodLookup[$compressionmethod]) ? $PNGcompressionMethodLookup[$compressionmethod] : 'invalid'); return (isset($PNGcompressionMethodLookup[$compressionmethod]) ? $PNGcompressionMethodLookup[$compressionmethod] : 'invalid');
} }
function PNGpHYsUnitLookup($unitid) { public function PNGpHYsUnitLookup($unitid) {
static $PNGpHYsUnitLookup = array( static $PNGpHYsUnitLookup = array(
0 => 'unknown', 0 => 'unknown',
1 => 'meter' 1 => 'meter'
@ -463,7 +464,7 @@ class getid3_png extends getid3_handler
return (isset($PNGpHYsUnitLookup[$unitid]) ? $PNGpHYsUnitLookup[$unitid] : 'invalid'); return (isset($PNGpHYsUnitLookup[$unitid]) ? $PNGpHYsUnitLookup[$unitid] : 'invalid');
} }
function PNGoFFsUnitLookup($unitid) { public function PNGoFFsUnitLookup($unitid) {
static $PNGoFFsUnitLookup = array( static $PNGoFFsUnitLookup = array(
0 => 'pixel', 0 => 'pixel',
1 => 'micrometer' 1 => 'micrometer'
@ -471,7 +472,7 @@ class getid3_png extends getid3_handler
return (isset($PNGoFFsUnitLookup[$unitid]) ? $PNGoFFsUnitLookup[$unitid] : 'invalid'); return (isset($PNGoFFsUnitLookup[$unitid]) ? $PNGoFFsUnitLookup[$unitid] : 'invalid');
} }
function PNGpCALequationTypeLookup($equationtype) { public function PNGpCALequationTypeLookup($equationtype) {
static $PNGpCALequationTypeLookup = array( static $PNGpCALequationTypeLookup = array(
0 => 'Linear mapping', 0 => 'Linear mapping',
1 => 'Base-e exponential mapping', 1 => 'Base-e exponential mapping',
@ -481,7 +482,7 @@ class getid3_png extends getid3_handler
return (isset($PNGpCALequationTypeLookup[$equationtype]) ? $PNGpCALequationTypeLookup[$equationtype] : 'invalid'); return (isset($PNGpCALequationTypeLookup[$equationtype]) ? $PNGpCALequationTypeLookup[$equationtype] : 'invalid');
} }
function PNGsCALUnitLookup($unitid) { public function PNGsCALUnitLookup($unitid) {
static $PNGsCALUnitLookup = array( static $PNGsCALUnitLookup = array(
0 => 'meter', 0 => 'meter',
1 => 'radian' 1 => 'radian'
@ -489,7 +490,7 @@ class getid3_png extends getid3_handler
return (isset($PNGsCALUnitLookup[$unitid]) ? $PNGsCALUnitLookup[$unitid] : 'invalid'); return (isset($PNGsCALUnitLookup[$unitid]) ? $PNGsCALUnitLookup[$unitid] : 'invalid');
} }
function IHDRcalculateBitsPerSample($color_type, $bit_depth) { public function IHDRcalculateBitsPerSample($color_type, $bit_depth) {
switch ($color_type) { switch ($color_type) {
case 0: // Each pixel is a grayscale sample. case 0: // Each pixel is a grayscale sample.
return $bit_depth; return $bit_depth;
@ -515,6 +516,3 @@ class getid3_png extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -18,12 +19,12 @@ class getid3_svg extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$SVGheader = fread($this->getid3->fp, 4096); $SVGheader = $this->fread(4096);
if (preg_match('#\<\?xml([^\>]+)\?\>#i', $SVGheader, $matches)) { if (preg_match('#\<\?xml([^\>]+)\?\>#i', $SVGheader, $matches)) {
$info['svg']['xml']['raw'] = $matches; $info['svg']['xml']['raw'] = $matches;
} }
@ -99,6 +100,3 @@ class getid3_svg extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,11 +18,11 @@
class getid3_tiff extends getid3_handler class getid3_tiff extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$TIFFheader = fread($this->getid3->fp, 4); $TIFFheader = $this->fread(4);
switch (substr($TIFFheader, 0, 2)) { switch (substr($TIFFheader, 0, 2)) {
case 'II': case 'II':
@ -44,20 +45,20 @@ class getid3_tiff extends getid3_handler
$FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8); $FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8);
$nextIFDoffset = $this->TIFFendian2Int(fread($this->getid3->fp, 4), $info['tiff']['byte_order']); $nextIFDoffset = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
while ($nextIFDoffset > 0) { while ($nextIFDoffset > 0) {
$CurrentIFD['offset'] = $nextIFDoffset; $CurrentIFD['offset'] = $nextIFDoffset;
fseek($this->getid3->fp, $info['avdataoffset'] + $nextIFDoffset, SEEK_SET); $this->fseek($info['avdataoffset'] + $nextIFDoffset);
$CurrentIFD['fieldcount'] = $this->TIFFendian2Int(fread($this->getid3->fp, 2), $info['tiff']['byte_order']); $CurrentIFD['fieldcount'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) { for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) {
$CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int(fread($this->getid3->fp, 2), $info['tiff']['byte_order']); $CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
$CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int(fread($this->getid3->fp, 2), $info['tiff']['byte_order']); $CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
$CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int(fread($this->getid3->fp, 4), $info['tiff']['byte_order']); $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
$CurrentIFD['fields'][$i]['raw']['offset'] = fread($this->getid3->fp, 4); $CurrentIFD['fields'][$i]['raw']['offset'] = $this->fread(4);
switch ($CurrentIFD['fields'][$i]['raw']['type']) { switch ($CurrentIFD['fields'][$i]['raw']['type']) {
case 1: // BYTE An 8-bit unsigned integer. case 1: // BYTE An 8-bit unsigned integer.
@ -99,7 +100,7 @@ class getid3_tiff extends getid3_handler
$info['tiff']['ifd'][] = $CurrentIFD; $info['tiff']['ifd'][] = $CurrentIFD;
$CurrentIFD = array(); $CurrentIFD = array();
$nextIFDoffset = $this->TIFFendian2Int(fread($this->getid3->fp, 4), $info['tiff']['byte_order']); $nextIFDoffset = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
} }
@ -111,8 +112,8 @@ class getid3_tiff extends getid3_handler
case 258: // BitsPerSample case 258: // BitsPerSample
case 259: // Compression case 259: // Compression
if (!isset($fieldarray['value'])) { if (!isset($fieldarray['value'])) {
fseek($this->getid3->fp, $fieldarray['offset'], SEEK_SET); $this->fseek($fieldarray['offset']);
$info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($this->getid3->fp, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $this->fread($fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
} }
break; break;
@ -127,8 +128,8 @@ class getid3_tiff extends getid3_handler
if (isset($fieldarray['value'])) { if (isset($fieldarray['value'])) {
$info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value']; $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value'];
} else { } else {
fseek($this->getid3->fp, $fieldarray['offset'], SEEK_SET); $this->fseek($fieldarray['offset']);
$info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($this->getid3->fp, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $this->fread($fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
} }
break; break;
@ -182,7 +183,7 @@ class getid3_tiff extends getid3_handler
} }
function TIFFendian2Int($bytestring, $byteorder) { public function TIFFendian2Int($bytestring, $byteorder) {
if ($byteorder == 'Intel') { if ($byteorder == 'Intel') {
return getid3_lib::LittleEndian2Int($bytestring); return getid3_lib::LittleEndian2Int($bytestring);
} elseif ($byteorder == 'Motorola') { } elseif ($byteorder == 'Motorola') {
@ -191,7 +192,7 @@ class getid3_tiff extends getid3_handler
return false; return false;
} }
function TIFFcompressionMethod($id) { public function TIFFcompressionMethod($id) {
static $TIFFcompressionMethod = array(); static $TIFFcompressionMethod = array();
if (empty($TIFFcompressionMethod)) { if (empty($TIFFcompressionMethod)) {
$TIFFcompressionMethod = array( $TIFFcompressionMethod = array(
@ -205,7 +206,7 @@ class getid3_tiff extends getid3_handler
return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')'); return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')');
} }
function TIFFcommentName($id) { public function TIFFcommentName($id) {
static $TIFFcommentName = array(); static $TIFFcommentName = array();
if (empty($TIFFcommentName)) { if (empty($TIFFcommentName)) {
$TIFFcommentName = array( $TIFFcommentName = array(
@ -222,6 +223,3 @@ class getid3_tiff extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -14,7 +15,7 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// Module originally written [2009-Mar-25] by // // Module originally written [2009-Mar-25] by //
// Nigel Barnes <ngbarnesØhotmail*com> // // Nigel Barnes <ngbarnesØhotmail*com> //
// Minor reformatting and similar small changes to integrate // // Minor reformatting and similar small changes to integrate //
// into getID3 by James Heinrich <info@getid3.org> // // into getID3 by James Heinrich <info@getid3.org> //
// /// // ///
@ -32,9 +33,9 @@
*/ */
class getid3_cue extends getid3_handler class getid3_cue extends getid3_handler
{ {
var $cuesheet = array(); public $cuesheet = array();
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'cue'; $info['fileformat'] = 'cue';
@ -45,7 +46,7 @@ class getid3_cue extends getid3_handler
function readCueSheetFilename($filename) public function readCueSheetFilename($filename)
{ {
$filedata = file_get_contents($filename); $filedata = file_get_contents($filename);
return $this->readCueSheet($filedata); return $this->readCueSheet($filedata);
@ -55,7 +56,7 @@ class getid3_cue extends getid3_handler
* *
* @param string $filename - The filename for the cue sheet to open. * @param string $filename - The filename for the cue sheet to open.
*/ */
function readCueSheet(&$filedata) public function readCueSheet(&$filedata)
{ {
$cue_lines = array(); $cue_lines = array();
foreach (explode("\n", str_replace("\r", null, $filedata)) as $line) foreach (explode("\n", str_replace("\r", null, $filedata)) as $line)
@ -75,7 +76,7 @@ class getid3_cue extends getid3_handler
* *
* @param array $file - The cuesheet as an array of each line. * @param array $file - The cuesheet as an array of each line.
*/ */
function parseCueSheet($file) public function parseCueSheet($file)
{ {
//-1 means still global, all others are track specific //-1 means still global, all others are track specific
$track_on = -1; $track_on = -1;
@ -129,7 +130,7 @@ class getid3_cue extends getid3_handler
* @param string $line - The line in the cue file that contains the TRACK command. * @param string $line - The line in the cue file that contains the TRACK command.
* @param integer $track_on - The track currently processing. * @param integer $track_on - The track currently processing.
*/ */
function parseComment($line, $track_on) public function parseComment($line, $track_on)
{ {
$explodedline = explode(' ', $line, 3); $explodedline = explode(' ', $line, 3);
$comment_REM = (isset($explodedline[0]) ? $explodedline[0] : ''); $comment_REM = (isset($explodedline[0]) ? $explodedline[0] : '');
@ -152,7 +153,7 @@ class getid3_cue extends getid3_handler
* @param string $line - The line in the cue file that contains the FILE command. * @param string $line - The line in the cue file that contains the FILE command.
* @return array - Array of FILENAME and TYPE of file.. * @return array - Array of FILENAME and TYPE of file..
*/ */
function parseFile($line) public function parseFile($line)
{ {
$line = substr($line, strpos($line, ' ') + 1); $line = substr($line, strpos($line, ' ') + 1);
$type = strtolower(substr($line, strrpos($line, ' '))); $type = strtolower(substr($line, strrpos($line, ' ')));
@ -172,7 +173,7 @@ class getid3_cue extends getid3_handler
* @param string $line - The line in the cue file that contains the TRACK command. * @param string $line - The line in the cue file that contains the TRACK command.
* @param integer $track_on - The track currently processing. * @param integer $track_on - The track currently processing.
*/ */
function parseFlags($line, $track_on) public function parseFlags($line, $track_on)
{ {
if ($track_on != -1) if ($track_on != -1)
{ {
@ -210,7 +211,7 @@ class getid3_cue extends getid3_handler
* @param string $line - The line in the cue file that contains the TRACK command. * @param string $line - The line in the cue file that contains the TRACK command.
* @param integer $track_on - The track currently processing. * @param integer $track_on - The track currently processing.
*/ */
function parseGarbage($line, $track_on) public function parseGarbage($line, $track_on)
{ {
if ( strlen($line) > 0 ) if ( strlen($line) > 0 )
{ {
@ -231,7 +232,7 @@ class getid3_cue extends getid3_handler
* @param string $line - The line in the cue file that contains the TRACK command. * @param string $line - The line in the cue file that contains the TRACK command.
* @param integer $track_on - The track currently processing. * @param integer $track_on - The track currently processing.
*/ */
function parseIndex($line, $track_on) public function parseIndex($line, $track_on)
{ {
$type = strtolower(substr($line, 0, strpos($line, ' '))); $type = strtolower(substr($line, 0, strpos($line, ' ')));
$line = substr($line, strpos($line, ' ') + 1); $line = substr($line, strpos($line, ' ') + 1);
@ -260,7 +261,7 @@ class getid3_cue extends getid3_handler
} }
} }
function parseString($line, $track_on) public function parseString($line, $track_on)
{ {
$category = strtolower(substr($line, 0, strpos($line, ' '))); $category = strtolower(substr($line, 0, strpos($line, ' ')));
$line = substr($line, strpos($line, ' ') + 1); $line = substr($line, strpos($line, ' ') + 1);
@ -296,7 +297,7 @@ class getid3_cue extends getid3_handler
* @param string $line - The line in the cue file that contains the TRACK command. * @param string $line - The line in the cue file that contains the TRACK command.
* @param integer $track_on - The track currently processing. * @param integer $track_on - The track currently processing.
*/ */
function parseTrack($line, $track_on) public function parseTrack($line, $track_on)
{ {
$line = substr($line, strpos($line, ' ') + 1); $line = substr($line, strpos($line, ' ') + 1);
$track = ltrim(substr($line, 0, strpos($line, ' ')), '0'); $track = ltrim(substr($line, 0, strpos($line, ' ')), '0');
@ -309,4 +310,3 @@ class getid3_cue extends getid3_handler
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,11 +18,11 @@
class getid3_exe extends getid3_handler class getid3_exe extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$EXEheader = fread($this->getid3->fp, 28); $EXEheader = $this->fread(28);
$magic = 'MZ'; $magic = 'MZ';
if (substr($EXEheader, 0, 2) != $magic) { if (substr($EXEheader, 0, 2) != $magic) {
@ -56,6 +57,3 @@ return false;
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,14 +18,14 @@
class getid3_iso extends getid3_handler class getid3_iso extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'iso'; $info['fileformat'] = 'iso';
for ($i = 16; $i <= 19; $i++) { for ($i = 16; $i <= 19; $i++) {
fseek($this->getid3->fp, 2048 * $i, SEEK_SET); $this->fseek(2048 * $i);
$ISOheader = fread($this->getid3->fp, 2048); $ISOheader = $this->fread(2048);
if (substr($ISOheader, 1, 5) == 'CD001') { if (substr($ISOheader, 1, 5) == 'CD001') {
switch (ord($ISOheader{0})) { switch (ord($ISOheader{0})) {
case 1: case 1:
@ -55,7 +56,7 @@ class getid3_iso extends getid3_handler
} }
function ParsePrimaryVolumeDescriptor(&$ISOheader) { public function ParsePrimaryVolumeDescriptor(&$ISOheader) {
// ISO integer values are stored *BOTH* Little-Endian AND Big-Endian format!! // ISO integer values are stored *BOTH* Little-Endian AND Big-Endian format!!
// ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field // ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field
@ -129,7 +130,7 @@ class getid3_iso extends getid3_handler
} }
function ParseSupplementaryVolumeDescriptor(&$ISOheader) { public function ParseSupplementaryVolumeDescriptor(&$ISOheader) {
// ISO integer values are stored Both-Endian format!! // ISO integer values are stored Both-Endian format!!
// ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field // ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field
@ -208,7 +209,7 @@ class getid3_iso extends getid3_handler
} }
function ParsePathTable() { public function ParsePathTable() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
if (!isset($info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($info['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) { if (!isset($info['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($info['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) {
return false; return false;
@ -229,8 +230,8 @@ class getid3_iso extends getid3_handler
} }
$info['iso']['path_table']['offset'] = $PathTableLocation * 2048; $info['iso']['path_table']['offset'] = $PathTableLocation * 2048;
fseek($this->getid3->fp, $info['iso']['path_table']['offset'], SEEK_SET); $this->fseek($info['iso']['path_table']['offset']);
$info['iso']['path_table']['raw'] = fread($this->getid3->fp, $PathTableSize); $info['iso']['path_table']['raw'] = $this->fread($PathTableSize);
$offset = 0; $offset = 0;
$pathcounter = 1; $pathcounter = 1;
@ -267,7 +268,7 @@ class getid3_iso extends getid3_handler
} }
function ParseDirectoryRecord($directorydata) { public function ParseDirectoryRecord($directorydata) {
$info = &$this->getid3->info; $info = &$this->getid3->info;
if (isset($info['iso']['supplementary_volume_descriptor'])) { if (isset($info['iso']['supplementary_volume_descriptor'])) {
$TextEncoding = 'UTF-16BE'; // Big-Endian Unicode $TextEncoding = 'UTF-16BE'; // Big-Endian Unicode
@ -275,12 +276,12 @@ class getid3_iso extends getid3_handler
$TextEncoding = 'ISO-8859-1'; // Latin-1 $TextEncoding = 'ISO-8859-1'; // Latin-1
} }
fseek($this->getid3->fp, $directorydata['location_bytes'], SEEK_SET); $this->fseek($directorydata['location_bytes']);
$DirectoryRecordData = fread($this->getid3->fp, 1); $DirectoryRecordData = $this->fread(1);
while (ord($DirectoryRecordData{0}) > 33) { while (ord($DirectoryRecordData{0}) > 33) {
$DirectoryRecordData .= fread($this->getid3->fp, ord($DirectoryRecordData{0}) - 1); $DirectoryRecordData .= $this->fread(ord($DirectoryRecordData{0}) - 1);
$ThisDirectoryRecord['raw']['length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 0, 1)); $ThisDirectoryRecord['raw']['length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 0, 1));
$ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 1, 1)); $ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 1, 1));
@ -314,13 +315,13 @@ class getid3_iso extends getid3_handler
} }
$DirectoryRecord[] = $ThisDirectoryRecord; $DirectoryRecord[] = $ThisDirectoryRecord;
$DirectoryRecordData = fread($this->getid3->fp, 1); $DirectoryRecordData = $this->fread(1);
} }
return $DirectoryRecord; return $DirectoryRecord;
} }
function ISOstripFilenameVersion($ISOfilename) { public function ISOstripFilenameVersion($ISOfilename) {
// convert 'filename.ext;1' to 'filename.ext' // convert 'filename.ext;1' to 'filename.ext'
if (!strstr($ISOfilename, ';')) { if (!strstr($ISOfilename, ';')) {
return $ISOfilename; return $ISOfilename;
@ -329,7 +330,7 @@ class getid3_iso extends getid3_handler
} }
} }
function ISOtimeText2UNIXtime($ISOtime) { public function ISOtimeText2UNIXtime($ISOtime) {
$UNIXyear = (int) substr($ISOtime, 0, 4); $UNIXyear = (int) substr($ISOtime, 0, 4);
$UNIXmonth = (int) substr($ISOtime, 4, 2); $UNIXmonth = (int) substr($ISOtime, 4, 2);
@ -344,7 +345,7 @@ class getid3_iso extends getid3_handler
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
} }
function ISOtime2UNIXtime($ISOtime) { public function ISOtime2UNIXtime($ISOtime) {
// Represented by seven bytes: // Represented by seven bytes:
// 1: Number of years since 1900 // 1: Number of years since 1900
// 2: Month of the year from 1 to 12 // 2: Month of the year from 1 to 12
@ -365,7 +366,7 @@ class getid3_iso extends getid3_handler
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
} }
function TwosCompliment2Decimal($BinaryValue) { public function TwosCompliment2Decimal($BinaryValue) {
// http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html // http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
// First check if the number is negative or positive by looking at the sign bit. // First check if the number is negative or positive by looking at the sign bit.
// If it is positive, simply convert it to decimal. // If it is positive, simply convert it to decimal.
@ -385,5 +386,3 @@ class getid3_iso extends getid3_handler
} }
?>

View file

@ -3,11 +3,12 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// module.archive.doc.php // // module.misc.msoffice.php //
// module for analyzing MS Office (.doc, .xls, etc) files // // module for analyzing MS Office (.doc, .xls, etc) files //
// dependencies: NONE // // dependencies: NONE //
// /// // ///
@ -17,11 +18,11 @@
class getid3_msoffice extends getid3_handler class getid3_msoffice extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $this->fseek($info['avdataoffset']);
$DOCFILEheader = fread($this->getid3->fp, 8); $DOCFILEheader = $this->fread(8);
$magic = "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"; $magic = "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1";
if (substr($DOCFILEheader, 0, 8) != $magic) { if (substr($DOCFILEheader, 0, 8) != $magic) {
$info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($DOCFILEheader, 0, 8)).' instead.'; $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($DOCFILEheader, 0, 8)).' instead.';
@ -35,6 +36,3 @@ return false;
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,7 +18,7 @@
class getid3_par2 extends getid3_handler class getid3_par2 extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'par2'; $info['fileformat'] = 'par2';
@ -28,6 +29,3 @@ class getid3_par2 extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,7 +18,7 @@
class getid3_pdf extends getid3_handler class getid3_pdf extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
$info['fileformat'] = 'pdf'; $info['fileformat'] = 'pdf';
@ -28,6 +29,3 @@ class getid3_pdf extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -15,10 +16,10 @@
class getid3_apetag extends getid3_handler class getid3_apetag extends getid3_handler
{ {
var $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory public $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory
var $overrideendoffset = 0; public $overrideendoffset = 0;
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
if (!getid3_lib::intValueSupported($info['filesize'])) { if (!getid3_lib::intValueSupported($info['filesize'])) {
@ -32,8 +33,8 @@ class getid3_apetag extends getid3_handler
if ($this->overrideendoffset == 0) { if ($this->overrideendoffset == 0) {
fseek($this->getid3->fp, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); $this->fseek(0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
$APEfooterID3v1 = fread($this->getid3->fp, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize); $APEfooterID3v1 = $this->fread($id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
//if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) { //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') { if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
@ -51,8 +52,8 @@ class getid3_apetag extends getid3_handler
} else { } else {
fseek($this->getid3->fp, $this->overrideendoffset - $apetagheadersize, SEEK_SET); $this->fseek($this->overrideendoffset - $apetagheadersize);
if (fread($this->getid3->fp, 8) == 'APETAGEX') { if ($this->fread(8) == 'APETAGEX') {
$info['ape']['tag_offset_end'] = $this->overrideendoffset; $info['ape']['tag_offset_end'] = $this->overrideendoffset;
} }
@ -68,21 +69,21 @@ class getid3_apetag extends getid3_handler
// shortcut // shortcut
$thisfile_ape = &$info['ape']; $thisfile_ape = &$info['ape'];
fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET); $this->fseek($thisfile_ape['tag_offset_end'] - $apetagheadersize);
$APEfooterData = fread($this->getid3->fp, 32); $APEfooterData = $this->fread(32);
if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) { if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
$info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']; $info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
return false; return false;
} }
if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET); $this->fseek($thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize);
$thisfile_ape['tag_offset_start'] = ftell($this->getid3->fp); $thisfile_ape['tag_offset_start'] = $this->ftell();
$APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
} else { } else {
$thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize']; $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
fseek($this->getid3->fp, $thisfile_ape['tag_offset_start'], SEEK_SET); $this->fseek($thisfile_ape['tag_offset_start']);
$APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize']); $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize']);
} }
$info['avdataend'] = $thisfile_ape['tag_offset_start']; $info['avdataend'] = $thisfile_ape['tag_offset_start'];
@ -137,58 +138,88 @@ class getid3_apetag extends getid3_handler
$thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags); $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
switch ($thisfile_ape_items_current['flags']['item_contents_raw']) { switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
case 0: // UTF-8 case 0: // UTF-8
case 3: // Locator (URL, filename, etc), UTF-8 encoded case 2: // Locator (URL, filename, etc), UTF-8 encoded
$thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data'])); $thisfile_ape_items_current['data'] = explode("\x00", $thisfile_ape_items_current['data']);
break; break;
default: // binary data case 1: // binary data
default:
break; break;
} }
switch (strtolower($item_key)) { switch (strtolower($item_key)) {
// http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
case 'replaygain_track_gain': case 'replaygain_track_gain':
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['track']['originator'] = 'unspecified'; $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
} else {
$info['warning'][] = 'MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break; break;
case 'replaygain_track_peak': case 'replaygain_track_peak':
$thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['track']['originator'] = 'unspecified'; $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
if ($thisfile_replaygain['track']['peak'] <= 0) { $thisfile_replaygain['track']['originator'] = 'unspecified';
$info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; if ($thisfile_replaygain['track']['peak'] <= 0) {
$info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
}
} else {
$info['warning'][] = 'MP3gainTrackPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
} }
break; break;
case 'replaygain_album_gain': case 'replaygain_album_gain':
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['album']['originator'] = 'unspecified'; $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
} else {
$info['warning'][] = 'MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break; break;
case 'replaygain_album_peak': case 'replaygain_album_peak':
$thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['album']['originator'] = 'unspecified'; $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
if ($thisfile_replaygain['album']['peak'] <= 0) { $thisfile_replaygain['album']['originator'] = 'unspecified';
$info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; if ($thisfile_replaygain['album']['peak'] <= 0) {
$info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
}
} else {
$info['warning'][] = 'MP3gainAlbumPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
} }
break; break;
case 'mp3gain_undo': case 'mp3gain_undo':
list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]); if (preg_match('#^[\\-\\+][0-9]{3},[\\-\\+][0-9]{3},[NW]$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false); $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
} else {
$info['warning'][] = 'MP3gainUndo value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break; break;
case 'mp3gain_minmax': case 'mp3gain_minmax':
list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]); if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
} else {
$info['warning'][] = 'MP3gainMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break; break;
case 'mp3gain_album_minmax': case 'mp3gain_album_minmax':
list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]); if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
} else {
$info['warning'][] = 'MP3gainAlbumMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break; break;
case 'tracknumber': case 'tracknumber':
@ -221,6 +252,10 @@ class getid3_apetag extends getid3_handler
case 'cover art (recording)': case 'cover art (recording)':
case 'cover art (studio)': case 'cover art (studio)':
// list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
if (is_array($thisfile_ape_items_current['data'])) {
$info['warning'][] = 'APEtag "'.$item_key.'" should be flagged as Binary data, but was incorrectly flagged as UTF-8';
$thisfile_ape_items_current['data'] = implode("\x00", $thisfile_ape_items_current['data']);
}
list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2); list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2);
$thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00"); $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00");
$thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']); $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
@ -268,7 +303,14 @@ class getid3_apetag extends getid3_handler
if (!isset($info['ape']['comments']['picture'])) { if (!isset($info['ape']['comments']['picture'])) {
$info['ape']['comments']['picture'] = array(); $info['ape']['comments']['picture'] = array();
} }
$info['ape']['comments']['picture'][] = array('data'=>$thisfile_ape_items_current['data'], 'image_mime'=>$thisfile_ape_items_current['image_mime']); $comments_picture_data = array();
foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
if (isset($thisfile_ape_items_current[$picture_key])) {
$comments_picture_data[$picture_key] = $thisfile_ape_items_current[$picture_key];
}
}
$info['ape']['comments']['picture'][] = $comments_picture_data;
unset($comments_picture_data);
} }
} while (false); } while (false);
break; break;
@ -289,7 +331,7 @@ class getid3_apetag extends getid3_handler
return true; return true;
} }
function parseAPEheaderFooter($APEheaderFooterData) { public function parseAPEheaderFooter($APEheaderFooterData) {
// http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
// shortcut // shortcut
@ -313,10 +355,10 @@ class getid3_apetag extends getid3_handler
return $headerfooterinfo; return $headerfooterinfo;
} }
function parseAPEtagFlags($rawflagint) { public function parseAPEtagFlags($rawflagint) {
// "Note: APE Tags 1.0 do not use any of the APE Tag flags. // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
// All are set to zero on creation and ignored on reading." // All are set to zero on creation and ignored on reading."
// http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html // http://wiki.hydrogenaud.io/index.php?title=Ape_Tags_Flags
$flags['header'] = (bool) ($rawflagint & 0x80000000); $flags['header'] = (bool) ($rawflagint & 0x80000000);
$flags['footer'] = (bool) ($rawflagint & 0x40000000); $flags['footer'] = (bool) ($rawflagint & 0x40000000);
$flags['this_is_header'] = (bool) ($rawflagint & 0x20000000); $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
@ -328,7 +370,7 @@ class getid3_apetag extends getid3_handler
return $flags; return $flags;
} }
function APEcontentTypeFlagLookup($contenttypeid) { public function APEcontentTypeFlagLookup($contenttypeid) {
static $APEcontentTypeFlagLookup = array( static $APEcontentTypeFlagLookup = array(
0 => 'utf-8', 0 => 'utf-8',
1 => 'binary', 1 => 'binary',
@ -338,7 +380,7 @@ class getid3_apetag extends getid3_handler
return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid'); return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
} }
function APEtagItemIsUTF8Lookup($itemkey) { public function APEtagItemIsUTF8Lookup($itemkey) {
static $APEtagItemIsUTF8Lookup = array( static $APEtagItemIsUTF8Lookup = array(
'title', 'title',
'subtitle', 'subtitle',
@ -368,5 +410,3 @@ class getid3_apetag extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,7 +18,7 @@
class getid3_id3v1 extends getid3_handler class getid3_id3v1 extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
if (!getid3_lib::intValueSupported($info['filesize'])) { if (!getid3_lib::intValueSupported($info['filesize'])) {
@ -25,9 +26,9 @@ class getid3_id3v1 extends getid3_handler
return false; return false;
} }
fseek($this->getid3->fp, -256, SEEK_END); $this->fseek(-256, SEEK_END);
$preid3v1 = fread($this->getid3->fp, 128); $preid3v1 = $this->fread(128);
$id3v1tag = fread($this->getid3->fp, 128); $id3v1tag = $this->fread(128);
if (substr($id3v1tag, 0, 3) == 'TAG') { if (substr($id3v1tag, 0, 3) == 'TAG') {
@ -102,11 +103,11 @@ class getid3_id3v1 extends getid3_handler
return true; return true;
} }
static function cutfield($str) { public static function cutfield($str) {
return trim(substr($str, 0, strcspn($str, "\x00"))); return trim(substr($str, 0, strcspn($str, "\x00")));
} }
static function ArrayOfGenres($allowSCMPXextended=false) { public static function ArrayOfGenres($allowSCMPXextended=false) {
static $GenreLookup = array( static $GenreLookup = array(
0 => 'Blues', 0 => 'Blues',
1 => 'Classic Rock', 1 => 'Classic Rock',
@ -290,7 +291,7 @@ class getid3_id3v1 extends getid3_handler
return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup); return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
} }
static function LookupGenreName($genreid, $allowSCMPXextended=true) { public static function LookupGenreName($genreid, $allowSCMPXextended=true) {
switch ($genreid) { switch ($genreid) {
case 'RX': case 'RX':
case 'CR': case 'CR':
@ -302,12 +303,12 @@ class getid3_id3v1 extends getid3_handler
$genreid = intval($genreid); // to handle 3 or '3' or '03' $genreid = intval($genreid); // to handle 3 or '3' or '03'
break; break;
} }
$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended); $GenreLookup = self::ArrayOfGenres($allowSCMPXextended);
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false); return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
} }
static function LookupGenreID($genre, $allowSCMPXextended=false) { public static function LookupGenreID($genre, $allowSCMPXextended=false) {
$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended); $GenreLookup = self::ArrayOfGenres($allowSCMPXextended);
$LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre)); $LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
foreach ($GenreLookup as $key => $value) { foreach ($GenreLookup as $key => $value) {
if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) { if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
@ -317,14 +318,14 @@ class getid3_id3v1 extends getid3_handler
return false; return false;
} }
static function StandardiseID3v1GenreName($OriginalGenre) { public static function StandardiseID3v1GenreName($OriginalGenre) {
if (($GenreID = getid3_id3v1::LookupGenreID($OriginalGenre)) !== false) { if (($GenreID = self::LookupGenreID($OriginalGenre)) !== false) {
return getid3_id3v1::LookupGenreName($GenreID); return self::LookupGenreName($GenreID);
} }
return $OriginalGenre; return $OriginalGenre;
} }
static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') { public static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
$ID3v1Tag = 'TAG'; $ID3v1Tag = 'TAG';
$ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT); $ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
$ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT); $ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
@ -357,6 +358,3 @@ class getid3_id3v1 extends getid3_handler
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,10 +18,9 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE_
class getid3_id3v2 extends getid3_handler class getid3_id3v2 extends getid3_handler
{ {
var $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory public $StartingOffset = 0;
var $StartingOffset = 0;
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// Overall tag structure: // Overall tag structure:
@ -52,8 +52,8 @@ class getid3_id3v2 extends getid3_handler
$thisfile_id3v2_flags = &$thisfile_id3v2['flags']; $thisfile_id3v2_flags = &$thisfile_id3v2['flags'];
fseek($this->getid3->fp, $this->StartingOffset, SEEK_SET); $this->fseek($this->StartingOffset);
$header = fread($this->getid3->fp, 10); $header = $this->fread(10);
if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
$thisfile_id3v2['majorversion'] = ord($header{3}); $thisfile_id3v2['majorversion'] = ord($header{3});
@ -132,7 +132,7 @@ class getid3_id3v2 extends getid3_handler
} }
if ($sizeofframes > 0) { if ($sizeofframes > 0) {
$framedata = fread($this->getid3->fp, $sizeofframes); // read all frames from file into $framedata variable $framedata = $this->fread($sizeofframes); // read all frames from file into $framedata variable
// if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) { if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) {
@ -424,7 +424,7 @@ class getid3_id3v2 extends getid3_handler
// ID3v2 size 4 * %0xxxxxxx // ID3v2 size 4 * %0xxxxxxx
if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
$footer = fread($this->getid3->fp, 10); $footer = $this->fread(10);
if (substr($footer, 0, 3) == '3DI') { if (substr($footer, 0, 3) == '3DI') {
$thisfile_id3v2['footer'] = true; $thisfile_id3v2['footer'] = true;
$thisfile_id3v2['majorversion_footer'] = ord($footer{3}); $thisfile_id3v2['majorversion_footer'] = ord($footer{3});
@ -495,7 +495,7 @@ class getid3_id3v2 extends getid3_handler
} }
function ParseID3v2GenreString($genrestring) { public function ParseID3v2GenreString($genrestring) {
// Parse genres into arrays of genreName and genreID // Parse genres into arrays of genreName and genreID
// ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)' // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
// ID3v2.4.x: '21' $00 'Eurodisco' $00 // ID3v2.4.x: '21' $00 'Eurodisco' $00
@ -518,7 +518,7 @@ class getid3_id3v2 extends getid3_handler
} }
function ParseID3v2Frame(&$parsedFrame) { public function ParseID3v2Frame(&$parsedFrame) {
// shortcuts // shortcuts
$info = &$this->getid3->info; $info = &$this->getid3->info;
@ -625,12 +625,13 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
@ -640,10 +641,15 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
$parsedFrame['description'] = $frame_description; $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description));
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
} else {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
}
} }
//unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
@ -667,10 +673,38 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
$string = rtrim($string, "\x00"); // remove possible terminating null (put by encoding id or software bug) // This of course breaks when an artist name contains slash character, e.g. "AC/DC"
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string; // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense
unset($string); // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user
switch ($parsedFrame['encoding']) {
case 'UTF-16':
case 'UTF-16BE':
case 'UTF-16LE':
$wordsize = 2;
break;
case 'ISO-8859-1':
case 'UTF-8':
default:
$wordsize = 1;
break;
}
$Txxx_elements = array();
$Txxx_elements_start_offset = 0;
for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) {
if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("\x00", $wordsize)) {
$Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
$Txxx_elements_start_offset = $i + $wordsize;
}
}
$Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
foreach ($Txxx_elements as $Txxx_element) {
$string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element);
if (!empty($string)) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
}
}
unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset);
} }
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame
@ -684,11 +718,13 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
@ -696,10 +732,10 @@ class getid3_id3v2 extends getid3_handler
if (ord($frame_description) === 0) { if (ord($frame_description) === 0) {
$frame_description = ''; $frame_description = '';
} }
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
if ($frame_terminatorpos) { if ($frame_terminatorpos) {
@ -738,6 +774,7 @@ class getid3_id3v2 extends getid3_handler
} elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only) } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only)
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only) (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only)
// http://id3.org/id3v2.3.0#sec4.4
// There may only be one 'IPL' frame in each tag // There may only be one 'IPL' frame in each tag
// <Header for 'User defined URL link frame', ID: 'IPL'> // <Header for 'User defined URL link frame', ID: 'IPL'>
// Text encoding $xx // Text encoding $xx
@ -750,10 +787,68 @@ class getid3_id3v2 extends getid3_handler
} }
$parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
$parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset);
// http://www.getid3.org/phpBB3/viewtopic.php?t=1369
// "this tag typically contains null terminated strings, which are associated in pairs"
// "there are users that use the tag incorrectly"
$IPLS_parts = array();
if (strpos($parsedFrame['data_raw'], "\x00") !== false) {
$IPLS_parts_unsorted = array();
if (((strlen($parsedFrame['data_raw']) % 2) == 0) && ((substr($parsedFrame['data_raw'], 0, 2) == "\xFF\xFE") || (substr($parsedFrame['data_raw'], 0, 2) == "\xFE\xFF"))) {
// UTF-16, be careful looking for null bytes since most 2-byte characters may contain one; you need to find twin null bytes, and on even padding
$thisILPS = '';
for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) {
$twobytes = substr($parsedFrame['data_raw'], $i, 2);
if ($twobytes === "\x00\x00") {
$IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
$thisILPS = '';
} else {
$thisILPS .= $twobytes;
}
}
if (strlen($thisILPS) > 2) { // 2-byte BOM
$IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
}
} else {
// ISO-8859-1 or UTF-8 or other single-byte-null character set
$IPLS_parts_unsorted = explode("\x00", $parsedFrame['data_raw']);
}
if (count($IPLS_parts_unsorted) == 1) {
// just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson"
foreach ($IPLS_parts_unsorted as $key => $value) {
$IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value);
$position = '';
foreach ($IPLS_parts_sorted as $person) {
$IPLS_parts[] = array('position'=>$position, 'person'=>$person);
}
}
} elseif ((count($IPLS_parts_unsorted) % 2) == 0) {
$position = '';
$person = '';
foreach ($IPLS_parts_unsorted as $key => $value) {
if (($key % 2) == 0) {
$position = $value;
} else {
$person = $value;
$IPLS_parts[] = array('position'=>$position, 'person'=>$person);
$position = '';
$person = '';
}
}
} else {
foreach ($IPLS_parts_unsorted as $key => $value) {
$IPLS_parts[] = array($value);
}
}
} else {
$IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']);
}
$parsedFrame['data'] = $IPLS_parts;
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
} }
@ -864,20 +959,22 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
$frame_offset += 3; $frame_offset += 3;
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_description) === 0) { if (ord($frame_description) === 0) {
$frame_description = ''; $frame_description = '';
} }
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
@ -910,8 +1007,10 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
$frame_offset += 3; $frame_offset += 3;
@ -928,16 +1027,16 @@ class getid3_id3v2 extends getid3_handler
$frame_remainingdata = substr($parsedFrame['data'], $frame_offset); $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
while (strlen($frame_remainingdata)) { while (strlen($frame_remainingdata)) {
$frame_offset = 0; $frame_offset = 0;
$frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_terminatorpos = strpos($frame_remainingdata, $frame_textencoding_terminator);
if ($frame_terminatorpos === false) { if ($frame_terminatorpos === false) {
$frame_remainingdata = ''; $frame_remainingdata = '';
} else { } else {
if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
$frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator));
if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) { if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
// timestamp probably omitted for first data item // timestamp probably omitted for first data item
} else { } else {
@ -968,20 +1067,22 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
$frame_offset += 3; $frame_offset += 3;
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_description) === 0) { if (ord($frame_description) === 0) {
$frame_description = ''; $frame_description = '';
} }
$frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
@ -991,7 +1092,12 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['description'] = $frame_description; $parsedFrame['description'] = $frame_description;
$parsedFrame['data'] = $frame_text; $parsedFrame['data'] = $frame_text;
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
} else {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
}
} }
} }
@ -1233,15 +1339,17 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) { if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
$frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3); $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
if (strtolower($frame_imagetype) == 'ima') { if (strtolower($frame_imagetype) == 'ima') {
// complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
// MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net) // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net)
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_mimetype) === 0) { if (ord($frame_mimetype) === 0) {
@ -1270,8 +1378,8 @@ class getid3_id3v2 extends getid3_handler
if ($frame_offset >= $parsedFrame['datalength']) { if ($frame_offset >= $parsedFrame['datalength']) {
$info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset); $info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset);
} else { } else {
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
@ -1289,7 +1397,7 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['picturetypeid'] = $frame_picturetype; $parsedFrame['picturetypeid'] = $frame_picturetype;
$parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
$parsedFrame['description'] = $frame_description; $parsedFrame['description'] = $frame_description;
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$parsedFrame['datalength'] = strlen($parsedFrame['data']); $parsedFrame['datalength'] = strlen($parsedFrame['data']);
$parsedFrame['image_mime'] = ''; $parsedFrame['image_mime'] = '';
@ -1306,32 +1414,34 @@ class getid3_id3v2 extends getid3_handler
} }
do { do {
if ($this->inline_attachments === false) { if ($this->getid3->option_save_attachments === false) {
// skip entirely // skip entirely
unset($parsedFrame['data']); unset($parsedFrame['data']);
break; break;
} }
if ($this->inline_attachments === true) { if ($this->getid3->option_save_attachments === true) {
// great // great
} elseif (is_int($this->inline_attachments)) { /*
if ($this->inline_attachments < $parsedFrame['data_length']) { } elseif (is_int($this->getid3->option_save_attachments)) {
if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) {
// too big, skip // too big, skip
$info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)'; $info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)';
unset($parsedFrame['data']); unset($parsedFrame['data']);
break; break;
} }
} elseif (is_string($this->inline_attachments)) { */
$this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR); } elseif (is_string($this->getid3->option_save_attachments)) {
if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) { $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
if (!is_dir($dir) || !is_writable($dir)) {
// cannot write, skip // cannot write, skip
$info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$this->inline_attachments.'" (not writable)'; $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$dir.'" (not writable)';
unset($parsedFrame['data']); unset($parsedFrame['data']);
break; break;
} }
} }
// if we get this far, must be OK // if we get this far, must be OK
if (is_string($this->inline_attachments)) { if (is_string($this->getid3->option_save_attachments)) {
$destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset; $destination_filename = $dir.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset;
if (!file_exists($destination_filename) || is_writable($destination_filename)) { if (!file_exists($destination_filename) || is_writable($destination_filename)) {
file_put_contents($destination_filename, $parsedFrame['data']); file_put_contents($destination_filename, $parsedFrame['data']);
} else { } else {
@ -1344,7 +1454,14 @@ class getid3_id3v2 extends getid3_handler
if (!isset($info['id3v2']['comments']['picture'])) { if (!isset($info['id3v2']['comments']['picture'])) {
$info['id3v2']['comments']['picture'] = array(); $info['id3v2']['comments']['picture'] = array();
} }
$info['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']); $comments_picture_data = array();
foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
if (isset($parsedFrame[$picture_key])) {
$comments_picture_data[$picture_key] = $parsedFrame[$picture_key];
}
}
$info['id3v2']['comments']['picture'][] = $comments_picture_data;
unset($comments_picture_data);
} }
} }
} while (false); } while (false);
@ -1363,8 +1480,10 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
@ -1373,25 +1492,25 @@ class getid3_id3v2 extends getid3_handler
} }
$frame_offset = $frame_terminatorpos + strlen("\x00"); $frame_offset = $frame_terminatorpos + strlen("\x00");
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_filename) === 0) { if (ord($frame_filename) === 0) {
$frame_filename = ''; $frame_filename = '';
} }
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_description) === 0) { if (ord($frame_description) === 0) {
$frame_description = ''; $frame_description = '';
} }
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
$parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encodingid'] = $frame_textencoding;
@ -1508,7 +1627,7 @@ class getid3_id3v2 extends getid3_handler
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information
// There may be more than one 'LINK' frame in a tag, // There may be more than one 'LINK' frame in a tag,
// but only one with the same contents // but only one with the same contents
// <Header for 'Linked information', ID: 'LINK'> // <Header for 'Linked information', ID: 'LINK'>
@ -1536,7 +1655,7 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']); $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback_iso88591_utf8($parsedFrame['url']);
} }
unset($parsedFrame['data']); unset($parsedFrame['data']);
@ -1630,8 +1749,10 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
@ -1653,25 +1774,25 @@ class getid3_id3v2 extends getid3_handler
$frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_sellername) === 0) { if (ord($frame_sellername) === 0) {
$frame_sellername = ''; $frame_sellername = '';
} }
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_description) === 0) { if (ord($frame_description) === 0) {
$frame_description = ''; $frame_description = '';
} }
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
@ -1797,7 +1918,7 @@ class getid3_id3v2 extends getid3_handler
$frame_offset += 2; $frame_offset += 2;
$parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8); $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
for ($i = 0; $i < $frame_indexpoints; $i++) { for ($i = 0; $i < $parsedFrame['indexpoints']; $i++) {
$parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint)); $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
$frame_offset += $frame_bytesperpoint; $frame_offset += $frame_bytesperpoint;
} }
@ -1845,17 +1966,197 @@ class getid3_id3v2 extends getid3_handler
unset($parsedFrame['data']); unset($parsedFrame['data']);
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CHAP')) { // CHAP Chapters frame (ID3v2.3+ only)
// http://id3.org/id3v2-chapters-1.0
// <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP"> (10 bytes)
// Element ID <text string> $00
// Start time $xx xx xx xx
// End time $xx xx xx xx
// Start offset $xx xx xx xx
// End offset $xx xx xx xx
// <Optional embedded sub-frames>
$frame_offset = 0;
@list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
$frame_offset += strlen($parsedFrame['element_id']."\x00");
$parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
$frame_offset += 4;
$parsedFrame['time_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
$frame_offset += 4;
if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") {
// "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
$parsedFrame['offset_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
}
$frame_offset += 4;
if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") {
// "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
$parsedFrame['offset_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
}
$frame_offset += 4;
if ($frame_offset < strlen($parsedFrame['data'])) {
$parsedFrame['subframes'] = array();
while ($frame_offset < strlen($parsedFrame['data'])) {
// <Optional embedded sub-frames>
$subframe = array();
$subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
$frame_offset += 4;
$subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
$frame_offset += 4;
$subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
$frame_offset += 2;
if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
$info['warning'][] = 'CHAP subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)';
break;
}
$subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
$frame_offset += $subframe['size'];
$subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
$subframe['text'] = substr($subframe_rawdata, 1);
$subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
$encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
switch (substr($encoding_converted_text, 0, 2)) {
case "\xFF\xFE":
case "\xFE\xFF":
switch (strtoupper($info['id3v2']['encoding'])) {
case 'ISO-8859-1':
case 'UTF-8':
$encoding_converted_text = substr($encoding_converted_text, 2);
// remove unwanted byte-order-marks
break;
default:
// ignore
break;
}
break;
default:
// do not remove BOM
break;
}
if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
if ($subframe['name'] == 'TIT2') {
$parsedFrame['chapter_name'] = $encoding_converted_text;
} elseif ($subframe['name'] == 'TIT3') {
$parsedFrame['chapter_description'] = $encoding_converted_text;
}
$parsedFrame['subframes'][] = $subframe;
} else {
$info['warning'][] = 'ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)';
}
}
unset($subframe_rawdata, $subframe, $encoding_converted_text);
}
$id3v2_chapter_entry = array();
foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {
if (isset($parsedFrame[$id3v2_chapter_key])) {
$id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
}
}
if (!isset($info['id3v2']['chapters'])) {
$info['id3v2']['chapters'] = array();
}
$info['id3v2']['chapters'][] = $id3v2_chapter_entry;
unset($id3v2_chapter_entry, $id3v2_chapter_key);
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only)
// http://id3.org/id3v2-chapters-1.0
// <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes)
// Element ID <text string> $00
// CTOC flags %xx
// Entry count $xx
// Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */
// <Optional embedded sub-frames>
$frame_offset = 0;
@list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
$frame_offset += strlen($parsedFrame['element_id']."\x00");
$ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1));
$frame_offset += 1;
$parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1));
$frame_offset += 1;
$terminator_position = null;
for ($i = 0; $i < $parsedFrame['entry_count']; $i++) {
$terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset);
$parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset);
$frame_offset = $terminator_position + 1;
}
$parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x01);
$parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03);
unset($ctoc_flags_raw, $terminator_position);
if ($frame_offset < strlen($parsedFrame['data'])) {
$parsedFrame['subframes'] = array();
while ($frame_offset < strlen($parsedFrame['data'])) {
// <Optional embedded sub-frames>
$subframe = array();
$subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
$frame_offset += 4;
$subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
$frame_offset += 4;
$subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
$frame_offset += 2;
if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
$info['warning'][] = 'CTOS subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)';
break;
}
$subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
$frame_offset += $subframe['size'];
$subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
$subframe['text'] = substr($subframe_rawdata, 1);
$subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
$encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
switch (substr($encoding_converted_text, 0, 2)) {
case "\xFF\xFE":
case "\xFE\xFF":
switch (strtoupper($info['id3v2']['encoding'])) {
case 'ISO-8859-1':
case 'UTF-8':
$encoding_converted_text = substr($encoding_converted_text, 2);
// remove unwanted byte-order-marks
break;
default:
// ignore
break;
}
break;
default:
// do not remove BOM
break;
}
if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
if ($subframe['name'] == 'TIT2') {
$parsedFrame['toc_name'] = $encoding_converted_text;
} elseif ($subframe['name'] == 'TIT3') {
$parsedFrame['toc_description'] = $encoding_converted_text;
}
$parsedFrame['subframes'][] = $subframe;
} else {
$info['warning'][] = 'ID3v2.CTOC subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)';
}
}
unset($subframe_rawdata, $subframe, $encoding_converted_text);
}
} }
return true; return true;
} }
function DeUnsynchronise($data) { public function DeUnsynchronise($data) {
return str_replace("\xFF\x00", "\xFF", $data); return str_replace("\xFF\x00", "\xFF", $data);
} }
function LookupExtendedHeaderRestrictionsTagSizeLimits($index) { public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) {
static $LookupExtendedHeaderRestrictionsTagSizeLimits = array( static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
0x00 => 'No more than 128 frames and 1 MB total tag size', 0x00 => 'No more than 128 frames and 1 MB total tag size',
0x01 => 'No more than 64 frames and 128 KB total tag size', 0x01 => 'No more than 64 frames and 128 KB total tag size',
@ -1865,7 +2166,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : ''); return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : '');
} }
function LookupExtendedHeaderRestrictionsTextEncodings($index) { public function LookupExtendedHeaderRestrictionsTextEncodings($index) {
static $LookupExtendedHeaderRestrictionsTextEncodings = array( static $LookupExtendedHeaderRestrictionsTextEncodings = array(
0x00 => 'No restrictions', 0x00 => 'No restrictions',
0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8', 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8',
@ -1873,7 +2174,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : ''); return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : '');
} }
function LookupExtendedHeaderRestrictionsTextFieldSize($index) { public function LookupExtendedHeaderRestrictionsTextFieldSize($index) {
static $LookupExtendedHeaderRestrictionsTextFieldSize = array( static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
0x00 => 'No restrictions', 0x00 => 'No restrictions',
0x01 => 'No string is longer than 1024 characters', 0x01 => 'No string is longer than 1024 characters',
@ -1883,7 +2184,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : ''); return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : '');
} }
function LookupExtendedHeaderRestrictionsImageEncoding($index) { public function LookupExtendedHeaderRestrictionsImageEncoding($index) {
static $LookupExtendedHeaderRestrictionsImageEncoding = array( static $LookupExtendedHeaderRestrictionsImageEncoding = array(
0x00 => 'No restrictions', 0x00 => 'No restrictions',
0x01 => 'Images are encoded only with PNG or JPEG', 0x01 => 'Images are encoded only with PNG or JPEG',
@ -1891,7 +2192,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : ''); return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : '');
} }
function LookupExtendedHeaderRestrictionsImageSizeSize($index) { public function LookupExtendedHeaderRestrictionsImageSizeSize($index) {
static $LookupExtendedHeaderRestrictionsImageSizeSize = array( static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
0x00 => 'No restrictions', 0x00 => 'No restrictions',
0x01 => 'All images are 256x256 pixels or smaller', 0x01 => 'All images are 256x256 pixels or smaller',
@ -1901,7 +2202,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : ''); return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : '');
} }
function LookupCurrencyUnits($currencyid) { public function LookupCurrencyUnits($currencyid) {
$begin = __LINE__; $begin = __LINE__;
@ -2098,7 +2399,7 @@ class getid3_id3v2 extends getid3_handler
} }
function LookupCurrencyCountry($currencyid) { public function LookupCurrencyCountry($currencyid) {
$begin = __LINE__; $begin = __LINE__;
@ -2250,7 +2551,7 @@ class getid3_id3v2 extends getid3_handler
SOS Somalia SOS Somalia
SPL Seborga SPL Seborga
SRG Suriname SRG Suriname
STD São Tome and Principe STD São Tome and Principe
SVC El Salvador SVC El Salvador
SYP Syria SYP Syria
SZL Swaziland SZL Swaziland
@ -2274,13 +2575,13 @@ class getid3_id3v2 extends getid3_handler
VND Viet Nam VND Viet Nam
VUV Vanuatu VUV Vanuatu
WST Samoa WST Samoa
XAF Communauté Financière Africaine XAF Communauté Financière Africaine
XAG Silver XAG Silver
XAU Gold XAU Gold
XCD East Caribbean XCD East Caribbean
XDR International Monetary Fund XDR International Monetary Fund
XPD Palladium XPD Palladium
XPF Comptoirs Français du Pacifique XPF Comptoirs Français du Pacifique
XPT Platinum XPT Platinum
YER Yemen YER Yemen
YUM Yugoslavia YUM Yugoslavia
@ -2295,7 +2596,7 @@ class getid3_id3v2 extends getid3_handler
static function LanguageLookup($languagecode, $casesensitive=false) { public static function LanguageLookup($languagecode, $casesensitive=false) {
if (!$casesensitive) { if (!$casesensitive) {
$languagecode = strtolower($languagecode); $languagecode = strtolower($languagecode);
@ -2724,7 +3025,7 @@ class getid3_id3v2 extends getid3_handler
vai Vai vai Vai
ven Venda ven Venda
vie Vietnamese vie Vietnamese
vol Volapük vol Volapük
vot Votic vot Votic
wak Wakashan Languages wak Wakashan Languages
wal Walamo wal Walamo
@ -2751,7 +3052,7 @@ class getid3_id3v2 extends getid3_handler
} }
static function ETCOEventLookup($index) { public static function ETCOEventLookup($index) {
if (($index >= 0x17) && ($index <= 0xDF)) { if (($index >= 0x17) && ($index <= 0xDF)) {
return 'reserved for future use'; return 'reserved for future use';
} }
@ -2794,7 +3095,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($EventLookup[$index]) ? $EventLookup[$index] : ''); return (isset($EventLookup[$index]) ? $EventLookup[$index] : '');
} }
static function SYTLContentTypeLookup($index) { public static function SYTLContentTypeLookup($index) {
static $SYTLContentTypeLookup = array( static $SYTLContentTypeLookup = array(
0x00 => 'other', 0x00 => 'other',
0x01 => 'lyrics', 0x01 => 'lyrics',
@ -2810,7 +3111,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : ''); return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : '');
} }
static function APICPictureTypeLookup($index, $returnarray=false) { public static function APICPictureTypeLookup($index, $returnarray=false) {
static $APICPictureTypeLookup = array( static $APICPictureTypeLookup = array(
0x00 => 'Other', 0x00 => 'Other',
0x01 => '32x32 pixels \'file icon\' (PNG only)', 0x01 => '32x32 pixels \'file icon\' (PNG only)',
@ -2840,7 +3141,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : ''); return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : '');
} }
static function COMRReceivedAsLookup($index) { public static function COMRReceivedAsLookup($index) {
static $COMRReceivedAsLookup = array( static $COMRReceivedAsLookup = array(
0x00 => 'Other', 0x00 => 'Other',
0x01 => 'Standard CD album with other songs', 0x01 => 'Standard CD album with other songs',
@ -2856,7 +3157,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : ''); return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : '');
} }
static function RVA2ChannelTypeLookup($index) { public static function RVA2ChannelTypeLookup($index) {
static $RVA2ChannelTypeLookup = array( static $RVA2ChannelTypeLookup = array(
0x00 => 'Other', 0x00 => 'Other',
0x01 => 'Master volume', 0x01 => 'Master volume',
@ -2872,7 +3173,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : ''); return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : '');
} }
static function FrameNameLongLookup($framename) { public static function FrameNameLongLookup($framename) {
$begin = __LINE__; $begin = __LINE__;
@ -3056,7 +3357,7 @@ class getid3_id3v2 extends getid3_handler
} }
static function FrameNameShortLookup($framename) { public static function FrameNameShortLookup($framename) {
$begin = __LINE__; $begin = __LINE__;
@ -3197,7 +3498,7 @@ class getid3_id3v2 extends getid3_handler
TSSE encoder_settings TSSE encoder_settings
TSST set_subtitle TSST set_subtitle
TST title_sort_order TST title_sort_order
TT1 description TT1 content_group_description
TT2 title TT2 title
TT3 subtitle TT3 subtitle
TXT lyricist TXT lyricist
@ -3235,7 +3536,7 @@ class getid3_id3v2 extends getid3_handler
return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short'); return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
} }
static function TextEncodingTerminatorLookup($encoding) { public static function TextEncodingTerminatorLookup($encoding) {
// http://www.id3.org/id3v2.4.0-structure.txt // http://www.id3.org/id3v2.4.0-structure.txt
// Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
static $TextEncodingTerminatorLookup = array( static $TextEncodingTerminatorLookup = array(
@ -3245,10 +3546,10 @@ class getid3_id3v2 extends getid3_handler
3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00. 3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00.
255 => "\x00\x00" 255 => "\x00\x00"
); );
return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : ''); return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : "\x00");
} }
static function TextEncodingNameLookup($encoding) { public static function TextEncodingNameLookup($encoding) {
// http://www.id3.org/id3v2.4.0-structure.txt // http://www.id3.org/id3v2.4.0-structure.txt
// Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
static $TextEncodingNameLookup = array( static $TextEncodingNameLookup = array(
@ -3261,7 +3562,7 @@ class getid3_id3v2 extends getid3_handler
return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1'); return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
} }
static function IsValidID3v2FrameName($framename, $id3v2majorversion) { public static function IsValidID3v2FrameName($framename, $id3v2majorversion) {
switch ($id3v2majorversion) { switch ($id3v2majorversion) {
case 2: case 2:
return preg_match('#[A-Z][A-Z0-9]{2}#', $framename); return preg_match('#[A-Z][A-Z0-9]{2}#', $framename);
@ -3275,7 +3576,7 @@ class getid3_id3v2 extends getid3_handler
return false; return false;
} }
static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
for ($i = 0; $i < strlen($numberstring); $i++) { for ($i = 0; $i < strlen($numberstring); $i++) {
if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) { if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
if (($numberstring{$i} == '.') && $allowdecimal) { if (($numberstring{$i} == '.') && $allowdecimal) {
@ -3290,7 +3591,7 @@ class getid3_id3v2 extends getid3_handler
return true; return true;
} }
static function IsValidDateStampString($datestamp) { public static function IsValidDateStampString($datestamp) {
if (strlen($datestamp) != 8) { if (strlen($datestamp) != 8) {
return false; return false;
} }
@ -3318,10 +3619,9 @@ class getid3_id3v2 extends getid3_handler
return true; return true;
} }
static function ID3v2HeaderLength($majorversion) { public static function ID3v2HeaderLength($majorversion) {
return (($majorversion == 2) ? 6 : 10); return (($majorversion == 2) ? 6 : 10);
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,7 +18,7 @@
class getid3_lyrics3 extends getid3_handler class getid3_lyrics3 extends getid3_handler
{ {
function Analyze() { public function Analyze() {
$info = &$this->getid3->info; $info = &$this->getid3->info;
// http://www.volweb.cz/str/tags.htm // http://www.volweb.cz/str/tags.htm
@ -27,8 +28,8 @@ class getid3_lyrics3 extends getid3_handler
return false; return false;
} }
fseek($this->getid3->fp, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size] $this->fseek((0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size]
$lyrics3_id3v1 = fread($this->getid3->fp, 128 + 9 + 6); $lyrics3_id3v1 = $this->fread(128 + 9 + 6);
$lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size $lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size
$lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200 $lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200
$id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1 $id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1
@ -68,9 +69,9 @@ class getid3_lyrics3 extends getid3_handler
if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) { if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) {
fseek($this->getid3->fp, $info['ape']['tag_offset_start'] - 15, SEEK_SET); $this->fseek($info['ape']['tag_offset_start'] - 15);
$lyrics3lsz = fread($this->getid3->fp, 6); $lyrics3lsz = $this->fread(6);
$lyrics3end = fread($this->getid3->fp, 9); $lyrics3end = $this->fread(9);
if ($lyrics3end == 'LYRICSEND') { if ($lyrics3end == 'LYRICSEND') {
// Lyrics3v1, APE, maybe ID3v1 // Lyrics3v1, APE, maybe ID3v1
@ -100,8 +101,9 @@ class getid3_lyrics3 extends getid3_handler
$this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size); $this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size);
if (!isset($info['ape'])) { if (!isset($info['ape'])) {
$GETID3_ERRORARRAY = &$info['warning']; if (isset($info['lyrics3']['tag_offset_start'])) {
if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, false)) { $GETID3_ERRORARRAY = &$info['warning'];
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
$getid3_temp = new getID3(); $getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename); $getid3_temp->openfile($this->getid3->filename);
$getid3_apetag = new getid3_apetag($getid3_temp); $getid3_apetag = new getid3_apetag($getid3_temp);
@ -114,6 +116,8 @@ class getid3_lyrics3 extends getid3_handler
$info['replay_gain'] = $getid3_temp->info['replay_gain']; $info['replay_gain'] = $getid3_temp->info['replay_gain'];
} }
unset($getid3_temp, $getid3_apetag); unset($getid3_temp, $getid3_apetag);
} else {
$info['warning'][] = 'Lyrics3 and APE tags appear to have become entangled (most likely due to updating the APE tags with a non-Lyrics3-aware tagger)';
} }
} }
@ -122,7 +126,7 @@ class getid3_lyrics3 extends getid3_handler
return true; return true;
} }
function getLyrics3Data($endoffset, $version, $length) { public function getLyrics3Data($endoffset, $version, $length) {
// http://www.volweb.cz/str/tags.htm // http://www.volweb.cz/str/tags.htm
$info = &$this->getid3->info; $info = &$this->getid3->info;
@ -132,11 +136,11 @@ class getid3_lyrics3 extends getid3_handler
return false; return false;
} }
fseek($this->getid3->fp, $endoffset, SEEK_SET); $this->fseek($endoffset);
if ($length <= 0) { if ($length <= 0) {
return false; return false;
} }
$rawdata = fread($this->getid3->fp, $length); $rawdata = $this->fread($length);
$ParsedLyrics3['raw']['lyrics3version'] = $version; $ParsedLyrics3['raw']['lyrics3version'] = $version;
$ParsedLyrics3['raw']['lyrics3tagsize'] = $length; $ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
@ -169,7 +173,7 @@ class getid3_lyrics3 extends getid3_handler
$ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9)); $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9));
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3); $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
} else { } else {
$info['error'][] = '"LYRICSEND" expected at '.(ftell($this->getid3->fp) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; $info['error'][] = '"LYRICSEND" expected at '.($this->ftell() - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
return false; return false;
} }
break; break;
@ -217,7 +221,7 @@ class getid3_lyrics3 extends getid3_handler
$this->Lyrics3LyricsTimestampParse($ParsedLyrics3); $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
} }
} else { } else {
$info['error'][] = '"LYRICS200" expected at '.(ftell($this->getid3->fp) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; $info['error'][] = '"LYRICS200" expected at '.($this->ftell() - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
return false; return false;
} }
break; break;
@ -246,14 +250,14 @@ class getid3_lyrics3 extends getid3_handler
return true; return true;
} }
function Lyrics3Timestamp2Seconds($rawtimestamp) { public function Lyrics3Timestamp2Seconds($rawtimestamp) {
if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) { if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) {
return (int) (($regs[1] * 60) + $regs[2]); return (int) (($regs[1] * 60) + $regs[2]);
} }
return false; return false;
} }
function Lyrics3LyricsTimestampParse(&$Lyrics3data) { public function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
$lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']); $lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
foreach ($lyricsarray as $key => $lyricline) { foreach ($lyricsarray as $key => $lyricline) {
$regs = array(); $regs = array();
@ -283,7 +287,7 @@ class getid3_lyrics3 extends getid3_handler
return true; return true;
} }
function IntString2Bool($char) { public function IntString2Bool($char) {
if ($char == '1') { if ($char == '1') {
return true; return true;
} elseif ($char == '0') { } elseif ($char == '0') {
@ -292,6 +296,3 @@ class getid3_lyrics3 extends getid3_handler
return null; return null;
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -14,7 +15,7 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// // // //
// Module originally written [2009-Mar-26] by // // Module originally written [2009-Mar-26] by //
// Nigel Barnes <ngbarnesØhotmail*com> // // Nigel Barnes <ngbarnesØhotmail*com> //
// Bundled into getID3 with permission // // Bundled into getID3 with permission //
// called by getID3 in module.graphic.jpg.php // // called by getID3 in module.graphic.jpg.php //
// /// // ///
@ -37,21 +38,21 @@ class Image_XMP
* The name of the image file that contains the XMP fields to extract and modify. * The name of the image file that contains the XMP fields to extract and modify.
* @see Image_XMP() * @see Image_XMP()
*/ */
var $_sFilename = null; public $_sFilename = null;
/** /**
* @var array * @var array
* The XMP fields that were extracted from the image or updated by this class. * The XMP fields that were extracted from the image or updated by this class.
* @see getAllTags() * @see getAllTags()
*/ */
var $_aXMP = array(); public $_aXMP = array();
/** /**
* @var boolean * @var boolean
* True if an APP1 segment was found to contain XMP metadata. * True if an APP1 segment was found to contain XMP metadata.
* @see isValid() * @see isValid()
*/ */
var $_bXMPParse = false; public $_bXMPParse = false;
/** /**
* Returns the status of XMP parsing during instantiation * Returns the status of XMP parsing during instantiation
@ -61,7 +62,7 @@ class Image_XMP
* @return boolean * @return boolean
* Returns true if an APP1 segment was found to contain XMP metadata. * Returns true if an APP1 segment was found to contain XMP metadata.
*/ */
function isValid() public function isValid()
{ {
return $this->_bXMPParse; return $this->_bXMPParse;
} }
@ -71,7 +72,7 @@ class Image_XMP
* *
* @return array - An array of XMP fields as it extracted by the XMPparse() function * @return array - An array of XMP fields as it extracted by the XMPparse() function
*/ */
function getAllTags() public function getAllTags()
{ {
return $this->_aXMP; return $this->_aXMP;
} }
@ -83,7 +84,7 @@ class Image_XMP
* @return array $headerdata - Array of JPEG header segments * @return array $headerdata - Array of JPEG header segments
* @return boolean FALSE - if headers could not be read * @return boolean FALSE - if headers could not be read
*/ */
function _get_jpeg_header_data($filename) public function _get_jpeg_header_data($filename)
{ {
// prevent refresh from aborting file operations and hosing file // prevent refresh from aborting file operations and hosing file
ignore_user_abort(true); ignore_user_abort(true);
@ -193,7 +194,7 @@ class Image_XMP
* @return string $xmp_data - the string of raw XML text * @return string $xmp_data - the string of raw XML text
* @return boolean FALSE - if an APP 1 XMP segment could not be found, or if an error occured * @return boolean FALSE - if an APP 1 XMP segment could not be found, or if an error occured
*/ */
function _get_XMP_text($filename) public function _get_XMP_text($filename)
{ {
//Get JPEG header data //Get JPEG header data
$jpeg_header_data = $this->_get_jpeg_header_data($filename); $jpeg_header_data = $this->_get_jpeg_header_data($filename);
@ -226,7 +227,7 @@ class Image_XMP
* @return array $xmp_array - an array containing all xmp details retrieved. * @return array $xmp_array - an array containing all xmp details retrieved.
* @return boolean FALSE - couldn't parse the XMP data * @return boolean FALSE - couldn't parse the XMP data
*/ */
function read_XMP_array_from_text($xmltext) public function read_XMP_array_from_text($xmltext)
{ {
// Check if there actually is any text to parse // Check if there actually is any text to parse
if (trim($xmltext) == '') if (trim($xmltext) == '')
@ -302,7 +303,8 @@ class Image_XMP
foreach (array_keys($xml_elem['attributes']) as $key) foreach (array_keys($xml_elem['attributes']) as $key)
{ {
// Check whether we want this details from this attribute // Check whether we want this details from this attribute
if (in_array($key, $GLOBALS['XMP_tag_captions'])) // if (in_array($key, $GLOBALS['XMP_tag_captions']))
if (true)
{ {
// Attribute wanted // Attribute wanted
$xmp_array[$key] = $xml_elem['attributes'][$key]; $xmp_array[$key] = $xml_elem['attributes'][$key];
@ -359,7 +361,8 @@ class Image_XMP
default: default:
// Check whether we want the details from this attribute // Check whether we want the details from this attribute
if (in_array($xml_elem['tag'], $GLOBALS['XMP_tag_captions'])) // if (in_array($xml_elem['tag'], $GLOBALS['XMP_tag_captions']))
if (true)
{ {
switch ($xml_elem['type']) switch ($xml_elem['type'])
{ {
@ -375,7 +378,7 @@ class Image_XMP
case 'complete': case 'complete':
// store attribute value // store attribute value
$xmp_array[$xml_elem['tag']] = (isset($xml_elem['value']) ? $xml_elem['value'] : ''); $xmp_array[$xml_elem['tag']] = (isset($xml_elem['attributes']) ? $xml_elem['attributes'] : (isset($xml_elem['value']) ? $xml_elem['value'] : ''));
break; break;
case 'cdata': case 'cdata':
@ -396,7 +399,7 @@ class Image_XMP
* *
* @param string - Name of the image file to access and extract XMP information from. * @param string - Name of the image file to access and extract XMP information from.
*/ */
function Image_XMP($sFilename) public function Image_XMP($sFilename)
{ {
$this->_sFilename = $sFilename; $this->_sFilename = $sFilename;
@ -420,6 +423,7 @@ class Image_XMP
* The Property names of all known XMP fields. * The Property names of all known XMP fields.
* Note: this is a full list with unrequired properties commented out. * Note: this is a full list with unrequired properties commented out.
*/ */
/*
$GLOBALS['XMP_tag_captions'] = array( $GLOBALS['XMP_tag_captions'] = array(
// IPTC Core // IPTC Core
'Iptc4xmpCore:CiAdrCity', 'Iptc4xmpCore:CiAdrCity',
@ -688,7 +692,7 @@ $GLOBALS['XMP_tag_captions'] = array(
'exif:Rows', 'exif:Rows',
'exif:Settings', 'exif:Settings',
); );
*/
/** /**
* Global Variable: JPEG_Segment_Names * Global Variable: JPEG_Segment_Names
@ -762,5 +766,3 @@ $GLOBALS['JPEG_Segment_Names'] = array(
0xFD => 'JPG13', 0xFD => 'JPG13',
0xFE => 'COM', 0xFE => 'COM',
); );
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -19,17 +20,17 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE
class getid3_write_apetag class getid3_write_apetag
{ {
var $filename; public $filename;
var $tag_data; public $tag_data;
var $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data public $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
var $warnings = array(); // any non-critical errors will be stored here public $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here public $errors = array(); // any critical errors will be stored here
function getid3_write_apetag() { public function getid3_write_apetag() {
return true; return true;
} }
function WriteAPEtag() { public function WriteAPEtag() {
// NOTE: All data passed to this function must be UTF-8 format // NOTE: All data passed to this function must be UTF-8 format
$getID3 = new getID3; $getID3 = new getID3;
@ -67,15 +68,15 @@ class getid3_write_apetag
if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) { if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']); $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
} }
fseek($fp, $PostAPEdataOffset, SEEK_SET); fseek($fp, $PostAPEdataOffset);
$PostAPEdata = ''; $PostAPEdata = '';
if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) { if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
$PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset); $PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
} }
fseek($fp, $PostAPEdataOffset, SEEK_SET); fseek($fp, $PostAPEdataOffset);
if (isset($ThisFileInfo['ape']['tag_offset_start'])) { if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET); fseek($fp, $ThisFileInfo['ape']['tag_offset_start']);
} }
ftruncate($fp, ftell($fp)); ftruncate($fp, ftell($fp));
fwrite($fp, $APEtag, strlen($APEtag)); fwrite($fp, $APEtag, strlen($APEtag));
@ -91,7 +92,7 @@ class getid3_write_apetag
return false; return false;
} }
function DeleteAPEtag() { public function DeleteAPEtag() {
$getID3 = new getID3; $getID3 = new getID3;
$ThisFileInfo = $getID3->analyze($this->filename); $ThisFileInfo = $getID3->analyze($this->filename);
if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) { if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
@ -100,14 +101,14 @@ class getid3_write_apetag
flock($fp, LOCK_EX); flock($fp, LOCK_EX);
$oldignoreuserabort = ignore_user_abort(true); $oldignoreuserabort = ignore_user_abort(true);
fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET); fseek($fp, $ThisFileInfo['ape']['tag_offset_end']);
$DataAfterAPE = ''; $DataAfterAPE = '';
if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) { if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
$DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']); $DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
} }
ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']); ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET); fseek($fp, $ThisFileInfo['ape']['tag_offset_start']);
if (!empty($DataAfterAPE)) { if (!empty($DataAfterAPE)) {
fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE)); fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
@ -125,7 +126,7 @@ class getid3_write_apetag
} }
function GenerateAPEtag() { public function GenerateAPEtag() {
// NOTE: All data passed to this function must be UTF-8 format // NOTE: All data passed to this function must be UTF-8 format
$items = array(); $items = array();
@ -159,7 +160,7 @@ class getid3_write_apetag
return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false); return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
} }
function GenerateAPEtagHeaderFooter(&$items, $isheader=false) { public function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
$tagdatalength = 0; $tagdatalength = 0;
foreach ($items as $itemdata) { foreach ($items as $itemdata) {
$tagdatalength += strlen($itemdata); $tagdatalength += strlen($itemdata);
@ -175,7 +176,7 @@ class getid3_write_apetag
return $APEheader; return $APEheader;
} }
function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) { public function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
$APEtagFlags = array_fill(0, 4, 0); $APEtagFlags = array_fill(0, 4, 0);
if ($header) { if ($header) {
$APEtagFlags[0] |= 0x80; // Tag contains a header $APEtagFlags[0] |= 0x80; // Tag contains a header
@ -188,8 +189,8 @@ class getid3_write_apetag
} }
// 0: Item contains text information coded in UTF-8 // 0: Item contains text information coded in UTF-8
// 1: Item contains binary information °) // 1: Item contains binary information °)
// 2: Item is a locator of external stored information °°) // 2: Item is a locator of external stored information °°)
// 3: reserved // 3: reserved
$APEtagFlags[3] |= ($encodingid << 1); $APEtagFlags[3] |= ($encodingid << 1);
@ -200,7 +201,7 @@ class getid3_write_apetag
return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]); return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
} }
function CleanAPEtagItemKey($itemkey) { public function CleanAPEtagItemKey($itemkey) {
$itemkey = preg_replace("#[^\x20-\x7E]#i", '', $itemkey); $itemkey = preg_replace("#[^\x20-\x7E]#i", '', $itemkey);
// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
@ -221,5 +222,3 @@ class getid3_write_apetag
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,17 +18,17 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE_
class getid3_write_id3v1 class getid3_write_id3v1
{ {
var $filename; public $filename;
var $filesize; public $filesize;
var $tag_data; public $tag_data;
var $warnings = array(); // any non-critical errors will be stored here public $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here public $errors = array(); // any critical errors will be stored here
function getid3_write_id3v1() { public function getid3_write_id3v1() {
return true; return true;
} }
function WriteID3v1() { public function WriteID3v1() {
// File MUST be writeable - CHMOD(646) at least // File MUST be writeable - CHMOD(646) at least
if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) { if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) {
$this->setRealFileSize(); $this->setRealFileSize();
@ -65,7 +66,7 @@ class getid3_write_id3v1
return false; return false;
} }
function FixID3v1Padding() { public function FixID3v1Padding() {
// ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces // ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces
// This function rewrites the ID3v1 tag with correct padding // This function rewrites the ID3v1 tag with correct padding
@ -87,7 +88,7 @@ class getid3_write_id3v1
return false; return false;
} }
function RemoveID3v1() { public function RemoveID3v1() {
// File MUST be writeable - CHMOD(646) at least // File MUST be writeable - CHMOD(646) at least
if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) { if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) {
$this->setRealFileSize(); $this->setRealFileSize();
@ -115,7 +116,7 @@ class getid3_write_id3v1
return false; return false;
} }
function setRealFileSize() { public function setRealFileSize() {
if (PHP_INT_MAX > 2147483647) { if (PHP_INT_MAX > 2147483647) {
$this->filesize = filesize($this->filename); $this->filesize = filesize($this->filename);
return true; return true;
@ -134,5 +135,3 @@ class getid3_write_id3v1
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,23 +18,23 @@ getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE_
class getid3_write_id3v2 class getid3_write_id3v2
{ {
var $filename; public $filename;
var $tag_data; public $tag_data;
var $fread_buffer_size = 32768; // read buffer size in bytes public $fread_buffer_size = 32768; // read buffer size in bytes
var $paddedlength = 4096; // minimum length of ID3v2 tag in bytes public $paddedlength = 4096; // minimum length of ID3v2 tag in bytes
var $majorversion = 3; // ID3v2 major version (2, 3 (recommended), 4) public $majorversion = 3; // ID3v2 major version (2, 3 (recommended), 4)
var $minorversion = 0; // ID3v2 minor version - always 0 public $minorversion = 0; // ID3v2 minor version - always 0
var $merge_existing_data = false; // if true, merge new data with existing tags; if false, delete old tag data and only write new tags public $merge_existing_data = false; // if true, merge new data with existing tags; if false, delete old tag data and only write new tags
var $id3v2_default_encodingid = 0; // default text encoding (ISO-8859-1) if not explicitly passed public $id3v2_default_encodingid = 0; // default text encoding (ISO-8859-1) if not explicitly passed
var $id3v2_use_unsynchronisation = false; // the specs say it should be TRUE, but most other ID3v2-aware programs are broken if unsynchronization is used, so by default don't use it. public $id3v2_use_unsynchronisation = false; // the specs say it should be TRUE, but most other ID3v2-aware programs are broken if unsynchronization is used, so by default don't use it.
var $warnings = array(); // any non-critical errors will be stored here public $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here public $errors = array(); // any critical errors will be stored here
function getid3_write_id3v2() { public function getid3_write_id3v2() {
return true; return true;
} }
function WriteID3v2() { public function WriteID3v2() {
// File MUST be writeable - CHMOD(646) at least. It's best if the // File MUST be writeable - CHMOD(646) at least. It's best if the
// directory is also writeable, because that method is both faster and less susceptible to errors. // directory is also writeable, because that method is both faster and less susceptible to errors.
@ -91,7 +92,7 @@ class getid3_write_id3v2
rewind($fp_source); rewind($fp_source);
if (!empty($OldThisFileInfo['avdataoffset'])) { if (!empty($OldThisFileInfo['avdataoffset'])) {
fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); fseek($fp_source, $OldThisFileInfo['avdataoffset']);
} }
while ($buffer = fread($fp_source, $this->fread_buffer_size)) { while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
@ -133,7 +134,7 @@ class getid3_write_id3v2
return false; return false;
} }
function RemoveID3v2() { public function RemoveID3v2() {
// File MUST be writeable - CHMOD(646) at least. It's best if the // File MUST be writeable - CHMOD(646) at least. It's best if the
// directory is also writeable, because that method is both faster and less susceptible to errors. // directory is also writeable, because that method is both faster and less susceptible to errors.
if (is_writeable(dirname($this->filename))) { if (is_writeable(dirname($this->filename))) {
@ -152,7 +153,7 @@ class getid3_write_id3v2
} }
rewind($fp_source); rewind($fp_source);
if ($OldThisFileInfo['avdataoffset'] !== false) { if ($OldThisFileInfo['avdataoffset'] !== false) {
fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); fseek($fp_source, $OldThisFileInfo['avdataoffset']);
} }
if (is_writable($this->filename) && is_file($this->filename) && ($fp_temp = fopen($this->filename.'getid3tmp', 'w+b'))) { if (is_writable($this->filename) && is_file($this->filename) && ($fp_temp = fopen($this->filename.'getid3tmp', 'w+b'))) {
while ($buffer = fread($fp_source, $this->fread_buffer_size)) { while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
@ -187,7 +188,7 @@ class getid3_write_id3v2
} }
rewind($fp_source); rewind($fp_source);
if ($OldThisFileInfo['avdataoffset'] !== false) { if ($OldThisFileInfo['avdataoffset'] !== false) {
fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); fseek($fp_source, $OldThisFileInfo['avdataoffset']);
} }
if ($fp_temp = tmpfile()) { if ($fp_temp = tmpfile()) {
while ($buffer = fread($fp_source, $this->fread_buffer_size)) { while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
@ -225,7 +226,7 @@ class getid3_write_id3v2
} }
function GenerateID3v2TagFlags($flags) { public function GenerateID3v2TagFlags($flags) {
switch ($this->majorversion) { switch ($this->majorversion) {
case 4: case 4:
// %abcd0000 // %abcd0000
@ -259,7 +260,7 @@ class getid3_write_id3v2
} }
function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) { public function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) {
switch ($this->majorversion) { switch ($this->majorversion) {
case 4: case 4:
// %0abc0000 %0h00kmnp // %0abc0000 %0h00kmnp
@ -299,7 +300,7 @@ class getid3_write_id3v2
return chr(bindec($flag1)).chr(bindec($flag2)); return chr(bindec($flag1)).chr(bindec($flag2));
} }
function GenerateID3v2FrameData($frame_name, $source_data_array) { public function GenerateID3v2FrameData($frame_name, $source_data_array) {
if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) { if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
return false; return false;
} }
@ -1173,7 +1174,7 @@ class getid3_write_id3v2
return $framedata; return $framedata;
} }
function ID3v2FrameIsAllowed($frame_name, $source_data_array) { public function ID3v2FrameIsAllowed($frame_name, $source_data_array) {
static $PreviousFrames = array(); static $PreviousFrames = array();
if ($frame_name === null) { if ($frame_name === null) {
@ -1530,7 +1531,7 @@ class getid3_write_id3v2
return true; return true;
} }
function GenerateID3v2Tag($noerrorsonly=true) { public function GenerateID3v2Tag($noerrorsonly=true) {
$this->ID3v2FrameIsAllowed(null, ''); // clear static array in case this isn't the first call to $this->GenerateID3v2Tag() $this->ID3v2FrameIsAllowed(null, ''); // clear static array in case this isn't the first call to $this->GenerateID3v2Tag()
$tagstring = ''; $tagstring = '';
@ -1542,6 +1543,9 @@ class getid3_write_id3v2
unset($frame_flags); unset($frame_flags);
$frame_data = false; $frame_data = false;
if ($this->ID3v2FrameIsAllowed($frame_name, $source_data_array)) { if ($this->ID3v2FrameIsAllowed($frame_name, $source_data_array)) {
if(array_key_exists('description', $source_data_array) && array_key_exists('encodingid', $source_data_array) && array_key_exists('encoding', $this->tag_data)) {
$source_data_array['description'] = getid3_lib::iconv_fallback($this->tag_data['encoding'], $source_data_array['encoding'], $source_data_array['description']);
}
if ($frame_data = $this->GenerateID3v2FrameData($frame_name, $source_data_array)) { if ($frame_data = $this->GenerateID3v2FrameData($frame_name, $source_data_array)) {
$FrameUnsynchronisation = false; $FrameUnsynchronisation = false;
if ($this->majorversion >= 4) { if ($this->majorversion >= 4) {
@ -1641,7 +1645,7 @@ class getid3_write_id3v2
return false; return false;
} }
function ID3v2IsValidPriceString($pricestring) { public function ID3v2IsValidPriceString($pricestring) {
if (getid3_id3v2::LanguageLookup(substr($pricestring, 0, 3), true) == '') { if (getid3_id3v2::LanguageLookup(substr($pricestring, 0, 3), true) == '') {
return false; return false;
} elseif (!$this->IsANumber(substr($pricestring, 3), true)) { } elseif (!$this->IsANumber(substr($pricestring, 3), true)) {
@ -1650,7 +1654,7 @@ class getid3_write_id3v2
return true; return true;
} }
function ID3v2FrameFlagsLookupTagAlter($framename) { public function ID3v2FrameFlagsLookupTagAlter($framename) {
// unfinished // unfinished
switch ($framename) { switch ($framename) {
case 'RGAD': case 'RGAD':
@ -1662,7 +1666,7 @@ class getid3_write_id3v2
return $allow; return $allow;
} }
function ID3v2FrameFlagsLookupFileAlter($framename) { public function ID3v2FrameFlagsLookupFileAlter($framename) {
// unfinished // unfinished
switch ($framename) { switch ($framename) {
case 'RGAD': case 'RGAD':
@ -1675,7 +1679,7 @@ class getid3_write_id3v2
} }
} }
function ID3v2IsValidETCOevent($eventid) { public function ID3v2IsValidETCOevent($eventid) {
if (($eventid < 0) || ($eventid > 0xFF)) { if (($eventid < 0) || ($eventid > 0xFF)) {
// outside range of 1 byte // outside range of 1 byte
return false; return false;
@ -1695,7 +1699,7 @@ class getid3_write_id3v2
return true; return true;
} }
function ID3v2IsValidSYLTtype($contenttype) { public function ID3v2IsValidSYLTtype($contenttype) {
if (($contenttype >= 0) && ($contenttype <= 8) && ($this->majorversion == 4)) { if (($contenttype >= 0) && ($contenttype <= 8) && ($this->majorversion == 4)) {
return true; return true;
} elseif (($contenttype >= 0) && ($contenttype <= 6) && ($this->majorversion == 3)) { } elseif (($contenttype >= 0) && ($contenttype <= 6) && ($this->majorversion == 3)) {
@ -1704,21 +1708,21 @@ class getid3_write_id3v2
return false; return false;
} }
function ID3v2IsValidRVA2channeltype($channeltype) { public function ID3v2IsValidRVA2channeltype($channeltype) {
if (($channeltype >= 0) && ($channeltype <= 8) && ($this->majorversion == 4)) { if (($channeltype >= 0) && ($channeltype <= 8) && ($this->majorversion == 4)) {
return true; return true;
} }
return false; return false;
} }
function ID3v2IsValidAPICpicturetype($picturetype) { public function ID3v2IsValidAPICpicturetype($picturetype) {
if (($picturetype >= 0) && ($picturetype <= 0x14) && ($this->majorversion >= 2) && ($this->majorversion <= 4)) { if (($picturetype >= 0) && ($picturetype <= 0x14) && ($this->majorversion >= 2) && ($this->majorversion <= 4)) {
return true; return true;
} }
return false; return false;
} }
function ID3v2IsValidAPICimageformat($imageformat) { public function ID3v2IsValidAPICimageformat($imageformat) {
if ($imageformat == '-->') { if ($imageformat == '-->') {
return true; return true;
} elseif ($this->majorversion == 2) { } elseif ($this->majorversion == 2) {
@ -1733,28 +1737,28 @@ class getid3_write_id3v2
return false; return false;
} }
function ID3v2IsValidCOMRreceivedAs($receivedas) { public function ID3v2IsValidCOMRreceivedAs($receivedas) {
if (($this->majorversion >= 3) && ($receivedas >= 0) && ($receivedas <= 8)) { if (($this->majorversion >= 3) && ($receivedas >= 0) && ($receivedas <= 8)) {
return true; return true;
} }
return false; return false;
} }
function ID3v2IsValidRGADname($RGADname) { public function ID3v2IsValidRGADname($RGADname) {
if (($RGADname >= 0) && ($RGADname <= 2)) { if (($RGADname >= 0) && ($RGADname <= 2)) {
return true; return true;
} }
return false; return false;
} }
function ID3v2IsValidRGADoriginator($RGADoriginator) { public function ID3v2IsValidRGADoriginator($RGADoriginator) {
if (($RGADoriginator >= 0) && ($RGADoriginator <= 3)) { if (($RGADoriginator >= 0) && ($RGADoriginator <= 3)) {
return true; return true;
} }
return false; return false;
} }
function ID3v2IsValidTextEncoding($textencodingbyte) { public function ID3v2IsValidTextEncoding($textencodingbyte) {
static $ID3v2IsValidTextEncoding_cache = array( static $ID3v2IsValidTextEncoding_cache = array(
2 => array(true, true), 2 => array(true, true),
3 => array(true, true), 3 => array(true, true),
@ -1762,7 +1766,7 @@ class getid3_write_id3v2
return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]); return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]);
} }
function Unsynchronise($data) { public function Unsynchronise($data) {
// Whenever a false synchronisation is found within the tag, one zeroed // Whenever a false synchronisation is found within the tag, one zeroed
// byte is inserted after the first false synchronisation byte. The // byte is inserted after the first false synchronisation byte. The
// format of a correct sync that should be altered by ID3 encoders is as // format of a correct sync that should be altered by ID3 encoders is as
@ -1792,8 +1796,8 @@ class getid3_write_id3v2
return $unsyncheddata; return $unsyncheddata;
} }
function is_hash($var) { public function is_hash($var) {
// written by dev-nullØchristophe*vg // written by dev-nullØchristophe*vg
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
if (is_array($var)) { if (is_array($var)) {
$keys = array_keys($var); $keys = array_keys($var);
@ -1807,8 +1811,8 @@ class getid3_write_id3v2
return false; return false;
} }
function array_join_merge($arr1, $arr2) { public function array_join_merge($arr1, $arr2) {
// written by dev-nullØchristophe*vg // written by dev-nullØchristophe*vg
// taken from http://www.php.net/manual/en/function.array-merge-recursive.php // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
if (is_array($arr1) && is_array($arr2)) { if (is_array($arr1) && is_array($arr2)) {
// the same -> merge // the same -> merge
@ -1831,14 +1835,14 @@ class getid3_write_id3v2
} }
} }
function IsValidMIMEstring($mimestring) { public function IsValidMIMEstring($mimestring) {
if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) { if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) {
return true; return true;
} }
return false; return false;
} }
function IsWithinBitRange($number, $maxbits, $signed=false) { public function IsWithinBitRange($number, $maxbits, $signed=false) {
if ($signed) { if ($signed) {
if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) { if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) {
return true; return true;
@ -1851,7 +1855,7 @@ class getid3_write_id3v2
return false; return false;
} }
function safe_parse_url($url) { public function safe_parse_url($url) {
$parts = @parse_url($url); $parts = @parse_url($url);
$parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : ''); $parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : '');
$parts['host'] = (isset($parts['host']) ? $parts['host'] : ''); $parts['host'] = (isset($parts['host']) ? $parts['host'] : '');
@ -1862,7 +1866,7 @@ class getid3_write_id3v2
return $parts; return $parts;
} }
function IsValidURL($url, $allowUserPass=false) { public function IsValidURL($url, $allowUserPass=false) {
if ($url == '') { if ($url == '') {
return false; return false;
} }
@ -1893,7 +1897,7 @@ class getid3_write_id3v2
return false; return false;
} }
static function ID3v2ShortFrameNameLookup($majorversion, $long_description) { public static function ID3v2ShortFrameNameLookup($majorversion, $long_description) {
$long_description = str_replace(' ', '_', strtolower(trim($long_description))); $long_description = str_replace(' ', '_', strtolower(trim($long_description)));
static $ID3v2ShortFrameNameLookup = array(); static $ID3v2ShortFrameNameLookup = array();
if (empty($ID3v2ShortFrameNameLookup)) { if (empty($ID3v2ShortFrameNameLookup)) {
@ -1920,6 +1924,7 @@ class getid3_write_id3v2
$ID3v2ShortFrameNameLookup[2]['publisher'] = 'TPB'; $ID3v2ShortFrameNameLookup[2]['publisher'] = 'TPB';
$ID3v2ShortFrameNameLookup[2]['isrc'] = 'TRC'; $ID3v2ShortFrameNameLookup[2]['isrc'] = 'TRC';
$ID3v2ShortFrameNameLookup[2]['tracknumber'] = 'TRK'; $ID3v2ShortFrameNameLookup[2]['tracknumber'] = 'TRK';
$ID3v2ShortFrameNameLookup[2]['track_number'] = 'TRK';
$ID3v2ShortFrameNameLookup[2]['size'] = 'TSI'; $ID3v2ShortFrameNameLookup[2]['size'] = 'TSI';
$ID3v2ShortFrameNameLookup[2]['encoder_settings'] = 'TSS'; $ID3v2ShortFrameNameLookup[2]['encoder_settings'] = 'TSS';
$ID3v2ShortFrameNameLookup[2]['description'] = 'TT1'; $ID3v2ShortFrameNameLookup[2]['description'] = 'TT1';
@ -1940,6 +1945,7 @@ class getid3_write_id3v2
// The following are common to ID3v2.3 and ID3v2.4 // The following are common to ID3v2.3 and ID3v2.4
$ID3v2ShortFrameNameLookup[3]['audio_encryption'] = 'AENC'; $ID3v2ShortFrameNameLookup[3]['audio_encryption'] = 'AENC';
$ID3v2ShortFrameNameLookup[3]['attached_picture'] = 'APIC'; $ID3v2ShortFrameNameLookup[3]['attached_picture'] = 'APIC';
$ID3v2ShortFrameNameLookup[3]['picture'] = 'APIC';
$ID3v2ShortFrameNameLookup[3]['comment'] = 'COMM'; $ID3v2ShortFrameNameLookup[3]['comment'] = 'COMM';
$ID3v2ShortFrameNameLookup[3]['commercial'] = 'COMR'; $ID3v2ShortFrameNameLookup[3]['commercial'] = 'COMR';
$ID3v2ShortFrameNameLookup[3]['encryption_method_registration'] = 'ENCR'; $ID3v2ShortFrameNameLookup[3]['encryption_method_registration'] = 'ENCR';
@ -1987,6 +1993,7 @@ class getid3_write_id3v2
$ID3v2ShortFrameNameLookup[3]['part_of_a_set'] = 'TPOS'; $ID3v2ShortFrameNameLookup[3]['part_of_a_set'] = 'TPOS';
$ID3v2ShortFrameNameLookup[3]['publisher'] = 'TPUB'; $ID3v2ShortFrameNameLookup[3]['publisher'] = 'TPUB';
$ID3v2ShortFrameNameLookup[3]['tracknumber'] = 'TRCK'; $ID3v2ShortFrameNameLookup[3]['tracknumber'] = 'TRCK';
$ID3v2ShortFrameNameLookup[3]['track_number'] = 'TRCK';
$ID3v2ShortFrameNameLookup[3]['internet_radio_station_name'] = 'TRSN'; $ID3v2ShortFrameNameLookup[3]['internet_radio_station_name'] = 'TRSN';
$ID3v2ShortFrameNameLookup[3]['internet_radio_station_owner'] = 'TRSO'; $ID3v2ShortFrameNameLookup[3]['internet_radio_station_owner'] = 'TRSO';
$ID3v2ShortFrameNameLookup[3]['isrc'] = 'TSRC'; $ID3v2ShortFrameNameLookup[3]['isrc'] = 'TSRC';
@ -2047,4 +2054,3 @@ class getid3_write_id3v2
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -16,21 +17,21 @@
class getid3_write_lyrics3 class getid3_write_lyrics3
{ {
var $filename; public $filename;
var $tag_data; public $tag_data;
//var $lyrics3_version = 2; // 1 or 2 //public $lyrics3_version = 2; // 1 or 2
var $warnings = array(); // any non-critical errors will be stored here public $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here public $errors = array(); // any critical errors will be stored here
function getid3_write_lyrics3() { public function getid3_write_lyrics3() {
return true; return true;
} }
function WriteLyrics3() { public function WriteLyrics3() {
$this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3'; $this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3';
return false; return false;
} }
function DeleteLyrics3() { public function DeleteLyrics3() {
// Initialize getID3 engine // Initialize getID3 engine
$getID3 = new getID3; $getID3 = new getID3;
$ThisFileInfo = $getID3->analyze($this->filename); $ThisFileInfo = $getID3->analyze($this->filename);
@ -40,7 +41,7 @@ class getid3_write_lyrics3
flock($fp, LOCK_EX); flock($fp, LOCK_EX);
$oldignoreuserabort = ignore_user_abort(true); $oldignoreuserabort = ignore_user_abort(true);
fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end'], SEEK_SET); fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end']);
$DataAfterLyrics3 = ''; $DataAfterLyrics3 = '';
if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) { if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) {
$DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']); $DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']);
@ -49,7 +50,7 @@ class getid3_write_lyrics3
ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']); ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']);
if (!empty($DataAfterLyrics3)) { if (!empty($DataAfterLyrics3)) {
fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start'], SEEK_SET); fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start']);
fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3)); fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3));
} }
@ -69,5 +70,3 @@ class getid3_write_lyrics3
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,16 +18,16 @@
class getid3_write_metaflac class getid3_write_metaflac
{ {
var $filename; public $filename;
var $tag_data; public $tag_data;
var $warnings = array(); // any non-critical errors will be stored here public $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here public $errors = array(); // any critical errors will be stored here
function getid3_write_metaflac() { public function getid3_write_metaflac() {
return true; return true;
} }
function WriteMetaFLAC() { public function WriteMetaFLAC() {
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written'; $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written';
@ -100,7 +101,7 @@ class getid3_write_metaflac
} }
function DeleteMetaFLAC() { public function DeleteMetaFLAC() {
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted'; $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted';
@ -146,18 +147,16 @@ class getid3_write_metaflac
} }
function CleanmetaflacName($originalcommentname) { public function CleanmetaflacName($originalcommentname) {
// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. // A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through // ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
// 0x7A inclusive (a-z). // 0x7A inclusive (a-z).
// replace invalid chars with a space, return uppercase text // replace invalid chars with a space, return uppercase text
// Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function // Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function
// note: *reg_replace() replaces nulls with empty string (not space) // note: *reg_replace() replaces nulls with empty string (not space)
return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname))); return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname)));
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -22,7 +23,7 @@
if (!defined('GETID3_INCLUDEPATH')) { if (!defined('GETID3_INCLUDEPATH')) {
throw new Exception('getid3.php MUST be included before calling getid3_writetags'); throw new Exception('getid3.php MUST be included before calling getid3_writetags');
} }
if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { if (!include_once(GETID3_INCLUDEPATH . 'getid3.lib.php')) {
throw new Exception('write.php depends on getid3.lib.php, which is missing.'); throw new Exception('write.php depends on getid3.lib.php, which is missing.');
} }
@ -47,28 +48,28 @@ if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
class getid3_writetags class getid3_writetags
{ {
// public // public
var $filename; // absolute filename of file to write tags to public $filename; // absolute filename of file to write tags to
var $tagformats = array(); // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real') public $tagformats = array(); // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real')
var $tag_data = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis') public $tag_data = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis')
var $tag_encoding = 'ISO-8859-1'; // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', ) public $tag_encoding = 'ISO-8859-1'; // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', )
var $overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data public $overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data
var $remove_other_tags = false; // if true will erase remove all existing tags and only write those passed in $tagformats; if false will ignore any tags not mentioned in $tagformats public $remove_other_tags = false; // if true will erase remove all existing tags and only write those passed in $tagformats; if false will ignore any tags not mentioned in $tagformats
var $id3v2_tag_language = 'eng'; // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html) public $id3v2_tag_language = 'eng'; // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html)
var $id3v2_paddedlength = 4096; // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter) public $id3v2_paddedlength = 4096; // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter)
var $warnings = array(); // any non-critical errors will be stored here public $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here public $errors = array(); // any critical errors will be stored here
// private // private
var $ThisFileInfo; // analysis of file before writing private $ThisFileInfo; // analysis of file before writing
function getid3_writetags() { public function getid3_writetags() {
return true; return true;
} }
function WriteTags() { public function WriteTags() {
if (empty($this->filename)) { if (empty($this->filename)) {
$this->errors[] = 'filename is undefined in getid3_writetags'; $this->errors[] = 'filename is undefined in getid3_writetags';
@ -178,9 +179,7 @@ class getid3_writetags
switch ($tagformat) { switch ($tagformat) {
case 'ape': case 'ape':
$GETID3_ERRORARRAY = &$this->errors; $GETID3_ERRORARRAY = &$this->errors;
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.apetag.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.apetag.php', __FILE__, true);
return false;
}
break; break;
case 'id3v1': case 'id3v1':
@ -189,9 +188,7 @@ class getid3_writetags
case 'metaflac': case 'metaflac':
case 'real': case 'real':
$GETID3_ERRORARRAY = &$this->errors; $GETID3_ERRORARRAY = &$this->errors;
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.'.$tagformat.'.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.'.$tagformat.'.php', __FILE__, true);
return false;
}
break; break;
case 'id3v2.2': case 'id3v2.2':
@ -199,9 +196,7 @@ class getid3_writetags
case 'id3v2.4': case 'id3v2.4':
case 'id3v2': case 'id3v2':
$GETID3_ERRORARRAY = &$this->errors; $GETID3_ERRORARRAY = &$this->errors;
if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v2.php', __FILE__, false)) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v2.php', __FILE__, true);
return false;
}
break; break;
default: default:
@ -341,7 +336,7 @@ class getid3_writetags
} }
function DeleteTags($TagFormatsToDelete) { public function DeleteTags($TagFormatsToDelete) {
foreach ($TagFormatsToDelete as $DeleteTagFormat) { foreach ($TagFormatsToDelete as $DeleteTagFormat) {
$success = false; // overridden if tag deletion is successful $success = false; // overridden if tag deletion is successful
switch ($DeleteTagFormat) { switch ($DeleteTagFormat) {
@ -414,7 +409,7 @@ class getid3_writetags
} }
function MergeExistingTagData($TagFormat, &$tag_data) { public function MergeExistingTagData($TagFormat, &$tag_data) {
// Merge supplied data with existing data, if requested // Merge supplied data with existing data, if requested
if ($this->overwrite_tags) { if ($this->overwrite_tags) {
// do nothing - ignore previous data // do nothing - ignore previous data
@ -428,7 +423,7 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve
return true; return true;
} }
function FormatDataForAPE() { public function FormatDataForAPE() {
$ape_tag_data = array(); $ape_tag_data = array();
foreach ($this->tag_data as $tag_key => $valuearray) { foreach ($this->tag_data as $tag_key => $valuearray) {
switch ($tag_key) { switch ($tag_key) {
@ -455,7 +450,7 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve
} }
function FormatDataForID3v1() { public function FormatDataForID3v1() {
$tag_data_id3v1['genreid'] = 255; $tag_data_id3v1['genreid'] = 255;
if (!empty($this->tag_data['GENRE'])) { if (!empty($this->tag_data['GENRE'])) {
foreach ($this->tag_data['GENRE'] as $key => $value) { foreach ($this->tag_data['GENRE'] as $key => $value) {
@ -479,7 +474,7 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve
return $tag_data_id3v1; return $tag_data_id3v1;
} }
function FormatDataForID3v2($id3v2_majorversion) { public function FormatDataForID3v2($id3v2_majorversion) {
$tag_data_id3v2 = array(); $tag_data_id3v2 = array();
$ID3v2_text_encoding_lookup[2] = array('ISO-8859-1'=>0, 'UTF-16'=>1); $ID3v2_text_encoding_lookup[2] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
@ -565,7 +560,7 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve
return $tag_data_id3v2; return $tag_data_id3v2;
} }
function FormatDataForVorbisComment() { public function FormatDataForVorbisComment() {
$tag_data_vorbiscomment = $this->tag_data; $tag_data_vorbiscomment = $this->tag_data;
// check for multi-line comment values - split out to multiple comments if neccesary // check for multi-line comment values - split out to multiple comments if neccesary
@ -594,13 +589,13 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve
return $tag_data_vorbiscomment; return $tag_data_vorbiscomment;
} }
function FormatDataForMetaFLAC() { public function FormatDataForMetaFLAC() {
// FLAC & OggFLAC use VorbisComments same as OggVorbis // FLAC & OggFLAC use VorbisComments same as OggVorbis
// but require metaflac to do the writing rather than vorbiscomment // but require metaflac to do the writing rather than vorbiscomment
return $this->FormatDataForVorbisComment(); return $this->FormatDataForVorbisComment();
} }
function FormatDataForReal() { public function FormatDataForReal() {
$tag_data_real['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE'] ) ? $this->tag_data['TITLE'] : array()))); $tag_data_real['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE'] ) ? $this->tag_data['TITLE'] : array())));
$tag_data_real['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST'] ) ? $this->tag_data['ARTIST'] : array()))); $tag_data_real['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST'] ) ? $this->tag_data['ARTIST'] : array())));
$tag_data_real['copyright'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COPYRIGHT']) ? $this->tag_data['COPYRIGHT'] : array()))); $tag_data_real['copyright'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COPYRIGHT']) ? $this->tag_data['COPYRIGHT'] : array())));
@ -611,5 +606,3 @@ throw new Exception('$this->overwrite_tags=false is known to be buggy in this ve
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -15,18 +16,18 @@
class getid3_write_real class getid3_write_real
{ {
var $filename; public $filename;
var $tag_data = array(); public $tag_data = array();
var $fread_buffer_size = 32768; // read buffer size in bytes public $fread_buffer_size = 32768; // read buffer size in bytes
var $warnings = array(); // any non-critical errors will be stored here public $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here public $errors = array(); // any critical errors will be stored here
var $paddedlength = 512; // minimum length of CONT tag in bytes public $paddedlength = 512; // minimum length of CONT tag in bytes
function getid3_write_real() { public function getid3_write_real() {
return true; return true;
} }
function WriteReal() { public function WriteReal() {
// File MUST be writeable - CHMOD(646) at least // File MUST be writeable - CHMOD(646) at least
if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) { if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
@ -56,7 +57,7 @@ class getid3_write_real
$new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']); $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
if (isset($oldChunkInfo['.RMF']['length']) && ($oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data))) { if (isset($oldChunkInfo['.RMF']['length']) && ($oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data))) {
fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET); fseek($fp_source, $oldChunkInfo['.RMF']['offset']);
fwrite($fp_source, $new__RMF_tag_data); fwrite($fp_source, $new__RMF_tag_data);
} else { } else {
$this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)'; $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
@ -65,7 +66,7 @@ class getid3_write_real
} }
if (isset($oldChunkInfo['PROP']['length']) && ($oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data))) { if (isset($oldChunkInfo['PROP']['length']) && ($oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data))) {
fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET); fseek($fp_source, $oldChunkInfo['PROP']['offset']);
fwrite($fp_source, $new_PROP_tag_data); fwrite($fp_source, $new_PROP_tag_data);
} else { } else {
$this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)'; $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
@ -76,7 +77,7 @@ class getid3_write_real
if (isset($oldChunkInfo['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) { if (isset($oldChunkInfo['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) {
// new data length is same as old data length - just overwrite // new data length is same as old data length - just overwrite
fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET); fseek($fp_source, $oldChunkInfo['CONT']['offset']);
fwrite($fp_source, $new_CONT_tag_data); fwrite($fp_source, $new_CONT_tag_data);
fclose($fp_source); fclose($fp_source);
return true; return true;
@ -98,7 +99,7 @@ class getid3_write_real
rewind($fp_source); rewind($fp_source);
fwrite($fp_temp, fread($fp_source, $BeforeOffset)); fwrite($fp_temp, fread($fp_source, $BeforeOffset));
fwrite($fp_temp, $new_CONT_tag_data); fwrite($fp_temp, $new_CONT_tag_data);
fseek($fp_source, $AfterOffset, SEEK_SET); fseek($fp_source, $AfterOffset);
while ($buffer = fread($fp_source, $this->fread_buffer_size)) { while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
fwrite($fp_temp, $buffer, strlen($buffer)); fwrite($fp_temp, $buffer, strlen($buffer));
} }
@ -126,7 +127,7 @@ class getid3_write_real
return false; return false;
} }
function GenerateRMFchunk(&$chunks) { public function GenerateRMFchunk(&$chunks) {
$oldCONTexists = false; $oldCONTexists = false;
foreach ($chunks as $key => $chunk) { foreach ($chunks as $key => $chunk) {
$chunkNameKeys[$chunk['name']] = $key; $chunkNameKeys[$chunk['name']] = $key;
@ -144,7 +145,7 @@ class getid3_write_real
return $RMFchunk; return $RMFchunk;
} }
function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) { public function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) {
$old_CONT_length = 0; $old_CONT_length = 0;
$old_DATA_offset = 0; $old_DATA_offset = 0;
$old_INDX_offset = 0; $old_INDX_offset = 0;
@ -181,7 +182,7 @@ class getid3_write_real
return $PROPchunk; return $PROPchunk;
} }
function GenerateCONTchunk() { public function GenerateCONTchunk() {
foreach ($this->tag_data as $key => $value) { foreach ($this->tag_data as $key => $value) {
// limit each value to 0xFFFF bytes // limit each value to 0xFFFF bytes
$this->tag_data[$key] = substr($value, 0, 65535); $this->tag_data[$key] = substr($value, 0, 65535);
@ -210,7 +211,7 @@ class getid3_write_real
return $CONTchunk; return $CONTchunk;
} }
function RemoveReal() { public function RemoveReal() {
// File MUST be writeable - CHMOD(646) at least // File MUST be writeable - CHMOD(646) at least
if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) { if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
@ -245,7 +246,7 @@ class getid3_write_real
rewind($fp_source); rewind($fp_source);
fwrite($fp_temp, fread($fp_source, $BeforeOffset)); fwrite($fp_temp, fread($fp_source, $BeforeOffset));
fseek($fp_source, $AfterOffset, SEEK_SET); fseek($fp_source, $AfterOffset);
while ($buffer = fread($fp_source, $this->fread_buffer_size)) { while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
fwrite($fp_temp, $buffer, strlen($buffer)); fwrite($fp_temp, $buffer, strlen($buffer));
} }
@ -271,5 +272,3 @@ class getid3_write_real
} }
} }
?>

View file

@ -3,6 +3,7 @@
/// getID3() by James Heinrich <info@getid3.org> // /// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net // // available at http://getid3.sourceforge.net //
// or http://www.getid3.org // // or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// See readme.txt for more details // // See readme.txt for more details //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -17,16 +18,16 @@
class getid3_write_vorbiscomment class getid3_write_vorbiscomment
{ {
var $filename; public $filename;
var $tag_data; public $tag_data;
var $warnings = array(); // any non-critical errors will be stored here public $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here public $errors = array(); // any critical errors will be stored here
function getid3_write_vorbiscomment() { public function getid3_write_vorbiscomment() {
return true; return true;
} }
function WriteVorbisComment() { public function WriteVorbisComment() {
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written'; $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written';
@ -99,23 +100,21 @@ class getid3_write_vorbiscomment
return true; return true;
} }
function DeleteVorbisComment() { public function DeleteVorbisComment() {
$this->tag_data = array(array()); $this->tag_data = array(array());
return $this->WriteVorbisComment(); return $this->WriteVorbisComment();
} }
function CleanVorbisCommentName($originalcommentname) { public function CleanVorbisCommentName($originalcommentname) {
// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. // A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through // ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
// 0x7A inclusive (a-z). // 0x7A inclusive (a-z).
// replace invalid chars with a space, return uppercase text // replace invalid chars with a space, return uppercase text
// Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function // Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function
// note: *reg_replace() replaces nulls with empty string (not space) // note: *reg_replace() replaces nulls with empty string (not space)
return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname))); return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname)));
} }
} }
?>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,56 @@
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /helperapps/readme.txt - part of getID3() //
// List of binary files required under Windows for some //
// features and/or file formats //
// See /readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
This directory should contain binaries of various helper applications
that getID3() depends on to handle some file formats under Windows.
The location of this directory is configurable in /getid3/getid3.php
as GETID3_HELPERAPPSDIR
If this directory is empty, or you are missing any files, please
download the latest version of the "getID3()-WindowsSupport" package
from the usual download location (http://getid3.sourceforge.net)
Included files:
=====================================================
Taken from http://www.cygwin.com/
* cygwin1.dll
Taken from http://unxutils.sourceforge.net/
* head.exe
* md5sum.exe
* tail.exe
Taken from http://ebible.org/mpj/software.htm
* sha1sum.exe
Taken from http://www.vorbis.com/download.psp
* vorbiscomment.exe
Taken from http://flac.sourceforge.net/download.html
* metaflac.exe
Taken from http://www.etree.org/shncom.html
* shorten.exe
/////////////////////////////////////////////////////////////////
Changelog:
2003.12.29:
* Initial release

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more