www.mediacollege.com | Support Forum
MC Media Player

Actarus flv injector test: Improving and expanding the flv4php library (Version beta2)

We got a php alternative for video tag's injection: flv4php. The weakness of this project is the integration and you will have to guess from the source code how to really use it. The provided getID3 files joined with it, is clearly outdated so you will have to overrides some files with the newest getID3. However, be sure not to remplace all the files, only the audio-video part else flv4php will no work anymore.

History / Updates

27.01.2011: I discovered yamdi and of course added to my hall of fame for FLV injectors. Without any doubts, this is the coolest FLV injector of the world: two reasons, it has a better tag injection as we will see and it's opensource. Well done Ingo Oppermann! I have however two requests:

  • the custom injection directly from xml file.. Why? for completing missing tags and setting own record signatures like G00gle does (flvmdi is the only tool that open the door thanks to xtradata).No more the one, a patch allows this! Sorry ladies and gentlemen, it ignores the tags too :(..
  • ID3 tags support for mp3 audio directly in flv. Why will be like a jump to sky to port this feature using the ID3 support from getID3...Of course at the end, it's the Heaven if all is inegrated in getID3!

29.01.2011: I have registered Dave's player to osflash.org. Oh my God, there is a php injector flv4php! (will benchmark it). Guess What? its code is no more provided but I took Goldorak and fetched it. Holy shit, you have to register for an opensource file? how that can it be? No Way anymore for you as I mirrored all this here. Now what's up? I have looked at the flv4php code and the winner is...getID3! Yes, you read well, it's a php addon built on getID3!!! My (d/st)reams are coming true?

31.01.2011: flv4php roxx, it opens the door to the custom tag injection at least!!!!! Default tag detection has to be improved but it works :). I have also fixed the mp3 player, there is a limitation when reading ID3 tags: you will have to wait the file to be completely downloaded.

04.02.2011: Now, it's time for tagging tools to pass their exams. So if you are lost in the flv4php,yamdi,flvtool2,flvmdi maze, do not worry there is an exit!.

05.02.2011: Cuepoints are cool points for videos ;), I have added this as a bonus. This is not finished.

06.02.2011: Busy day with cuepoints but now, we got a least an auto cuepoint generator on the php side, downloadable and free of use (shipped with flvinjector 2.1)! Big discovery: flv1 is h263 encapsulated with the flv header, so when you encode with ffmpeg, don't use -vcodec h263 (or h263+) but -vcodec flv else you will got tagging problems with flvtool2 that will not recognize the flv (header) and generate an error!.

