/** * Get a files meta data and cuepoints * * @param string $filename * @return array $meta */ function getMeta( $file ) { // Open file $f = fopen( $file,'rb' ); // Read header $buffer = fread( $f, 9 ); $header = @unpack( 'C3signature/Cversion/Cflags/Noffset', $buffer ); // If signature is valid, go on if( $header['signature1'] == 70 && $header['signature2'] == 76 && $header['signature3'] == 86 ){ // Read tags fseek( $f, $header['offset'] ); while( feof( $f ) == false ){ // Read tag header and check length $buffer = fread( $f, 15 ); if( strlen( $buffer ) < 15) break; // Interpret header $tag = @unpack( 'Nprevsize/C1type/C3size/C4timestamp/C3stream', $buffer ); $tag['size'] = ( $tag['size1'] << 16 ) + ( $tag['size2'] << 8 ) + ( $tag['size3'] ); if( !$tag['size'] ) break; // Read tag body (max 16k) $next = ftell( $f ) + $tag['size']; $body = fread( $f, min( $tag['size'], 16384 ) ); // Seek fseek( $f, $next ); switch( $tag['type'] ){ case 0x09: // Unpack flags $info = @unpack( 'Cflags', $body ); // Get frame type and store it $ft = ( $info['flags'] >> 4 ) & 15; if( !isset( $fw ) && !isset( $fh ) && $ft == 0x01 ){ switch( $info['flags'] & 15 ){ case 0x04: case 0x05: //codec_on2_vp6: //codec_on2_vp6alpha: $fw = ord( $body[5] )*16; $fh = ord( $body[6] )*16; break; //codec_sorenson_h263: case 0x02: $bin = ''; for( $i=0; $i<16; $i++ ){ $sbin = decbin( ord( $body[$i+1] ) ); $bin .= str_pad( $sbin, 8, '0', STR_PAD_LEFT ); } // Size type $size = bindec( substr( $bin, 30, 3 ) ); // Get width/height switch ($size) { case 0: // Custom, 8 bit $fw = bindec( substr( $bin, 33, 8 ) ); $fh = bindec( substr( $bin, 41, 8 ) ); break; case 1: // Custom, 16 bit $fw = bindec( substr( $bin, 33, 16 ) ); $fh = bindec( substr( $bin, 49, 16 ) ); break; case 2: $fw = 352; $fh = 288; break; case 3: $fw = 176; $fh = 144; break; case 4: $fw = 128; $fh = 96; break; case 5: $fw = 320; $fh = 240; break; case 6: $fw = 160; $fh = 120; break; } break; } } break; case 0x12: $parser = new AMF0Parser(); // Parse data $data = $parser->readAllPackets( $body ); $meta = $data[0] == 'onMetaData' ? $data[1] : array('width'=>'', 'height'=>'', 'duration'=>''); break; } }// while } // Close file fclose( $f ); // Width if ( isset( $fw ) && !isset( $meta['width'] ) ) { $meta['width'] = $fw; } // Height if ( isset( $fh ) && !isset( $meta['height'] ) ) { $meta['height'] = $fh; } if( isset( $meta['duration'] ) ){ $time = abs( $meta['duration'] ); $minutes = floor( $time / 60); $seconds = round( ( ( $time / 60 ) - floor( $time / 60 ) ) * 60 ); if( $seconds >= 60 ){ $seconds -= 60; $minutes++; } $meta['duration'] = $minutes . ':' . str_pad( $seconds, 2, 0, STR_PAD_LEFT ); }else{ $meta['duration'] = 0; } return $meta; }
/** * Rewrite (or strip) meta data and cuepoints from FLV file * * @author Tommy Lacroix <*****@*****.**> * @access public * @param string $in Input file * @param string $out Output file (must be different!) * @param string[] $meta Meta data (ie. array('width'=>320,'height'=>240,...), optional * @param array $cuepoints Cuepoints (ie. array(array('name'=>'cue1','time'=>'4.4',type=>'event'),...), optional */ public function rewriteMeta($in, $out, $meta = false, $cuepoints = false) { // No tag found yet $tagParsed = 0; // Open input file $f = fopen($in, 'rb'); // Read header $buf = fread($f, 9); $header = unpack('C3signature/Cversion/Cflags/Noffset', $buf); // Check signature $signature = $header['signature1'] == 70 && $header['signature2'] == 76 && $header['signature3'] == 86; // If signature is valid, go on if ($signature) { // Open output file, and write header $o = fopen($out, 'w'); if (!$o) { throw new Exception('Cannot open output file!'); } fwrite($o, $buf, 9); // Version //$version = $header['version']; // Write meta data if ($meta !== false) { $parser = new AMF0Parser(); $metadata = $parser->writeString('onMetaData'); $metadata .= $parser->writeMixedArray($meta); $metadataSize = substr(pack('N', strlen($metadata)), 1); // Write $metadataHead = pack('N', 0) . chr(18) . $metadataSize . pack('N', 0) . chr(0) . chr(0) . chr(0); fwrite($o, $metadataHead); fwrite($o, $metadata); } // Read tags //$prevTagSize = 0; $lastTimeStamp = 0; do { // Read tag header and check length $buf = fread($f, 15); if (strlen($buf) < 15) { break; } // Interpret header $tagInfo = unpack('Nprevsize/C1type/C3size/C4timestamp/C3stream', $buf); $tagInfo['size'] = ($tagInfo['size1'] << 16) + ($tagInfo['size2'] << 8) + $tagInfo['size3']; $tagInfo['stream'] = ($tagInfo['stream1'] << 16) + ($tagInfo['stream2'] << 8) + $tagInfo['stream3']; $tagInfo['timestamp'] = ($tagInfo['timestamp1'] << 16) + ($tagInfo['timestamp2'] << 8) + $tagInfo['timestamp3'] + ($tagInfo['timestamp4'] << 24); // Need we insert a cuepoint here? if ($cuepoints !== false) { foreach ($cuepoints as $index => $cuepoint) { if ($lastTimeStamp < $cuepoint['time'] * 1000 && $tagInfo['timestamp'] >= $cuepoint['time'] * 1000) { // Write cuepoint $parser = new AMF0Parser(); $metadata = $parser->writeString('onCuePoint'); $metadata .= $parser->writeMixedArray($cuepoint); $metadataSize = substr(pack('N', strlen($metadata)), 1); // Write first $metadataHead = pack('N', $tagInfo['prevsize']) . chr(18) . $metadataSize . pack('N', 0) . chr(0) . chr(0) . chr(0); fwrite($o, $metadataHead); fwrite($o, $metadata); // Update prevsize in $buf // @todo // Unset cuepoint unset($cuepoints[$index]); } } } // Validate previous offset //if ($tagInfo['prevsize'] != $prevTagSize) { // Do nothing //} // Read tag body $body = fread($f, $tagInfo['size']); // Intepret body switch ($tagInfo['type']) { case 0x9: // Video tag // Write to output file fwrite($o, $buf, 15); fwrite($o, $body, $tagInfo['size']); break; case 0x8: // Audio tag // Write to output file fwrite($o, $buf, 15); fwrite($o, $body, $tagInfo['size']); break; case 0x12: // Meta tag // Skip break; } // Increase parsed tag count $tagParsed++; } while (feof($f) == false); } // Close file fclose($f); fclose($o); }