Often I find, particularly with exhibit type projects, there is a requirement for some looping video. Often the loop is required as it’s part of the UI, rather than consumable content. A designer or animator has made some cool effect and video is simply the best way to deliver this asset.
Now there comes a problem – in older versions of flash player it was quite easy to just loop a video, but in more recent releases it’s broken and according to adobe this is not important enough to fix.
The only way that I found to get around this was to convert the file to a swf with the video on the timeline. I had some success with this, but the quality always suffered and every time the designers updated the asset I had to reconvert. Someone on the bug report suggested however that there might be way to get the video file to repeat seamlessly by using appendBytes but hadn’t provided a working example. So I just ran into this again and decided to tackle it once and for all – here’s a class that’ll seamlessly loop an flv (appendBytes only supports flv):
Edit: I recently had cause to use this again in a project so have tidied up the code a little and fixed a bug where I had delays in later loops. Also I’ve added a variation for cases where an animation plays in with one video, then loops with another.
package
{
import flash.events.AsyncErrorEvent;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.NetStreamAppendBytesAction;
import flash.utils.ByteArray;
public class AppendByteVideoLoop
{
private var _video:Video;
private var _connection:NetConnection;
private var _stream:NetStream;
private var _byteArray:ByteArray;
public function AppendByteVideoLoop(video:Video, data:ByteArray):void
{
_video = video;
_byteArray = data;
connect();
}
private function connect():void
{
_connection = new NetConnection();
_connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
_connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
_connection.connect(null);
}
private function addToStream():void
{
_stream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
_stream.appendBytes(_byteArray);
}
public function destroy():void
{
_stream.close();
_stream = null;
_connection.close()
_connection = null;
}
public function togglePause():void
{
_stream.togglePause()
}
public function onMetaData(metaData:Object):void
{
_video.width = metaData.width;
_video.height = metaData.height;
addToStream();
}
public function onXMPData(xmp:Object):void { }
public function onPlayStatus(status:Object):void { }
private function connectStream():void
{
_stream = new NetStream(_connection);
_stream.client = this;
_stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
_stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
_video.attachNetStream(_stream);
_stream.play(null);
_stream.appendBytes(_byteArray);
}
private function netStatusHandler(event:NetStatusEvent):void
{
switch (event.info.code)
{
case "NetConnection.Connect.Success":
connectStream();
break;
case "NetStream.Play.StreamNotFound":
trace(event.info.code, "Unable to locate video data ");
break;
}
}
private function securityErrorHandler(event:SecurityErrorEvent):void
{
trace("securityErrorHandler: " + event);
}
private function asyncErrorHandler(event:AsyncErrorEvent):void
{
trace("asyncErrorHandler: " + event);
}
}
}
package
{
import flash.events.AsyncErrorEvent;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.NetStreamAppendBytesAction;
import flash.utils.ByteArray;
public class AppendByteVideoLoopWithIntro
{
private var _video:Video;
private var _connection:NetConnection;
private var _stream:NetStream;
private var _byteArrayIntro:ByteArray;
private var _byteArrayLoop:ByteArray;
public function AppendByteVideoLoopWithIntro(video:Video, dataIntro:ByteArray, dataLoop:ByteArray):void
{
_video = video;
_byteArrayIntro = dataIntro;
_byteArrayLoop = dataLoop;
connect();
}
private function connect():void
{
_connection = new NetConnection();
_connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
_connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
_connection.connect(null);
}
private function addToStream():void
{
_stream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
_stream.appendBytes(_byteArrayLoop);
}
public function togglePause():void
{
_stream.togglePause()
}
public function destroy():void
{
_stream.close();
_stream = null;
_connection.close()
_connection = null;
}
public function onMetaData(metaData:Object):void
{
_video.width = metaData.width;
_video.height = metaData.height;
addToStream();
}
public function onXMPData(xmp:Object):void { }
public function onPlayStatus(status:Object):void { }
private function connectStream():void
{
_stream = new NetStream(_connection);
_stream.client = this;
_stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
_stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
_video.attachNetStream(_stream);
_stream.play(null);
_stream.appendBytes(_byteArrayIntro);
}
private function netStatusHandler(event:NetStatusEvent):void
{
switch (event.info.code)
{
case "NetConnection.Connect.Success":
connectStream();
break;
case "NetStream.Play.StreamNotFound":
trace(event.info.code, "Unable to locate video data ");
break;
}
}
private function securityErrorHandler(event:SecurityErrorEvent):void
{
trace("securityErrorHandler: " + event);
}
private function asyncErrorHandler(event:AsyncErrorEvent):void
{
trace("asyncErrorHandler: " + event);
}
}
}