rlm@3: | rlm@3: // | Allan Hansen | rlm@3: // +----------------------------------------------------------------------+ rlm@3: // | module.archive.gzip.php | rlm@3: // | module for analyzing GZIP files | rlm@3: // | dependencies: NONE | rlm@3: // +----------------------------------------------------------------------+ rlm@3: // | FLV module by Seth Kaufman | rlm@3: // | | rlm@3: // | * version 0.1 (26 June 2005) | rlm@3: // | | rlm@3: // | minor modifications by James Heinrich | rlm@3: // | * version 0.1.1 (15 July 2005) | rlm@3: // | | rlm@3: // | Support for On2 VP6 codec and meta information by | rlm@3: // | Steve Webster | rlm@3: // | * version 0.2 (22 February 2006) | rlm@3: // | | rlm@3: // | Modified to not read entire file into memory | rlm@3: // | by James Heinrich | rlm@3: // | * version 0.3 (15 June 2006) | rlm@3: // | | rlm@3: // | Modifications by Allan Hansen | rlm@3: // | Adapted module for PHP5 and getID3 2.0.0. | rlm@3: // +----------------------------------------------------------------------+ rlm@3: // rlm@3: // $Id: module.audio-video.flv.php,v 1.7 2006/11/10 11:20:12 ah Exp $ rlm@3: rlm@3: rlm@3: rlm@3: class getid3_flv extends getid3_handler rlm@3: { rlm@3: rlm@3: const TAG_AUDIO = 8; rlm@3: const TAG_VIDEO = 9; rlm@3: const TAG_META = 18; rlm@3: rlm@3: const VIDEO_H263 = 2; rlm@3: const VIDEO_SCREEN = 3; rlm@3: const VIDEO_VP6 = 4; rlm@3: rlm@3: rlm@3: public function Analyze() rlm@3: { rlm@3: $info = &$this->getid3->info; rlm@3: rlm@3: $info['flv'] = array (); rlm@3: $info_flv = &$info['flv']; rlm@3: rlm@3: fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); rlm@3: rlm@3: $flv_data_length = $info['avdataend'] - $info['avdataoffset']; rlm@3: $flv_header = fread($this->getid3->fp, 5); rlm@3: rlm@3: $info['fileformat'] = 'flv'; rlm@3: $info_flv['header']['signature'] = substr($flv_header, 0, 3); rlm@3: $info_flv['header']['version'] = getid3_lib::BigEndian2Int(substr($flv_header, 3, 1)); rlm@3: $type_flags = getid3_lib::BigEndian2Int(substr($flv_header, 4, 1)); rlm@3: rlm@3: $info_flv['header']['hasAudio'] = (bool) ($type_flags & 0x04); rlm@3: $info_flv['header']['hasVideo'] = (bool) ($type_flags & 0x01); rlm@3: rlm@3: $frame_size_data_length = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 4)); rlm@3: $flv_header_frame_length = 9; rlm@3: if ($frame_size_data_length > $flv_header_frame_length) { rlm@3: fseek($this->getid3->fp, $frame_size_data_length - $flv_header_frame_length, SEEK_CUR); rlm@3: } rlm@3: rlm@3: $duration = 0; rlm@3: while ((ftell($this->getid3->fp) + 1) < $info['avdataend']) { rlm@3: rlm@3: $this_tag_header = fread($this->getid3->fp, 16); rlm@3: rlm@3: $previous_tag_length = getid3_lib::BigEndian2Int(substr($this_tag_header, 0, 4)); rlm@3: $tag_type = getid3_lib::BigEndian2Int(substr($this_tag_header, 4, 1)); rlm@3: $data_length = getid3_lib::BigEndian2Int(substr($this_tag_header, 5, 3)); rlm@3: $timestamp = getid3_lib::BigEndian2Int(substr($this_tag_header, 8, 3)); rlm@3: $last_header_byte = getid3_lib::BigEndian2Int(substr($this_tag_header, 15, 1)); rlm@3: $next_offset = ftell($this->getid3->fp) - 1 + $data_length; rlm@3: rlm@3: switch ($tag_type) { rlm@3: rlm@3: case getid3_flv::TAG_AUDIO: rlm@3: if (!isset($info_flv['audio']['audioFormat'])) { rlm@3: $info_flv['audio']['audioFormat'] = $last_header_byte & 0x07; rlm@3: $info_flv['audio']['audioRate'] = ($last_header_byte & 0x30) / 0x10; rlm@3: $info_flv['audio']['audioSampleSize'] = ($last_header_byte & 0x40) / 0x40; rlm@3: $info_flv['audio']['audioType'] = ($last_header_byte & 0x80) / 0x80; rlm@3: } rlm@3: break; rlm@3: rlm@3: rlm@3: case getid3_flv::TAG_VIDEO: rlm@3: if (!isset($info_flv['video']['videoCodec'])) { rlm@3: $info_flv['video']['videoCodec'] = $last_header_byte & 0x07; rlm@3: rlm@3: $flv_video_header = fread($this->getid3->fp, 11); rlm@3: rlm@3: if ($info_flv['video']['videoCodec'] != getid3_flv::VIDEO_VP6) { rlm@3: rlm@3: $picture_size_type = (getid3_lib::BigEndian2Int(substr($flv_video_header, 3, 2))) >> 7; rlm@3: $picture_size_type = $picture_size_type & 0x0007; rlm@3: $info_flv['header']['videoSizeType'] = $picture_size_type; rlm@3: rlm@3: switch ($picture_size_type) { rlm@3: case 0: rlm@3: $picture_size_enc = getid3_lib::BigEndian2Int(substr($flv_video_header, 5, 2)); rlm@3: $picture_size_enc <<= 1; rlm@3: $info['video']['resolution_x'] = ($picture_size_enc & 0xFF00) >> 8; rlm@3: $picture_size_enc = getid3_lib::BigEndian2Int(substr($flv_video_header, 6, 2)); rlm@3: $picture_size_enc <<= 1; rlm@3: $info['video']['resolution_y'] = ($picture_size_enc & 0xFF00) >> 8; rlm@3: break; rlm@3: rlm@3: case 1: rlm@3: $picture_size_enc = getid3_lib::BigEndian2Int(substr($flv_video_header, 5, 4)); rlm@3: $picture_size_enc <<= 1; rlm@3: $info['video']['resolution_x'] = ($picture_size_enc & 0xFFFF0000) >> 16; rlm@3: rlm@3: $picture_size_enc = getid3_lib::BigEndian2Int(substr($flv_video_header, 7, 4)); rlm@3: $picture_size_enc <<= 1; rlm@3: $info['video']['resolution_y'] = ($picture_size_enc & 0xFFFF0000) >> 16; rlm@3: break; rlm@3: rlm@3: case 2: rlm@3: $info['video']['resolution_x'] = 352; rlm@3: $info['video']['resolution_y'] = 288; rlm@3: break; rlm@3: rlm@3: case 3: rlm@3: $info['video']['resolution_x'] = 176; rlm@3: $info['video']['resolution_y'] = 144; rlm@3: break; rlm@3: rlm@3: case 4: rlm@3: $info['video']['resolution_x'] = 128; rlm@3: $info['video']['resolution_y'] = 96; rlm@3: break; rlm@3: rlm@3: case 5: rlm@3: $info['video']['resolution_x'] = 320; rlm@3: $info['video']['resolution_y'] = 240; rlm@3: break; rlm@3: rlm@3: case 6: rlm@3: $info['video']['resolution_x'] = 160; rlm@3: $info['video']['resolution_y'] = 120; rlm@3: break; rlm@3: rlm@3: default: rlm@3: $info['video']['resolution_x'] = 0; rlm@3: $info['video']['resolution_y'] = 0; rlm@3: break; rlm@3: } rlm@3: } rlm@3: } rlm@3: break; rlm@3: rlm@3: rlm@3: // Meta tag rlm@3: case getid3_flv::TAG_META: rlm@3: rlm@3: fseek($this->getid3->fp, -1, SEEK_CUR); rlm@3: $reader = new AMFReader(new AMFStream(fread($this->getid3->fp, $data_length))); rlm@3: $event_name = $reader->readData(); rlm@3: $info['meta'][$event_name] = $reader->readData(); rlm@3: unset($reader); rlm@3: rlm@3: $info['video']['frame_rate'] = @$info['meta']['onMetaData']['framerate']; rlm@3: $info['video']['resolution_x'] = @$info['meta']['onMetaData']['width']; rlm@3: $info['video']['resolution_y'] = @$info['meta']['onMetaData']['height']; rlm@3: break; rlm@3: rlm@3: default: rlm@3: // noop rlm@3: break; rlm@3: } rlm@3: rlm@3: if ($timestamp > $duration) { rlm@3: $duration = $timestamp; rlm@3: } rlm@3: rlm@3: fseek($this->getid3->fp, $next_offset, SEEK_SET); rlm@3: } rlm@3: rlm@3: if ($info['playtime_seconds'] = $duration / 1000) { rlm@3: $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']; rlm@3: } rlm@3: rlm@3: if ($info_flv['header']['hasAudio']) { rlm@3: $info['audio']['codec'] = $this->FLVaudioFormat($info_flv['audio']['audioFormat']); rlm@3: $info['audio']['sample_rate'] = $this->FLVaudioRate($info_flv['audio']['audioRate']); rlm@3: $info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info_flv['audio']['audioSampleSize']); rlm@3: rlm@3: $info['audio']['channels'] = $info_flv['audio']['audioType'] + 1; // 0=mono,1=stereo rlm@3: $info['audio']['lossless'] = ($info_flv['audio']['audioFormat'] ? false : true); // 0=uncompressed rlm@3: $info['audio']['dataformat'] = 'flv'; rlm@3: } rlm@3: if (@$info_flv['header']['hasVideo']) { rlm@3: $info['video']['codec'] = $this->FLVvideoCodec($info_flv['video']['videoCodec']); rlm@3: $info['video']['dataformat'] = 'flv'; rlm@3: $info['video']['lossless'] = false; rlm@3: } rlm@3: rlm@3: return true; rlm@3: } rlm@3: rlm@3: rlm@3: public static function FLVaudioFormat($id) { rlm@3: rlm@3: static $lookup = array( rlm@3: 0 => 'uncompressed', rlm@3: 1 => 'ADPCM', rlm@3: 2 => 'mp3', rlm@3: 5 => 'Nellymoser 8kHz mono', rlm@3: 6 => 'Nellymoser', rlm@3: ); rlm@3: return (@$lookup[$id] ? @$lookup[$id] : false); rlm@3: } rlm@3: rlm@3: rlm@3: public static function FLVaudioRate($id) { rlm@3: rlm@3: static $lookup = array( rlm@3: 0 => 5500, rlm@3: 1 => 11025, rlm@3: 2 => 22050, rlm@3: 3 => 44100, rlm@3: ); rlm@3: return (@$lookup[$id] ? @$lookup[$id] : false); rlm@3: } rlm@3: rlm@3: rlm@3: public static function FLVaudioBitDepth($id) { rlm@3: rlm@3: static $lookup = array( rlm@3: 0 => 8, rlm@3: 1 => 16, rlm@3: ); rlm@3: return (@$lookup[$id] ? @$lookup[$id] : false); rlm@3: } rlm@3: rlm@3: rlm@3: public static function FLVvideoCodec($id) { rlm@3: rlm@3: static $lookup = array( rlm@3: getid3_flv::VIDEO_H263 => 'Sorenson H.263', rlm@3: getid3_flv::VIDEO_SCREEN => 'Screen video', rlm@3: getid3_flv::VIDEO_VP6 => 'On2 VP6', rlm@3: ); rlm@3: return (@$lookup[$id] ? @$lookup[$id] : false); rlm@3: } rlm@3: } rlm@3: rlm@3: rlm@3: rlm@3: class AMFStream rlm@3: { rlm@3: public $bytes; rlm@3: public $pos; rlm@3: rlm@3: rlm@3: public function AMFStream($bytes) { rlm@3: rlm@3: $this->bytes = $bytes; rlm@3: $this->pos = 0; rlm@3: } rlm@3: rlm@3: rlm@3: public function readByte() { rlm@3: rlm@3: return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1)); rlm@3: } rlm@3: rlm@3: rlm@3: public function readInt() { rlm@3: rlm@3: return ($this->readByte() << 8) + $this->readByte(); rlm@3: } rlm@3: rlm@3: rlm@3: public function readLong() { rlm@3: rlm@3: return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); rlm@3: } rlm@3: rlm@3: rlm@3: public function readDouble() { rlm@3: rlm@3: return getid3_lib::BigEndian2Float($this->read(8)); rlm@3: } rlm@3: rlm@3: rlm@3: public function readUTF() { rlm@3: rlm@3: $length = $this->readInt(); rlm@3: return $this->read($length); rlm@3: } rlm@3: rlm@3: rlm@3: public function readLongUTF() { rlm@3: rlm@3: $length = $this->readLong(); rlm@3: return $this->read($length); rlm@3: } rlm@3: rlm@3: rlm@3: public function read($length) { rlm@3: rlm@3: $val = substr($this->bytes, $this->pos, $length); rlm@3: $this->pos += $length; rlm@3: return $val; rlm@3: } rlm@3: rlm@3: rlm@3: public function peekByte() { rlm@3: rlm@3: $pos = $this->pos; rlm@3: $val = $this->readByte(); rlm@3: $this->pos = $pos; rlm@3: return $val; rlm@3: } rlm@3: rlm@3: rlm@3: public function peekInt() { rlm@3: rlm@3: $pos = $this->pos; rlm@3: $val = $this->readInt(); rlm@3: $this->pos = $pos; rlm@3: return $val; rlm@3: } rlm@3: rlm@3: rlm@3: public function peekLong() { rlm@3: rlm@3: $pos = $this->pos; rlm@3: $val = $this->readLong(); rlm@3: $this->pos = $pos; rlm@3: return $val; rlm@3: } rlm@3: rlm@3: rlm@3: public function peekDouble() { rlm@3: rlm@3: $pos = $this->pos; rlm@3: $val = $this->readDouble(); rlm@3: $this->pos = $pos; rlm@3: return $val; rlm@3: } rlm@3: rlm@3: rlm@3: public function peekUTF() { rlm@3: rlm@3: $pos = $this->pos; rlm@3: $val = $this->readUTF(); rlm@3: $this->pos = $pos; rlm@3: return $val; rlm@3: } rlm@3: rlm@3: rlm@3: public function peekLongUTF() { rlm@3: rlm@3: $pos = $this->pos; rlm@3: $val = $this->readLongUTF(); rlm@3: $this->pos = $pos; rlm@3: return $val; rlm@3: } rlm@3: } rlm@3: rlm@3: rlm@3: rlm@3: class AMFReader rlm@3: { rlm@3: public $stream; rlm@3: rlm@3: public function __construct($stream) { rlm@3: rlm@3: $this->stream = $stream; rlm@3: } rlm@3: rlm@3: rlm@3: public function readData() { rlm@3: rlm@3: $value = null; rlm@3: rlm@3: $type = $this->stream->readByte(); rlm@3: rlm@3: switch($type) { rlm@3: // Double rlm@3: case 0: rlm@3: $value = $this->readDouble(); rlm@3: break; rlm@3: rlm@3: // Boolean rlm@3: case 1: rlm@3: $value = $this->readBoolean(); rlm@3: break; rlm@3: rlm@3: // String rlm@3: case 2: rlm@3: $value = $this->readString(); rlm@3: break; rlm@3: rlm@3: // Object rlm@3: case 3: rlm@3: $value = $this->readObject(); rlm@3: break; rlm@3: rlm@3: // null rlm@3: case 6: rlm@3: return null; rlm@3: break; rlm@3: rlm@3: // Mixed array rlm@3: case 8: rlm@3: $value = $this->readMixedArray(); rlm@3: break; rlm@3: rlm@3: // Array rlm@3: case 10: rlm@3: $value = $this->readArray(); rlm@3: break; rlm@3: rlm@3: // Date rlm@3: case 11: rlm@3: $value = $this->readDate(); rlm@3: break; rlm@3: rlm@3: // Long string rlm@3: case 13: rlm@3: $value = $this->readLongString(); rlm@3: break; rlm@3: rlm@3: // XML (handled as string) rlm@3: case 15: rlm@3: $value = $this->readXML(); rlm@3: break; rlm@3: rlm@3: // Typed object (handled as object) rlm@3: case 16: rlm@3: $value = $this->readTypedObject(); rlm@3: break; rlm@3: rlm@3: // Long string rlm@3: default: rlm@3: $value = '(unknown or unsupported data type)'; rlm@3: break; rlm@3: } rlm@3: rlm@3: return $value; rlm@3: } rlm@3: rlm@3: rlm@3: public function readDouble() { rlm@3: rlm@3: return $this->stream->readDouble(); rlm@3: } rlm@3: rlm@3: rlm@3: public function readBoolean() { rlm@3: rlm@3: return $this->stream->readByte() == 1; rlm@3: } rlm@3: rlm@3: rlm@3: public function readString() { rlm@3: rlm@3: return $this->stream->readUTF(); rlm@3: } rlm@3: rlm@3: rlm@3: public function readObject() { rlm@3: rlm@3: // Get highest numerical index - ignored rlm@3: $highestIndex = $this->stream->readLong(); rlm@3: rlm@3: $data = array(); rlm@3: rlm@3: while ($key = $this->stream->readUTF()) { rlm@3: // Mixed array record ends with empty string (0x00 0x00) and 0x09 rlm@3: if (($key == '') && ($this->stream->peekByte() == 0x09)) { rlm@3: // Consume byte rlm@3: $this->stream->readByte(); rlm@3: break; rlm@3: } rlm@3: rlm@3: $data[$key] = $this->readData(); rlm@3: } rlm@3: rlm@3: return $data; rlm@3: } rlm@3: rlm@3: rlm@3: public function readMixedArray() { rlm@3: rlm@3: // Get highest numerical index - ignored rlm@3: $highestIndex = $this->stream->readLong(); rlm@3: rlm@3: $data = array(); rlm@3: rlm@3: while ($key = $this->stream->readUTF()) { rlm@3: // Mixed array record ends with empty string (0x00 0x00) and 0x09 rlm@3: if (($key == '') && ($this->stream->peekByte() == 0x09)) { rlm@3: // Consume byte rlm@3: $this->stream->readByte(); rlm@3: break; rlm@3: } rlm@3: rlm@3: if (is_numeric($key)) { rlm@3: $key = (float) $key; rlm@3: } rlm@3: rlm@3: $data[$key] = $this->readData(); rlm@3: } rlm@3: rlm@3: return $data; rlm@3: } rlm@3: rlm@3: rlm@3: public function readArray() { rlm@3: rlm@3: $length = $this->stream->readLong(); rlm@3: rlm@3: $data = array(); rlm@3: rlm@3: for ($i = 0; $i < count($length); $i++) { rlm@3: $data[] = $this->readData(); rlm@3: } rlm@3: rlm@3: return $data; rlm@3: } rlm@3: rlm@3: rlm@3: public function readDate() { rlm@3: rlm@3: $timestamp = $this->stream->readDouble(); rlm@3: $timezone = $this->stream->readInt(); rlm@3: return $timestamp; rlm@3: } rlm@3: rlm@3: rlm@3: public function readLongString() { rlm@3: rlm@3: return $this->stream->readLongUTF(); rlm@3: } rlm@3: rlm@3: rlm@3: public function readXML() { rlm@3: rlm@3: return $this->stream->readLongUTF(); rlm@3: } rlm@3: rlm@3: rlm@3: public function readTypedObject() { rlm@3: rlm@3: $className = $this->stream->readUTF(); rlm@3: return $this->readObject(); rlm@3: } rlm@3: } rlm@3: rlm@3: ?>