Newer
Older
tuve_scripts / include / FLV / play.php
<?php

include_once 'FLV/FLV.php';

class MyFLV extends FLV {
	
	/**
	 * On audio-only files the frame index will use this as minimum gap 
	 */
	private $audioFrameGap = 3;
	
	private $origMetaOfs = 0;
	private $origMetaSize = 0;
	private $origMetaData;
	private $compMetaData;
	
	
	function computeMetaData()
	{
		$this->compMetaData = array();
		$this->compMetaData['metadatacreator'] = 'FLV Tools for PHP v0.1 by DrSlump';
		$this->compMetaData['metadatadate'] = gmdate('Y-m-d\TH:i:s') . '.000Z';
		$this->compMetaData['keyframes'] = array();
		$this->compMetaData['keyframes']['filepositions'] = array();
		$this->compMetaData['keyframes']['times'] = array();
		
		$this->origMetaOfs = 0;
		$this->origMetaSize = 0;
		$this->origMetaData = null;
		
		$skipTagTypes = array();
		while ($tag = $this->getTag( $skipTagTypes ))
		{
			// pre-calculate the timestamp as seconds
	    	$ts = number_format($tag->timestamp/1000, 3);
	    
	    	if ($tag->timestamp > 0)
		    	$this->compMetaData['lasttimestamp'] = $ts;
	    
	    	switch ($tag->type)
	    	{
	        	case FLV_Tag::TYPE_VIDEO :
	        	        	
	           		//Optimization, extract the frametype without analyzing the tag body
	           		if ((ord($tag->body[0]) >> 4) == FLV_Tag_Video::FRAME_KEYFRAME)
	           		{
						$this->compMetaData['keyframes']['filepositions'][] = $this->getTagOffset();
						$this->compMetaData['keyframes']['times'][] = $ts;
	           		}
	           	
	            	if ( !in_array(FLV_TAG::TYPE_VIDEO, $skipTagTypes) )
	            	{
	                	$this->compMetaData['width'] = $tag->width;
	                	$this->compMetaData['height'] = $tag->height;
	                	$this->compMetaData['videocodecid'] = $tag->codec;
						//Processing one video tag is enough               
	            		array_push( $skipTagTypes, FLV_Tag::TYPE_VIDEO );
	            	}
	            
	        		break;
	        	
	        	case FLV_Tag::TYPE_AUDIO :
	        	
					//Save audio frame positions when there is no video 
	        		if (!$flv->hasVideo && $ts - $oldTs > $this->audioFrameGap)
	        		{
		        		$this->compMetaData['keyframes']['filepositions'][] = $this->getTagOffset();
		        		$this->compMetaData['keyframes']['times'][] = $ts;
		        		$oldTs = $ts;
	        		}
	        	
	            	if ( !in_array( FLV_Tag::TYPE_AUDIO, $skipTagTypes) )  
	            	{
		            	$this->compMetaData['audiocodecid'] = $tag->codec;
		            	$this->compMetaData['audiofreqid'] = $tag->frequency;
		            	$this->compMetaData['audiodepthid'] = $tag->depth;
		            	$this->compMetaData['audiomodeid'] = $tag->mode;
		            
						//Processing one audio tag is enough
	            		array_push( $skipTagTypes, FLV_Tag::TYPE_AUDIO );
	            	}
					
	        		break;
					
	        	case FLV_Tag::TYPE_DATA :
	            	if ($tag->name == 'onMetaData')
	            	{
	            		$this->origMetaOfs = $this->getTagOffset();
	            		$this->origMetaSize = $tag->size + self::TAG_HEADER_SIZE;
	            		$this->origMetaData = $tag->value;
	            	}
	        		break;
	    	}
	    
	    	//Does this actually help with memory allocation?
	    	unset($tag);
		}
		
		if (! empty($this->compMetaData['keyframes']['times']))
			$this->compMetaData['lastkeyframetimestamp'] = $this->compMetaData['keyframes']['times'][ count($this->compMetaData['keyframes']['times'])-1 ];
	
		$this->compMetaData['duration'] = $this->compMetaData['lasttimestamp'];
		
		return $this->compMetaData;
	}
	
	function setMetaData( $metadata, $origMetaOfs = 0, $origMetaSize = 0 )
	{
		$this->compMetaData = $metadata;
		$this->origMetaOfs = $origMetaOfs;
		$this->origMetaSize = $origMetaSize;
	}
	
	function getMetaData()
	{
		if (! is_array($this->origMetaData))
			return $this->compMetaData;
		else
			return array_merge( $this->origMetaData, $this->compMetaData );
	}
	
	
	function play( $from = 0 )
	{
		fseek($this->fp, 0);
		
		// get original file header just in case it has any special flag
		echo fread($this->fp, $this->bodyOfs + 4);
		
		// output the metadata if available
		$meta = $this->getMetaData();
		if (! empty($meta))
		{
			//serialize the metadata as an AMF stream
			include_once 'FLV/Util/AMFSerialize.php';
			$amf = new FLV_Util_AMFSerialize();

			$serMeta = $amf->serialize('onMetaData');
			$serMeta.= $amf->serialize($meta);

			//Data tag mark
			$out = pack('C', FLV_Tag::TYPE_DATA);
			//Size of the data tag (BUG: limited to 64Kb)
			$out.= pack('Cn', 0, strlen($serMeta));
			//Timestamp
			$out.= pack('N', 0);
			//StreamID
			$out.= pack('Cn', 0, 0);
			
			echo $out;
			echo $serMeta;
			
			// PrevTagSize for the metadata
			echo pack('N', strlen($serMeta) + strlen($out) );
		}
		
		$chunkSize = 4096;
		$skippedOrigMeta = empty($this->origMetaSize);
		while (! feof($this->fp))
		{
			// if the original metadata is pressent and not yet skipped...
			if (! $skippedOrigMeta)
			{				
				$pos = ftell($this->fp);
			
				// check if we are going to output it in this loop step
				if ( $pos <= $this->origMetaOfs &&
					 $pos + $chunkSize > $this->origMetaOfs )
				{
					// output the bytes just before the original metadata tag
					if ($this->origMetaOfs - $pos > 0)
						echo fread($this->fp, $this->origMetaOfs - $pos);
					
					// position the file pointer just after the metadata tag
					fseek($this->fp, $this->origMetaOfs + $this->origMetaSize);
					
					$skippedOrigMeta = true;
					continue;
				}
			}
			
			echo fread($this->fp, $chunkSize);
		}
	}
}



$flv = new MyFLV();
try {
	$flv->open( 'test1.flv' );
} catch (Exception $e) {
	die("<pre>The following exception was detected while trying to open a FLV file:\n" . $e->getMessage() . "</pre>");
}

//Here we should cache the result and use ->setMetaData() instead
$start = microtime(true);
$flv->computeMetaData();
$end = microtime(true);
//echo "<hr/>EXTRACT METADATA PROCESS TOOK " . number_format(($end-$start), 2) . " seconds<br/>";


//echo "<pre>" . print_r($flv->getMetaData(), true) . "</pre>";
header('Content-type: flv-application/octet-stream');
header('Content-Disposition: attachment; filename="out.flv"');
$flv->play(0);


$flv->close();	



?>