07.02.2011: Since my yesterday discovery: my advice for flv1 generation (if you need to inject tags behind with flvtool2) is to avoid avidemux that does not generate correct sorenson spark (codec is good but the flv header is missing!). Use ffmpeg instead but I warn you, the id3 tag injection is broken (I lost my afternoon trying to inject id3tags ^^').

09.02.2011: Please stay stuned because I am forking flv4php ^^ and it's core is not a piece of cake at all. Of course, it will use flvinfo core for a better tag injection/detection in the metadata and integrate flvcuepoints. I played a bit with RichFLV which opens the cut feature of flvtool2 to the H264 world :). The bad news for videos keyframes declared in the metadata, is that no tool agree each other on their values, whose calculation is right, I am really wondering now. I've finally succeeded to inject ID3 tags into H263 with FLVTool2 since yesterday, I will explain all this as soon as possible.

11.02.2011: I validated at least the times values by external means, don't miss it, it worth a banana not to use the bad liana. For this, I have built a tool to manipulate timecodes and keyframes :flvtimecodes

12.02.2011: MAJOR RELEASE of my flv tag injector (now 3.0) with some big surprises inside, run as fast as you can only one second left to get it (I am joking) in download of course.

13.02.2011: flvinjector-3.1 is out, now id3 tags can be injected to h264 flv as well, follow the interactive demo, it's fun!

14.02.2011: Woaw an interactive gui to tag flv (id3 only for now)! Where? in flvinjector-3.2, update your bits as you will get dynamically ffmpeg batchs!

16.02.2011: Two days means two new packages, doesn't it? Yes, I released the new flvinfo-3.0 that ends truely the fight with the h263/flv detection and improves riff support so you can know now if the video was produced by virtualdub =). The new flvinjector-4.0 solves xml bugs and makes cuepoints injection possible!

17.02.2011: flvinjector-4.1 solves xml bugs (again!) and adds a new architecture with a concept of plugins/extensions. cuepoints injection from .motn and .ass working!

18.02.2011: flvinjector-4.1 repacked to match version.

19.02.2011: I really hesitated vith the version number as flvinjector-4.2 brings major meta/cue/id3 tagging into reality with a nice html entry joined! The bad duck is keyframes generation...

28.02.2011: FFprobe confirms tbe timecodes values and frame number with a killing graph as bonus.

Tags

Tags allow to store extra informations of the file. For example, jpeg extra informations (author, size...) are saved in the exif header.This is very usefull when you got a lot of pictures and want to set copyrights and establish categories. For flash videos with the flv header, as we will see below in the table, all technical video settings is encoded in an info table.Note that getID3 provides an extra table with extended infos called infoset not shown here but in the example of  flvinfo-3.0.

Injection

Let's take the test.flv file provided in the fanno.dk's tutorial and analyse it with flvinfo to extract its video info table as a html table.It showed clearly that duration tag has a problem with its negative value and lasttimestamp has the same value.
video
fileprops
(thumb
for this
virtual tag)
durationvideo
data
rate
lastkey
frame
time
stamp
lastkey
frame
location

creator
metadata

creator
has
key
frames
has
metadata
key
frames
widthheightmetadata

date
data
size
audio
codec
id
audio
data
rate
audio

size
video

size
file
size
last
time
stamp
audio
delay
encodercreation
date
xtra
data
albumartistalbum_artistcomposerdiscTYERgenretitletrack
flv FLV1 with FLV4PHP-12.179287.0546717305327FLV Editor for PHP V0.21 (Project: Flv4php)FLV Editor for PHP V0.21 (Project: Flv4php)11Array(1)320240129653961300057100.000156799157379-12.179             

Let's decide to fix this with our hacked injector. The first thing to do is to clear tags from the file because if you do so, flv4php will read this time correctly the duration value!. I have done this using yamdi :
	yamdi -i test.flv -M -o test_clean.flv 
giving this info table (Oh my God, YAMDI has ejected Goldorak's tags from its video spaceship! Hey there are still tags remaing there Mr YAMDI) Sorry, it was on my side, fixed in flvinfo 1.7 ^^'.
video
fileprops
(thumb
for this
virtual tag)
durationvideo
data
rate
lastkey
frame
time
stamp
lastkey
frame
location

creator
metadata

creator
has
key
frames
has
metadata
key
frames
widthheightmetadata

date
data
size
audio
codec
id
audio
data
rate
audio

size
video

size
file
size
last
time
stamp
audio
delay
encodercreation
date
xtra
data
albumartistalbum_artistcomposerdiscTYERgenretitletrack
flv FLV1                                

and then reinject the tags with the fixed value thanks to our hacked injector:
	
    /**
	* fix_metadata.php, a php flv injector fixer helper by Actarus
	*
	* requires flv4php with an updated getId3!
	* V1.0 (04.02.2011)  
	**/

	$path = '<your_video_dir>';  // Path to flv files (could be behind web root)

	$demo=false;

	if( !isset($_GET["file"]) ) {
		$_GET["file"]="test_clean.flv"; //die(header("HTTP/1.0 404 Not Found"));goldorak_h264
		$demo=true;
	}


	$file = $path . htmlspecialchars($_GET["file"]);

	if( !isset($_GET["type"]) ) $_GET["type"]='inject';

			
	require_once('lib/flv4php_php4/FLV.php'); // Path to flv.php / (flv4php)
	

	if ( file_exists($file) ) {
	
			$flv = new FLV($file);
			
			//read and decodes meta tags from the flv
			$meta=$flv->getMetaData();
			$newMetaData=$meta->data;
			
			//now inject your custom tags here
			//Fix negative duration and lastimestamp..(getMetaData() is buggy when this tag is already defined!)
			$newMetaData["duration"]=5.005; //This is the "playtime_seconds" value read with flv4php from test_cleaned.flv generated with yamdi -i test.flv -M -o test_cleaned.flv
			$newMetaData["lasttimestamp"]=$newMetaData["duration"];
			$newMetaData["creator"]= makeblink("Goldorak's injector!");
            $newMetaData["metadatacreator"]="FLV Editor for PHP V0.21a (Project: Flv4php)";
			//the surprise..the famous tags are not read by getMetaData, seems we have to handle here..
			$newMetaData["haskeyframes"]=(array_key_exists("keyframes",$newMetaData)&&(is_array($newMetaData["keyframes"])&&(count($newMetaData["keyframes"])>1))) ? '1' : '0';
			$newMetaData["hasmetadata"]='1'; //silly tag imho, because dude, you are one!
			$newMetaData["hasKeyframes"]=$newMetaData["haskeyframes"];
			$newMetaData["hasMetadata"]=$newMetaData["hasmetadata"];

			if($_GET["type"]=="preview") {
			  
				echo "<pre>";
				print_r($newMetaData);
				echo "</pre>";
			
			}else {
			
				//encodes MetaData into amf structure
				$metadata = $flv->createMetaData($newMetaData);
				
				//generates the flv file with the new injected metadata
				//!TODO: let the choice to write directly to the disk..
				$flv->playFlv(1, 0,$newMetaData,$merge = false);
			 }
		
	} else {
	
			die(header("HTTP/1.0 404 Not Found"));
	
	}

If you want to inject default tags instead, here is the injector code:
	
    /**
	* inject_metadata.php, a php flv injector helper by Actarus
	*
	* requires flv4php with an updated getId3!
	* V1.0 (30.01.2011)  
	**/


	$path = '<your_video_dir>';  // Path to flv files (could be behind web root)

	$_GET["file"]="goldorak_h264.flv"; //Comment this line, equivalent to  inject_metadata.php?file=goldorak_h264.flv
	
	if( !isset($_GET["file"]) ) die(header("HTTP/1.0 404 Not Found"));
	
	$file = $path . htmlspecialchars($_GET["file"]);

	// 'type=inject' is by default, it will inject the tags and you will be able to download the updated flv file. Use 'type=preview' in the url to dump.
	if( !isset($_GET["type"]) ) $_GET["type"]='inject';

			
	require_once('lib/flv4php_php4/FLV.php'); // Path to flv.php / (flv4php)
	
	if ( file_exists($file) ) {
	
			$flv = new FLV($file);
			
			//read and decodes default meta tags
			$newMetaData = $flv->defaultMetaData(true);
			
			//now inject your custom tags here
			//!TODO:  read this from the xml file
			
			//Please use your wished values, this is a dummy sample
			$newMetaData['hasKeyframes']=makeblink('+');	
			$newMetaData['hasVideo']=makeblink('1');		
			$newMetaData['hasAudio']=makeblink('1');			
			$newMetaData['hasMetadata']=makeblink('1');		
			$newMetaData['canSeekToEnd']=makeblink('+');
			$newMetaData['datasize']=makeblink('+');	
			$newMetaData['videosize']=makeblink('+');	
			$newMetaData['audiosize']=makeblink('+');	
			$newMetaData['lasttimestamp']=makeblink('+');		
			$newMetaData['lastkeyframetimestamp']=makeblink('+');
			$newMetaData['lastkeyframelocation']=makeblink('+');
			$newMetaData['keyframes']=makeblink('+');	
			$newMetaData['audiodelay']=makeblink('+');
			$newMetaData['creationdate']=makeblink('+');	
			$newMetaData['xtradata']=makeblink('+');	
			$newMetaData['album']=makeblink('+');
			$newMetaData['artist']=makeblink('+');
			$newMetaData['album_artist']=makeblink('+');		
			$newMetaData['composer']=makeblink('+');	
			$newMetaData['disc']=makeblink('+');	
			$newMetaData['TYER']=makeblink('+');	
			$newMetaData['genre']=makeblink('+');	
			$newMetaData['title']=makeblink('+');	
			$newMetaData['track']=makeblink('+');	
			$newMetaData['creator']=makeblink("Goldorak's injector!");
			
			if($_GET["type"]=="preview") {
			  
				echo "<pre>";
				print_r($newMetaData);
				echo "</pre>";
			
			}else {
			
				//encodes MetaData into amf structure
				$metadata = $flv->createMetaData($newMetaData);
				
				//generates the flv file with the new injected metadata
				//!TODO:  let the choice to write directly to the disk..
				$flv->playFlv(1, 0,$newMetaData,$merge = true);
			 }
		
	} else {
	
			die(header("HTTP/1.0 404 Not Found"));
	
	}
?>

An the goldorak's part? Sure, here it is:
 /** The two functions here are the core of the tag injection demo of course **/

	function rainbow($text,$factor=0.4) {
	//---------------------------------
		$colors=array("FFFF00","66FF99","33FF33","66FFFF","33CCFF","3366FF","CC33CC","993366","990000","CC0000","FF0000");
	
		$b=4;
		$c=0;
		$t=0;
		$tMax=strlen(utf8_decode($text));
		$cMax=count($colors);
		$rText=array();
		while(($c<$cMax)&&($t<$tMax)) {
			$rText[$t]='<font color="#'.$colors[floor($c*$factor)+$b].'">'.$text[$t].'</font>';
			$c++;
			$t++;
			if(($b+$c)>$cMax) $c=0;
		}
		$text=implode('',$rText);
		return $text;
		
	}
	
	
	function makeblink($code) {
        //-------------------------
		$size=strlen($code);
		if($size>1) {
			$size=7;
			$code=rainbow($code);
		}
		return '<font color="blue"><blink><marquee style="width:'.$size.'em;">'.$code.'</marquee></blink></font>';
	}
Let's read the fixed flv:
video
fileprops
(thumb
for this
virtual tag)
durationvideo
data
rate
lastkey
frame
time
stamp
lastkey
frame
location

creator
metadata

creator
has
key
frames
has
metadata
key
frames
widthheightmetadata

date
data
size
audio
codec
id
audio
data
rate
audio

size
video

size
file
size
last
time
stamp
audio
delay
encodercreation
date
xtra
data
albumartistalbum_artistcomposerdiscTYERgenretitletrack
flv FLV1 with FLV4PHP-12.179287.0546717305327FLV Editor for PHP V0.21 (Project: Flv4php)FLV Editor for PHP V0.21 (Project: Flv4php)11Array(1)320240129653961300057100.000156799157379-12.179             
flv FLV1                                
flv FLV1 with FLV4PHP5.005287.0546717305327Goldorak's injector!FLV Editor for PHP V0.21a (Project: Flv4php)Array(1)3202402011-02-01T06:53:33.-392Z57100.0001567991573795.005             



Key frames support

Let's check this by having a look to the keyframes generated by all the stuff we got under the hand. According what I have said before on codec compatibility, we can do this only on H263 based videos files. This is good as test.flv is one :)

Keyframe injection summary table
flv4php
(inject_metadata.php)
yamdi
(yamdi -i test_clean.flv -o test_inject.flv -x test_yamdi.xml)
RichFLV
Load of test_clean.flv, then manual dump of Keyframes's values from the GUI (no meta xml export nor cmd line!)
RichFLV_keyframe
flvtool2
copy test_clean.flv test_clean.bak
flvtool2 -U test_clean.flv
move test_clean.flv test_inject_flv2.flv
move test_clean.bak test_clean.flv
flvtool2 -Px test_inject_flv2.flv > test_flv2.xml
flvmdi
flvmdi test_clean.flv test_inject_flvmdi.flv /x /k /l
move test_inject_flvmdi.flv test_flvmdi.xml
	 Array
(
    [times] => Array
        (
            [0] => 0
        )

    [filepositions] => Array
        (
            [0] => 580
        )

)
	 
		Array
(
    [times] => Array
        (
            [0] => 0.00
            [1] => 1.44
            [2] => 1.47
            [3] => 1.53
            [4] => 1.60
            [5] => 2.80
            [6] => 4.04
            [7] => 4.64
            [8] => 4.67
        )

    [filepositions] => Array
        (
            [0] => 13
            [1] => 43932
            [2] => 46480
            [3] => 49068
            [4] => 53291
            [5] => 86271
            [6] => 121634
            [7] => 142109
            [8] => 144685
        )

)
		
		 Array
(
    [times] => Array
        (
            [0] => 0
            [1] => 1.401
            [2] => 1.435
            [3] => 1.468
            [4] => 1.568
            [5] => 2.769
            [6] => 4.004
            [7] => 4.604
            [8] => 4.638
        )

    [filepositions] => Array
        (
            [0] => 9
            [1] => 43928
            [2] => 46476
            [3] => 49064
            [4] => 53287
            [5] => 86267
            [6] => 121630
            [7] => 142105
            [8] => 144681
        )

)
		
		 Array
(
    [times] => Array
        (
            [0] => 0
            [1] => 1.435
            [2] => 1.468
            [3] => 1.535
            [4] => 1.601
            [5] => 2.803
            [6] => 4.037
            [7] => 4.638
            [8] => 4.671
        )

    [filepositions] => Array
        (
            [0] => 840
            [1] => 44759
            [2] => 47307
            [3] => 49895
            [4] => 54118
            [5] => 87098
            [6] => 122499
            [7] => 142974
            [8] => 145550
        )

)
		
		 Array
(
    [times] => Array
        (
            [0] => 0
            [1] => 1,435
            [2] => 1,468
            [3] => 1,535
            [4] => 1,601
            [5] => 2,803
            [6] => 4,037
            [7] => 4,638
            [8] => 4,671
        )

    [filepositions] => Array
        (
            [0] => 656
            [1] => 44575
            [2] => 47123
            [3] => 49711
            [4] => 53934
            [5] => 86914
            [6] => 122315
            [7] => 142790
            [8] => 145366
        )

)
		

Analysis
None of the tag tools have the same frames values, seems to be a lottery at the first glance but it is not the case if you take into account starting offsets.
Indeed if you shift all filepositions value by starting_offset wich equals filepositions[0], you get:
filepositions inspection
flv4php yamdi RichFLV flvtool2 flvmdi
			Not enough keyframes	 
		Array
(
    [filepositions] => Array
        (
            [0] => 0
            [1] => 43919
            [2] => 46467
            [3] => 49055
            [4] => 53278
            [5] => 86258
            [6] => 121621
            [7] => 142096
            [8] => 144672
        )

    [starting offset] => 13
)
		
		 Array
(
    [filepositions] => Array
        (
            [0] => 0
            [1] => 43919
            [2] => 46467
            [3] => 49055
            [4] => 53278
            [5] => 86258
            [6] => 121621
            [7] => 142096
            [8] => 144672
        )

    [starting offset] => 9
)
		
		 Array
(
    [filepositions] => Array
        (
            [0] => 0
            [1] => 43919
            [2] => 46467
            [3] => 49055
            [4] => 53278
            [5] => 86258
            [6] => 121659
            [7] => 142134
            [8] => 144710
        )

    [starting offset] => 840
)
		
		 Array
(
    [filepositions] => Array
        (
            [0] => 0
            [1] => 43919
            [2] => 46467
            [3] => 49055
            [4] => 53278
            [5] => 86258
            [6] => 121659
            [7] => 142134
            [8] => 144710
        )

    [starting offset] => 656
)
		
That means, if you ommit the starting offset, flvtool2 and flvmdi agrees together, whereas Yamdi is RichFlv's friend. Is this the ying and yang feature?
All could be marvellous if since fileposition[6], a grain of salt had not entered somewhere into calculations and we got the same starting offset. Rounding cause?



Comments
  • FLV4PHP: Ohhhh noooooooooo...what happened to my dearest flv4php? Only one? poor lonely frame.We notice that some injection did not succeed (has...tags has been ignored). It seems it can only update not create new custom tags from scratch and the keyframes support is broken :(.
  • YAMDI: Times seems to be rounded at 2 digits
  • RichFLV: Times seems to be shifted by one if you compare with others...
  • FLVTOOL2: Be sure to backup your flv source first else it will be updated! Times's values follows the FLVMDI's values.
  • FLVMDI: its xml format is a nightmare to compare with others: you will have to rename root node as fileset, then inject missing flv node with the file name attribute and at least, rename time (or pos) tag to value. its xml format really sucks.UPDATE IT!

Conclusion
For H263 you can use flvtool2,yamdi,flvmdi to generate keyframes. For H264 only Yamdi succeed to inject keyframes.However this is very surprizing than noone agrees with the keyframes values (either times or filepositions differ)! I don't even know how to calculate them, please if you know, tell me or better patch my code :) . RichFLV is the one that provides a GUI to jump to a particular keyframe but do not provides any keyframe injection capability only set timestamps (lastkeyframe and lastime), durations and metadatacreator to RichFLV if you save your flv after.

Validation (timecodes only)
As I was fed up with all these incoherencies, I looked for a way to validate this part, the good new is I found a way to validate the timecodes but the bad news is not for filepositions (as I did NOT succeed to compile ffmpegsource2, having no source of the original ffmpegsource 1.3). You will be suprised as well by the way I have done this. To start, I have used virtualdub with plugins for flv that worked only for h263/vp6. At this time, we got only two and they provides indirectly timecodes because you will have to manualy jump between each keyframe and write it down. Fortunately, there were only 9 keyframes in test_clean.flv so it wasn't so painfull. Here are the xml results following the scheme of an extended format of the metadata's timescodes I created for the occasion: I have introduced version and id and type attributes for the time element and name for the value element. This last attribute's value matches the frame number of the key.
flv  test_clean_flv.vdplugin_timecodes.xmlFLVInputDriver  test_clean_FLVInputDriver.vdplugin_timecodes.xml
<!-- Manually extracted with flv.vdplugin 1.3 (FccHandler)
http://fcchandler.home.comcast.net/~fcchandler/Plugins/FLV/index.html
Version 1.3 (December 24, 2010): 
24.975fps , Datarate=245kbps - 126 frames last is 5045
-->
<times id="flv.vdplugin" type="keyframes" version="1.3">
	<value id="0" name="0">0</value>
	<value id="1" name="36">1.441</value>
	<value id="2" name="37">1.481</value>
	<value id="3" name="38">1.522</value>
	<value id="4" name="40">1.602</value>
	<value id="5" name="70">2.803</value>
	<value id="6" name="101">4.044</value>
	<value id="7" name="116">4.645</value>
	<value id="8" name="117">4.685</value>
</times>
<!-- Manually with FLVInputDriver.vdplugin (http://moitah.net/)
1.1.0 (2008-May-07): H.263 / VP6 + MP3 / PCM .
File:  test_clean.flv - 151 frames last is 5038
Estimated True Frame Rate:  29,940119760479 (5000/167)
Average Frame Rate:  24,975024975025 (25000/1001)
-->
<times id="FLVInputDriver.vdplugin" type="keyframes" version="1.1.0">
	<value id="0" name="0">0</value>
	<value id="1" name="43">1.435</value>
	<value id="2" name="44">1.468</value>
	<value id="3" name="46">1.535</value>
	<value id="4" name="48">1.602</value>
	<value id="5" name="84">2.803</value>
	<value id="6" name="121">4.037</value>
	<value id="7" name="139">4.638</value>
	<value id="8" name="140">4.671</value>
</times>
You may say now WTF as nothing matches too ^^'. Indeed, if we look at FccHandler's plugin some values differs from previous results and if we look at moitah's plugins, timecodes seems to be fine but we are now incertain about frame numbers! Don't be desesperate, there are two judges to help us: ffmpegsource with is a avisynth's plugin and...aegisub! Indeeed, if you create an avisynth script  test_clean_ffmpegsource.avs to open the video under virtualdub, it will create a cache file that has all inside and if you use aegisub and load your video inside (that works with h264 too!) you can export keyframes and timecodes as text and mux them with my  flvtimecodes-1.0 tool and you got the good results. Lets' dump this:
ffmpegsource  test_clean_ffmpegsource_frames.txtaegisub  test_clean.ass
<!-- Generated from test_clean_ffmpegsource_frames.txt, 9 frame(s) found-->
<times id="ffmpegsource" type="keyframes" version="1.13">
	<value id="0" name="0">0</value>
	<value id="1" name="36">1.435</value>
	<value id="2" name="37">1.468</value>
	<value id="3" name="38">1.535</value>
	<value id="4" name="40">1.601</value>
	<value id="5" name="70">2.803</value>
	<value id="6" name="101">4.037</value>
	<value id="7" name="116">4.638</value>
	<value id="8" name="117">4.671</value>
</times>
<!-- Generated from test_clean_aegisub_frames.txt, 9 frame(s) found-->
<times id="aegisub" type="keyframes" version="2.1.8">
	<value id="0" name="0">0</value>
	<value id="1" name="36">1.435</value>
	<value id="2" name="37">1.468</value>
	<value id="3" name="38">1.535</value>
	<value id="4" name="40">1.601</value>
	<value id="5" name="70">2.803</value>
	<value id="6" name="101">4.037</value>
	<value id="7" name="116">4.638</value>
	<value id="8" name="117">4.671</value>
</times>
php4ffprobe  test_clean_ffprobe_timecodes.phps
			<!-- Manually converted from php array generated 
with test_clean_ffprobe_timecodes.phps -->
<times id="php4ffprobe" type="keyframes" version="4.0">
	<value id="0" name="0">0</value>
	<value id="1" name="36">1.435</value>
	<value id="2" name="37">1.468</value>
	<value id="3" name="38">1.535</value>
	<value id="4" name="40">1.601</value>
	<value id="5" name="70">2.803</value>
	<value id="6" name="101">4.037</value>
	<value id="7" name="116">4.638</value>
	<value id="8" name="117">4.671</value>
</times>
			

Final conclusion
If you have to use virtualdub to manipulate flv1/h263 files, better use ffmpegsource (not ffmepgsource2 bringing ffms2.dll that really sucks, got errors when loading avs and compiling due to broken postprocessing) through an avisynth script because both flv plugins are buggy somehow (the winner equation is fccHandler's frame numbers+Moitah's timecodes=ffmpegsource's frame numbers/timecodes). Yamdi need to fix its timecodes values to be great, richFLV has incoherent timecodes but as it does not inject keyframes metatags, I don't mind. Unless I find a way to validate filepositions, better use flvtool2 or flvmdi to inject keyframes for h263 and Yamdi for h264 or my injector when keyframes will be handled ;).


Coherency check

To do this, let's compare all the video tagged files (I have removed most of the tags not related to video or to frame)

metainfosetinfoset->flv->headerinfoset->flv->metainfoset->flv->video
fileprops
(thumb
for this
virtual tag)
metadata

creator
has
Key
frames
has
Video
has
Audio
has
Meta
data
can
Seek
To
End
durationdata
size
video

size
frame
rate
video
data
rate
video
codec
id
file
size
last
time
stamp
lastkey
frame
time
stamp
lastkey
frame
location
key
frames
metadata

date

creator
cuePointshas
key
frames
has
CuePoints
file
size
avdataoffsetavdataendvideo
play
time_seconds
bit
rate
play
time_string
signatureversionhas
Audio
has
Video
video
SizeType
onMetaDatavideo
Codec
flv FLV1 with FLV4PHPFLV Editor for PHP V0.21a (Project: Flv4php)     5.005571156799 287.05 1573795.00546717305327Array(1)2011-02-01T06:53:33.-392ZGoldorak's injector!  1580330158033Array(7)5.005206040.417209910:051111111
flv FLV1 with YAMDIYet Another Metadata Injector for FLV - Version 1.7a1115.00515680315629925.174825174825241.8121574685.0054.671145337Array(9)     1574680157468Array(8)5.005205303.78096480:051111111
flv FLV1 with FLVMDIManitu Group FLV MetaData Injector 2    5.00568115680325.20.0021574975.0054.671 Array(9)1296844221499    1574970157497Array(8)5.005205341.590612780:051111111
flv FLV1 with FLVTOOL2inlet media FLVTool2 v1.0.6 - http://www.inlet-media.de/flvtool21115.03815715615629930247.6121576815.0054.671 Array(9)1296829221125 Array(0) 1576810157681Array(8)5.038205581.48631030:051111111


Analysis
Flvtool2 seems to be the best alternative to tag h263 videos mainly because it positionnates correctly the "has flag" tags, inject the keyframes and it is the only tool that has also the cuePoint feature (I dumped the array here, its empty so I don't know what to think about it for now).
  • FLV4PHP:framerate is lost..it's the only tool that fill correctly the audiocodecid tag.
  • YAMDI: metadatadate is mssing and this tool seems to have no audio tag support, maybe for future release?
  • FLVTOOL2: I have detected a incoherency in Flvtool2 tag generation: the 'audiocodecid' tag produces a warning in GetID3 because the value given is an empty array instead of an integer!
  • FLVMDI: this tool forget to set the has flags...and set videodatarate incorrectly. The tool version number is missing in the metadatacreator this is a bad stategy imho. I have used the 2.96 here.
None of these tool set SeekToEnd or videocodecid tags but the interesting point is none agrees with the framerate value, this is surely the explanation why keyframes values differs...

Cuepoints (Bonus)

Cuepoints are strategic so I can't avoid talking about them. By putting markers in the video file (step two) from a marker config file (step one), you provide the cut facility behind(step three). Of course, for a paranoid like me, an extra step, the check (step four) is a must.Thus cuepoints are useful when you want to produce some video footage or extract some important scenes for a demo.
Step one The first step is to generate the cuepoint config file, for this I took a look at osflash flvtool2 scripts. None were for php, so I have ported some of them here taking the only one that provides operability: the xsl file. The idea was to generate then the .motn from sratch, by example with a php array so we could apply the xsl template on it. I tried my best to guess the parameters and it resulted a nice library I included with  flvinjector-2.1 and this sample code:

<?php 

    
//======sample 2 in flvtool2 source (more interesting as it shows the overwrite process)
    //include my cuepoint generator (included in flvinjector >= 2.0)
    
require_once('lib/flvcuepoint.php');

    
//this function is stored in "flvcuepoint_sample2.php";
    
function build_ozml_flvtool2_sources($filename) {
    
//------------------------------------------
        
$o=getOzml($filename);
        
        
$label="TestEvent1";
        
$inpoint=8.325;
        
$speaker="Peter";
        
$says="Hello my name is Peter.";
        
addEventTimeMarker($o,$label,$inpoint,$speaker,$says);
        
        
$label="TestEvent2";
        
$inpoint=25;
        
$index=1;
        
$title="Chapter 1";
        
addNavigationTimeMarker($o,$label,$inpoint,$index,$title) ;
        
        
$label="TestEvent3";
        
$inpoint=25//same inpoint as previous, this will override "TestEvent2"
        
$index=2;
        
$title="Chapter 2";
        
addNavigationTimeMarker($o,$label,$inpoint,$index,$title) ;
        
        
//magic call..
        
return xml_ident(mc_($o,'','')); // xml_ident(
    
    
}
    
    
//configure output files
    
$ozmlfile="motn/cuepoint2.xml"//generated from php, input
    
$flv2file="cuepoints/cuepoint2.xml";//converted from php, output
    
    //Generate the motn/ozml file 
    
$ozml=build_ozml_flvtool2_sources($ozmlfile);
    
    
//show it
    
echo("<pre>");
    echo(
xml_highlight($flvml));
    echo(
"</pre>");
    
    
//convert to flvtool2 xml format and save it to disk
    
$flvml=convert_cuepoint_xml($xml_dir.$ozmlfile);
    
file_put_contents($xml_dir.$flv2file,$flvml);
    
?>
Source file, .motn file  cuepoint2.xml generated from the php above flv2tool cuepoint file  cuepoint2.xml
<ozml version="2.0">
	<factory id="Actarus" uuid="31a262d6247f3513b19d1149102e116d">
		<description>Cuepoint file for motn/cuepoint2.xml</description>
		<manufacturer>php cuepoint generator for flvtool2</manufacturer>
		<version>1.MC</version>
	</factory>
	<scene name="flv cuepoint demo" id="goldorak" factoryID="Actarus" version="1.0">
		<timemarkerset>
			<timemarker>
				<label>TestEvent1</label>
				<inpoint>8.325</inpoint>
				<parameter name="speaker" value="Peter"/>
				<parameter name="says" value="Hello my name is Peter."/>
			</timemarker>
			<timemarker>
				<label>TestEvent2</label>
				<inpoint>25</inpoint>
				<parameter name="index" value="1"/>
				<parameter name="title" value="Chapter 1"/>
			</timemarker>
			<timemarker>
				<label>TestEvent3</label>
				<inpoint>25</inpoint>
				<parameter name="index" value="2"/>
				<parameter name="title" value="Chapter 2"/>
			</timemarker>
		</timemarkerset>
	</scene>
</ozml> 
				
==PHP/XSLT Bridge==>
<?xml version="1.0" encoding="utf-8"?>
<!--source="download/flvinjector/motn/cuepoint2.xml"-->
	<tags>
		<metatag event="onCuePoint" overwrite="true">
			<name>TestEvent1</name>
			<timestamp>333</timestamp>
			<parameters>
				<speaker>Peter</speaker>
				<says>Hello my name is Peter.</says>
			</parameters>
			<type>event</type>
		</metatag>
		<metatag event="onCuePoint" overwrite="true">
			<name>TestEvent2</name>
			<timestamp>1000</timestamp>
			<parameters>
				<index>1</index>
				<title>Chapter 1</title>
			</parameters>
			<type>navigation</type>
		</metatag>
		<metatag event="onCuePoint" overwrite="true">
			<name>TestEvent3</name>
			<timestamp>1000</timestamp>
			<parameters>
				<index>2</index>
				<title>Chapter 2</title>
			</parameters>
			<type>navigation</type>
		</metatag>
	</tags> 
				
where XSLT used for the bridge is a goldorak's improvement of the osflash script  cuemotn.xsl
				<?xml version="1.0" encoding="utf-8"?>
<!-- Here's another xslt-script which generates FLVTool2 cuepoints from Motion project-files (*.motn), -->
<!-- tested with Apple Motion 2.0.1. Use the generated XML like flvtool2 -AUPktv mymeta.cue.xml test.flv testout.flv. -->
<!-- patch update by Actarus from sample http://osflash.org/flvtool2-scripts on 06.02.2011-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8"  omit-xml-declaration="no" indent="yes"/>
<xsl:preserve-space elements="*"/>
<xsl:param name="name">motn/cuepoint2.xml</xsl:param>
<xsl:param name="fps">25</xsl:param>

<xsl:template match="/ozml">
<xsl:comment>source="<xsl:copy-of select="$name"/>"</xsl:comment>
<tags>
	<xsl:for-each select="./scene/timemarkerset/timemarker">
		<metatag event="onCuePoint" overwrite="true">
			<name><xsl:value-of select="./label/text()" /></name>
			<timestamp><xsl:value-of select="round(./inpoint/text() div $fps * 1000)" /></timestamp>
			<xsl:if test="./parameter">
				<parameters>
				<xsl:for-each select="./parameter">
					<xsl:variable name="name" select="@name"/>
					<xsl:element name="{$name}">
						<xsl:value-of select="@value"/>
					</xsl:element>
				</xsl:for-each>
				</parameters>
			</xsl:if>
			<xsl:choose>
				<xsl:when test="./parameter[@name='index']">
					<type>navigation</type>
				</xsl:when>
				<xsl:otherwise>
					<type>event</type>
				</xsl:otherwise>
			</xsl:choose>
		</metatag>
  	 </xsl:for-each>
</tags>
</xsl:template>
</xsl:stylesheet> 
				
Step two We will inject the tags into the flv using flvtool2 and the previous config file as follow:
flvtool2 -AUt cuepoint2.xml test_inject_flv2.flv test_cueinject_flv2.flv
flvtool2 -Px test_cueinject_flv2.flv > test_flv2cue2.xml 
Step three It's clearly the time to play, let's cut some part of the video between two cuepoints.Cuts file after 2 seconds and writes it back to input-source(from osflash's flvtool2 usage examples - not yet tested)
flvtool2 -CU -o 2000 example.flv 
Step four
to finish, we make some checks behind.
Generated and read from cuepoint2.xml read from getID3
		 Array
(
    [0] => Array
        (
            [name] => TestEvent1
            [time] => 0
            [parameters] => Array
                (
                    [speaker] => Peter
                    [says] => Hello my name is Peter.
                )

            [type] => event
        )

    [1] => Array
        (
            [name] => TestEvent3
            [time] => 1
            [parameters] => Array
                (
                    [title] => Chapter 2
                    [index] => 2
                )

            [type] => navigation
        )

)
		
			Array
(
    [0] => Array
        (
            [name] => TestEvent1
            [time] => 0
            [parameters] => Array
                (
                    [speaker] => Peter
                    [says] => Hello my name is Peter.
                )

            [type] => event
        )

    [1] => Array
        (
            [name] => TestEvent3
            [time] => 1
            [parameters] => Array
                (
                    [title] => Chapter 2
                    [index] => 2
                )

            [type] => navigation
        )

)
		

Analysis
Will come soon

Conclusion
Let's imagine we integrate the yamdi features into flv4php (and vice versa) with the cuepoint of flvtool2 and we have clearly killing tools to manipulate flv videos. Due to the headaches I have got to make flv4php working and as I want to avoid you this pain too, you can the download the goldorak's flv injector here.