Blog of Steve Savage A blog for business analysts and architects, and fellow user's of Sparx's EA

20Mar/060

‘Streaming’ Flash FLV video using Coldfusion, PHP or ASP

Problem Addressed

Allowing users to jump to any point in a movie without having to wait for the entire movie to download and without purchasing expensive media server software

Solution

  1. Convert you movie file to an FLV file
  2. Run Buraks - FLV MetaData Injector on the file. This will 'inject' the byte position of the videos 'keyframes' as additional information in the FLV.
  3. Install either:
  4. Select a player:
<cfsetting enablecfoutputonly="NO">
<!--- the function that sends the feed --->
<cfscript>
 function f_StreamFLV(s_file,i_seek) {
 // i_seek = the byte position of a key frame in the video
 var i_position = i_seek;
 var i_buffer = 10000;
 var byteClass = createObject("java",
 	"java.lang.Byte");
 var byteArray = createObject("java",
 	"java.lang.reflect.Array")
	.newInstance(byteClass.TYPE, i_buffer);
 var context = getPageContext();
 var response = context.getResponse().getResponse();
 var flvinstream = createObject("java",
 	"java.io.FileInputStream");
 // take over control of the feed to the browser
 var flvoutstream = response.getOutputStream();
 byteClass.Init(1);
 flvinstream.init(s_file);
 context.setFlushOutput(false);
 try {
  if(i_seek GT 0) {
   // jump to a location of a key frame
   flvinstream.skip(i_seek);
   // output the header bytes
   flvoutstream.write(toBinary('RkxWAQEAAAAJAAAACQ=='));
  }
  do {
   i_length = flvinstream.read(byteArray,0,i_buffer);
   if (i_length neq -1) {
   flvoutstream.write(byteArray);
   flvoutstream.flush();
  }
  }
	while (i_length neq -1);
	// keep going until there's nothing left to read.
 }
 catch(any excpt) {}
 flvoutstream.flush(); // send any remaining bytes
 response.reset(); // reset the feed to the browser
 flvoutstream.close(); // close the stream to flash
 flvinstream.close(); // close the file stream
}
</cfscript>

<!--- set to false to allow the file name
	to be passed, true for a numeric ID --->
<cfset b_secure = true>

<!--- path for the movie --->
<cfset s_path = ...>
<!--- must use the extended path either in the
	form c:/path/file.flv or c:\\path\\file.flv. --->
<!--- the application.root variable is set
	in my application.cfc file,
and gives the absolute path to the root of my site --->

<cfparam name="position" default="0">
<cfif isNumeric(position)>
 <cfset i_seek = position><cfelse><cfset i_seek = 0>
</cfif>

<!--- create an array for possible movies,
or this could read an XML file, or the vidID
could be used to reference a database record --->
<cfset a_movies=ArrayNew(1)>
<cfset a_movies[1] = "##s_path##movie1.flv">
<cfset a_movies[2] = "##s_path##movie2.flv">
<cfset s_file = a_movies[1]> <!--- set the default movie ---> 

<cfif b_secure>
 <!--- I decided to use a file ID instead
	of a file name to make the script more secure --->
 <cfif isDefined("url.vidID")
 	and isNumeric(url.vidID) and url.vidID LTE arrayLen(a_movies)>
  <cfset s_file = a_movies[url.vidID]>
 </cfif>
<cfelseif isDefined("url.vidFile")>
 <cfset s_file = "##s_path####url.vidFile##">
</cfif>
<cfset f_StreamFLV(s_file,i_seek)>
<cfsetting enablecfoutputonly="NOS">

How this works

When you 'seek' to a new position in the movie, the player will determine the location of the nearest key frame (as a byte position) and passes that number to the server.

The server opens a file stream to the video, and seeks to the specified position, outputs the standard FLV header bytes, and then starts sending the file starting at that point.

Background

This is the result of a major geekout weekend, the code works, but could probably use some refactoring.

I was trying to figure out a way to stream video for a hobby site I was planning, and I came across an article on streaming flash FLV files using PHP, great I thought, but I use Coldfusion hosting services (no PHP support), I took a look at the PHP script, and it seemed fairly straight forward, but I wasn't sure how to do a binary stream in coldfusion, some more research and I found Christian Cantrell's site, with entries on Byte Arrays and Writing Binary Data to Browser putting this together with some java that I know I managed to create a script for Streaming FLV video via ColdFusion.

For the video, I compressed an AVI file (Space Quest promo video) using sorensen Squeeze 3.5, and injected the key-frame position metadata using Buraks - FLV MetaData Injector