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);
		}
	
	}
}