Index: server/asobj/NetStreamFfmpeg.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/NetStreamFfmpeg.cpp,v
retrieving revision 1.134
diff -u -r1.134 NetStreamFfmpeg.cpp
--- server/asobj/NetStreamFfmpeg.cpp	24 May 2008 22:03:32 -0000	1.134
+++ server/asobj/NetStreamFfmpeg.cpp	26 May 2008 16:39:52 -0000
@@ -32,7 +32,7 @@
 #include "movie_root.h"
 #include "sound_handler.h"
 #include "VideoDecoderFfmpeg.h"
-#include "ClockTime.h" // TODO: use the VirtualClock instead ?
+#include "SystemClock.h"
 
 #include "FLVParser.h" 
 
@@ -48,24 +48,28 @@
 #endif
 
 /// Define this to add debugging prints for locking
-#define GNASH_DEBUG_THREADS
+//#define GNASH_DEBUG_THREADS
 
 // Define the following macro to have status notification handling debugged
 //#define GNASH_DEBUG_STATUS
 
+namespace {
+
 // Used to free data in the AVPackets we create our self
-static void avpacket_destruct(AVPacket* av)
+void avpacket_destruct(AVPacket* av)
 {
 	delete [] av->data;
 }
 
+} // anonymous namespace
+
 
 namespace gnash {
 
 
-NetStreamFfmpeg::NetStreamFfmpeg():
+NetStreamFfmpeg::NetStreamFfmpeg()
+	:
 
-	_playback_state(PLAY_NONE),
 	_decoding_state(DEC_NONE),
 
 	m_video_index(-1),
@@ -82,10 +86,11 @@
 
 	m_last_video_timestamp(0),
 	m_last_audio_timestamp(0),
-	m_current_timestamp(0),
+
+	_playbackClock(new InterruptableVirtualClock(new SystemClock)),
+	_playHead(_playbackClock.get()), 
+
 	m_unqueued_data(NULL),
-	m_time_of_pause(0),
-	m_start_onbuffer(false),
 
 	_decoderBuffer(0),
 	_soundHandler(get_sound_handler())
@@ -107,23 +112,21 @@
 void NetStreamFfmpeg::pause( PauseMode mode )
 {
 	log_debug("::pause(%d) called ", mode);
-  switch ( mode ) {
-    case pauseModeToggle:
-			if ( playbackStatus() == PLAY_PAUSED ) {
-			  unpausePlayback();
-			} else {
-			  pausePlayback();
-			}
+	switch ( mode )
+	{
+		case pauseModeToggle:
+			if ( _playHead.getState() == PlayHead::PLAY_PAUSED) unpausePlayback();
+			else pausePlayback();
 			break;
-    case pauseModePause:
+		case pauseModePause:
 			pausePlayback();
 			break;
-    case pauseModeUnPause:
+		case pauseModeUnPause:
 			unpausePlayback();
 			break;
-    default:
+		default:
 			break;
-  }
+	}
 
 }
 
@@ -165,11 +168,6 @@
 	delete m_unqueued_data;
 	m_unqueued_data = NULL;
 
-	boost::mutex::scoped_lock lock(_qMutex);
-
-	m_qvideo.clear();
-	m_qaudio.clear();
-
 	delete [] ByteIOCxt.buffer;
 
 }
@@ -180,10 +178,12 @@
 {
 
 	NetStreamFfmpeg* ns = static_cast<NetStreamFfmpeg*>(opaque);
-	boost::intrusive_ptr<NetConnection> nc = ns->_netCon;
 
-	size_t ret = nc->read(static_cast<void*>(buf), buf_size);
-	ns->inputPos += ret;
+	assert( ns->_inputStream.get() );
+	tu_file& in = *(ns->_inputStream);
+
+	size_t ret = in.read_bytes(static_cast<void*>(buf), buf_size);
+	ns->inputPos += ret; // what for ??
 	return ret;
 
 }
@@ -194,21 +194,21 @@
 {
 
 	NetStreamFfmpeg* ns = static_cast<NetStreamFfmpeg*>(opaque);
-	boost::intrusive_ptr<NetConnection> nc = ns->_netCon;
 
+	tu_file& in = *(ns->_inputStream);
 
 	// Offset is absolute new position in the file
 	if (whence == SEEK_SET)
 	{	
-		nc->seek(offset);
-		ns->inputPos = offset;
+		in.set_position(offset);
+		ns->inputPos = offset; // what for ?!
 
 	// New position is offset + old position
 	}
 	else if (whence == SEEK_CUR)
 	{
-		nc->seek(ns->inputPos + offset);
-		ns->inputPos = ns->inputPos + offset;
+		in.set_position(ns->inputPos + offset);
+		ns->inputPos = ns->inputPos + offset; // what for ?!
 
 	// New position is offset + end of file
 	}
@@ -216,23 +216,22 @@
 	{
 		// This is (most likely) a streamed file, so we can't seek to the end!
 		// Instead we seek to 50.000 bytes... seems to work fine...
-		nc->seek(50000);
-		ns->inputPos = 50000;
+		in.set_position(50000);
+		ns->inputPos = 50000; // what for ?!
 
 	}
 
-	return ns->inputPos;
+	return ns->inputPos; // ah, thats why ! :/
 }
 
 void
 NetStreamFfmpeg::play(const std::string& c_url)
 {
-
 	// Is it already playing ?
-	if (playbackStatus() != PLAY_NONE && playbackStatus() != PLAY_STOPPED)
+	if ( m_parser.get() )
 	{
-		log_error("NetStream.play() called already playing ?"); // TODO: fix this case
-		//unpausePlayback(); // will check for playbackStatus itself..
+		// TODO: check what to do in these cases
+		log_error("FIXME: NetStream.play() called while already streaming");
 		return;
 	}
 
@@ -245,13 +244,34 @@
 		return;
 	}
 
-	if (url.size() == 0) url += c_url;
+	url = c_url;
+
 	// Remove any "mp3:" prefix. Maybe should use this to mark as audio-only
 	if (url.compare(0, 4, std::string("mp3:")) == 0)
 	{
 		url = url.substr(4);
 	}
 
+	// TODO: check what is this needed for, I'm not sure it would be needed..
+	url = _netCon->validateURL(url);
+	if (url.empty())
+	{
+		log_error("Couldn't load URL %s", c_url);
+		return;
+	}
+
+	log_security( _("Connecting to movie: %s"), url );
+
+	StreamProvider& streamProvider = StreamProvider::getDefaultInstance();
+	_inputStream.reset( streamProvider.getStream( url ) );
+
+	if ( ! _inputStream.get() )
+	{
+		log_error( _("Gnash could not open this url: %s"), url );
+		setStatus(streamNotFound);
+		return;
+	}
+
 	// We need to start playback
 	if (!startPlayback())
 	{
@@ -259,15 +279,13 @@
 		return;
 	}
 
-	//decodingStatus(DEC_BUFFERING);
-
 	// We need to restart the audio
 	if (_soundHandler)
 		_soundHandler->attach_aux_streamer(audio_streamer, this);
 
 	// This starts the decoding thread
-	_decodeThread = new boost::thread(boost::bind(NetStreamFfmpeg::av_streamer, this)); 
-	_decodeThreadBarrier.wait();
+	//_decodeThread = new boost::thread(boost::bind(NetStreamFfmpeg::av_streamer, this)); 
+	//_decodeThreadBarrier.wait();
 
 	return;
 }
@@ -354,6 +372,7 @@
 	FLVAudioInfo* audioInfo =  parser.getAudioInfo();
 	if (!audioInfo)
 	{
+		log_debug("No audio in FLV stream");
 		return NULL;
 	}
 
@@ -407,42 +426,37 @@
 bool
 NetStreamFfmpeg::startPlayback()
 {
+	assert(_inputStream.get());
+	assert(_inputStream->get_position() == 0);
 
-	boost::intrusive_ptr<NetConnection> nc = _netCon;
-	assert(nc);
-
-	// Pass stuff from/to the NetConnection object.
-	if ( !nc->openConnection(url) )
-	{
-		log_error(_("Gnash could not open movie: %s"), url.c_str());
-		setStatus(streamNotFound);
-		return false;
-	}
-
-	nc->seek(0);
 	inputPos = 0;
 
 	// Check if the file is a FLV, in which case we use our own parser
 	char head[4] = {0, 0, 0, 0};
-	if (nc->read(head, 3) < 3)
+	if (_inputStream->read_bytes(head, 3) < 3)
 	{
+		log_error(_("Could not read 3 bytes from NetStream input"));
+		// not really correct, the stream was found, just wasn't what we expected..
 		setStatus(streamNotFound);
 		return false;
 	}
 
-	nc->seek(0);
+
+	_inputStream->set_position(0);
 	if (std::string(head) == "FLV")
 	{
 		m_isFLV = true;
-		if (!m_parser.get())
+		assert ( !m_parser.get() );
+
+		m_parser.reset( new FLVParser(*_inputStream) ); 
+		//assert(! _inputStream.get() ); // TODO: when ownership will be transferred...
+
+		if (! m_parser.get() )
 		{
-			m_parser = nc->getConnectedParser();
-			if (! m_parser.get() )
-			{
-				setStatus(streamNotFound);
-				log_error(_("Gnash could not open FLV movie: %s"), url.c_str());
-				return false;
-			}
+			log_error(_("Gnash could not open FLV movie: %s"), url.c_str());
+			// not really correct, the stream was found, just wasn't what we expected..
+			setStatus(streamNotFound);
+			return false;
 		}
 
 		// Init the avdecoder-decoder
@@ -459,8 +473,9 @@
 		m_ACodecCtx = initFlvAudio(*m_parser);
 		if (!m_ACodecCtx)
 		{
-			log_error(_("Failed to initialize FLV audio codec"));
-			return false;
+			// There might simply be no audio, no problem...
+			//log_error(_("Failed to initialize FLV audio codec"));
+			//return false;
 		}
 
 		// We just define the indexes here, they're not really used when
@@ -468,8 +483,6 @@
 		m_video_index = 0;
 		m_audio_index = 1;
 
-		m_start_onbuffer = true;
-
 		// Allocate a frame to store the decoded frame in
 		m_Frame = avcodec_alloc_frame();
 	}
@@ -490,7 +503,9 @@
 		}
 
 		// After the format probe, reset to the beginning of the file.
-		nc->seek(0);
+		// TODO: have this done by probeStream !
+		//       (actually, have the whole thing done by MediaParser)
+		_inputStream->set_position(0);
 
 		// Setup the filereader/seeker mechanism. 7th argument (NULL) is the writer function,
 		// which isn't needed.
@@ -603,8 +618,16 @@
 		}
 	}
 
-	playbackStatus(PLAY_PLAYING);
-	m_start_clock = clocktime::getTicks();
+	_playHead.init(m_VCodecCtx!=0, false); // second arg should be m_ACodecCtx!=0, but we're testing video only for now
+	_playHead.setState(PlayHead::PLAY_PLAYING);
+
+	decodingStatus(DEC_BUFFERING);
+
+//#ifdef GNASH_DEBUG_STATUS
+	log_debug("Setting playStart status");
+//#endif
+	setStatus(playStart);
+
 	return true;
 }
 
@@ -639,106 +662,14 @@
 
 	ns->_decodeThreadBarrier.wait();
 
-	//assert (ns->m_ACodecCtx); // is only set if audio decoder could be initialized 
-	//assert (ns->m_VCodecCtx); // is only set if video decder could be initialized 
-	//assert (ns->m_FormatCtx); // is only set for non-flv
-
-	ns->setStatus(playStart);
-
-	ns->m_last_video_timestamp = 0;
-	ns->m_last_audio_timestamp = 0;
-	ns->m_current_timestamp = 0;
-
-	ns->m_start_clock = clocktime::getTicks();
-
-	ns->m_unqueued_data = NULL;
-
-	// Loop until killed
-	while ( ! ns->decodeThreadKillRequested() ) // locks _qMutex
+	// Parse in a thread...
+	abort(); // has to be fixed to use mutex against parser
+	// FIXME: 
+	while ( ! ns->m_parser->parsingCompleted()
+		&& ! ns->decodeThreadKillRequested() ) 
 	{
-		unsigned long int sleepTime = 1000;
-
-		{
-#ifdef GNASH_DEBUG_THREADS
-		log_debug("qMutex: waiting for lock in av_streamer");
-#endif
-		boost::mutex::scoped_lock lock(ns->_qMutex);
-#ifdef GNASH_DEBUG_THREADS
-		log_debug("qMutex: lock obtained in av_streamer");
-#endif
-
-		if ( ns->decodingStatus() == DEC_STOPPED )
-		{
-			log_debug("Dec stopped (eof), waiting on qNeedRefill condition");
-			ns->_qFillerResume.wait(lock);
-			continue; // will release the lock for a moment
-		}
-
-#ifdef GNASH_DEBUG_THREADS
-		log_debug("Decoding iteration. bufferTime=%lu, bufferLen=%lu, videoFrames=%lu, audioFrames=%lu",
-			ns->bufferTime(), ns->bufferLength(), ns->m_qvideo.size(), ns->m_qaudio.size());
-#endif
-
-		if (ns->m_isFLV)
-		{
-			// If any of the two queues are full don't bother fetching more
-			// (next consumer will wake us up)
-			//
-      			if ( ns->m_qvideo.full() || ns->m_qaudio.full() )
-			{
-				ns->decodingStatus(DEC_DECODING); // that's to say: not buffering anymore
-
-				// Instead wait till waked up by short-queues event
-				log_debug("Queues full, waiting on qNeedRefill condition");
-				ns->_qFillerResume.wait(lock);
-			}
-			else
-			{
-				log_debug("Calling decodeFLVFrame");
-				bool successDecoding = ns->decodeFLVFrame();
-				//log_debug("decodeFLVFrame returned %d", successDecoding);
-				if ( ! successDecoding )
-				{
-					// Possible failures:
-					// 1. could not decode frame... lot's of possible
-					//    reasons...
-					// 2. EOF reached
-					if ( ns->m_videoFrameFormat != render::NONE )
-					{
-						log_error("Could not decode FLV frame");
-					}
-					// else it's expected, we'll keep going anyway
-				}
-
-			}
-
-		}
-		else
-		{
-
-			// If we have problems with decoding - break
-			if (ns->decodeMediaFrame() == false && ns->m_start_onbuffer == false && ns->m_qvideo.size() == 0 && ns->m_qaudio.size() == 0)
-			{
-				break;
-			}
-
-		}
-
-#ifdef GNASH_DEBUG_THREADS
-		log_debug("qMutex: releasing lock in av_streamer");
-#endif
-		}
-
-		//log_debug("Sleeping %d microseconds", sleepTime);
-		usleep(sleepTime); // Sleep 1ms to avoid busying the processor.
-
+		ns->m_parser->parseNextTag();
 	}
-
-//#ifdef GNASH_DEBUG_THREADS
-	log_debug("Out of decoding loop. playbackStatus:%d, decodingStatus:%d", ns->playbackStatus(), ns->decodingStatus());
-//#endif
-	ns->decodingStatus(DEC_STOPPED);
-
 }
 
 // audio callback is running in sound handler thread
@@ -746,6 +677,10 @@
 {
 	//GNASH_REPORT_FUNCTION;
 
+	return false;
+
+#if 0 // no audio for now, needs proper mutex design first (SDL sound handler runs in a thread)
+
 	NetStreamFfmpeg* ns = static_cast<NetStreamFfmpeg*>(owner);
 
 	PlaybackState pbStatus = ns->playbackStatus();
@@ -798,10 +733,100 @@
 #endif
 	}
 	return true;
+#endif
 }
 
-bool NetStreamFfmpeg::decodeFLVFrame()
+media::raw_mediadata_t*
+NetStreamFfmpeg::getDecodedVideoFrame(boost::uint32_t ts)
 {
+	if ( ! m_parser.get() )
+	{
+		log_error("getDecodedVideoFrame: no parser available");
+		return 0; // no parser, no party
+	}
+
+	FLVVideoFrameInfo* info = m_parser->peekNextVideoFrameInfo();
+	if ( ! info )
+	{
+		log_debug("getDecodedVideoFrame(%d): no more video frames in input (peekNextVideoFrameInfo returned false)");
+		decodingStatus(DEC_STOPPED);
+		return 0;
+	}
+
+	if ( info->timestamp > ts )
+	{
+		log_debug("getDecodedVideoFrame(%d): next video frame is in the future (%d)", ts, info->timestamp);
+		return 0; // next frame is in the future
+	}
+
+	// Loop until a good frame is found
+    	media::raw_mediadata_t* video = 0;
+	while ( 1 )
+	{
+    		video = decodeNextVideoFrame();
+		if ( ! video )
+		{
+			log_error("peekNextVideoFrameInfo returned some info, "
+				"but decodeNextVideoFrame returned null, "
+				"I don't think this should ever happen");
+			break;
+		}
+
+		FLVVideoFrameInfo* info = m_parser->peekNextVideoFrameInfo();
+		if ( ! info )
+		{
+			// the one we decoded was the last one
+			log_debug("last video frame decoded (should set playback status to STOP?)");
+			break;
+		}
+		if ( info->timestamp > ts )
+		{
+			// the next one is in the future, we'll return this one.
+			log_debug("next video frame is in the future, we'll return this one");
+			break; // the one we decoded
+		}
+	}
+
+	return video;
+}
+
+media::raw_mediadata_t*
+NetStreamFfmpeg::decodeNextVideoFrame()
+{
+	if ( ! m_parser.get() )
+	{
+		log_error("decodeNextVideoFrame: no parser available");
+		return 0; // no parser, no party
+	}
+
+	FLVFrame* frame = m_parser->nextVideoFrame(); 
+	if (frame == NULL)
+	{
+		log_debug("decodeNextVideoFrame: no more video frames in input");
+		return 0;
+	}
+	assert (frame->type == videoFrame);
+
+  	AVPacket packet;
+
+  	packet.destruct = avpacket_destruct; // needed ?
+  	packet.size = frame->dataSize;
+  	packet.data = frame->data;
+  	// FIXME: is this the right value for packet.dts?
+  	packet.pts = packet.dts = static_cast<boost::int64_t>(frame->timestamp);
+	assert (frame->type == videoFrame);
+	packet.stream_index = 0;
+
+	return decodeVideo(&packet);
+}
+
+bool
+NetStreamFfmpeg::decodeFLVFrame()
+{
+#if 1
+	abort();
+	return false;
+#else
 	FLVFrame* frame = m_parser->nextMediaFrame(); // we don't care which one, do we ?
 
 	if (frame == NULL)
@@ -823,21 +848,40 @@
 	if (frame->type == videoFrame)
 	{
     		packet.stream_index = 0;
-    		return decodeVideo(&packet);
+		media::raw_mediadata_t* video = decodeVideo(&packet);
+		assert (m_isFLV);
+		if (video)
+		{
+			// NOTE: Caller is assumed to have locked _qMutex already
+			if ( ! m_qvideo.push(video) )
+			{
+				log_error("Video queue full !");
+			}
+		}
 	}
 	else
 	{
 		assert(frame->type == audioFrame);
     		packet.stream_index = 1;
-    		return decodeAudio(&packet);
+		media::raw_mediadata_t* audio = decodeAudio(&packet);
+		if ( audio )
+		{
+			if ( ! m_qaudio.push(audio) )
+			{
+				log_error("Audio queue full!");
+			}
+		}
 	}
 
+	return true;
+#endif
 }
 
 
-bool NetStreamFfmpeg::decodeAudio( AVPacket* packet )
+media::raw_mediadata_t* 
+NetStreamFfmpeg::decodeAudio( AVPacket* packet )
 {
-	if (!m_ACodecCtx) return false;
+	if (!m_ACodecCtx) return 0;
 
 	int frame_size;
 	//static const unsigned int bufsize = (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2;
@@ -945,26 +989,21 @@
 
 		m_last_audio_timestamp += frame_delay;
 
-		if (m_isFLV)
-		{
-			if ( ! m_qaudio.push(raw) )
-			{
-				log_error("Audio queue full!");
-			}
-		}
-		else m_unqueued_data = m_qaudio.push(raw) ? NULL : raw;
+		return raw;
 	}
-	return true;
+	return 0;
 }
 
 
-bool NetStreamFfmpeg::decodeVideo(AVPacket* packet)
+media::raw_mediadata_t* 
+NetStreamFfmpeg::decodeVideo(AVPacket* packet)
 {
-	if (!m_VCodecCtx) return false;
+	if (!m_VCodecCtx) return NULL;
+	if (!m_Frame) return NULL;
 
 	int got = 0;
 	avcodec_decode_video(m_VCodecCtx, m_Frame, &got, packet->data, packet->size);
-	if (!got) return false;
+	if (!got) return NULL;
 
 	// This tmpImage is really only used to compute proper size of the video data...
 	// stupid isn't it ?
@@ -983,7 +1022,7 @@
 	if (m_videoFrameFormat == render::NONE)
 	{
 		// NullGui?
-		return false;
+		return NULL;
 
 	}
 	else if (m_videoFrameFormat == render::YUV && m_VCodecCtx->pix_fmt != PIX_FMT_YUV420P)
@@ -998,7 +1037,7 @@
 		rgbpicture = media::VideoDecoderFfmpeg::convertRGB24(m_VCodecCtx, *m_Frame);
 		if (!rgbpicture.data[0])
 		{
-			return false;
+			return NULL;
 		}
 	}
 
@@ -1089,21 +1128,14 @@
 
 	}
 
-	// NOTE: Caller is assumed to have locked _qMutex already
-	if (m_isFLV)
-	{
-		if ( ! m_qvideo.push(video) )
-		{
-			log_error("Video queue full !");
-		}
-	}
-	else m_unqueued_data = m_qvideo.push(video) ? NULL : video;
-
-	return true;
+	return video;
 }
 
 bool NetStreamFfmpeg::decodeMediaFrame()
 {
+	return false;
+
+#if 0 // Only FLV for now (non-FLV should be threated the same as FLV, using a MediaParser in place of the FLVParser)
 
 	if (m_unqueued_data)
 	{
@@ -1133,20 +1165,24 @@
 	{
 		if (packet.stream_index == m_audio_index && _soundHandler)
 		{
-      			if (!decodeAudio(&packet)) 
+			media::raw_mediadata_t* audio = decodeAudio(&packet);
+      			if (!audio)
 			{
 				log_error(_("Problems decoding audio frame"));
 				return false;
 			}
+			m_unqueued_data = m_qaudio.push(audio) ? NULL : audio;
 		}
 		else
 		if (packet.stream_index == m_video_index)
 		{
-      			if (!decodeVideo(&packet)) 
+			media::raw_mediadata_t* video = decodeVideo(&packet);
+      			if (!video)
 			{
 				log_error(_("Problems decoding video frame"));
 				return false;
 			}
+			m_unqueued_data = m_qvideo.push(video) ? NULL : video;
 		}
 		av_free_packet(&packet);
 	}
@@ -1157,15 +1193,24 @@
 	}
 
 	return true;
+#endif
 }
 
 void
-NetStreamFfmpeg::seek(boost::uint32_t pos)
+NetStreamFfmpeg::seek(boost::uint32_t posSeconds)
 {
 	GNASH_REPORT_FUNCTION;
 
-	// We'll mess with the queues here
-	boost::mutex::scoped_lock lock(_qMutex);
+	// We'll mess with the input here
+	if ( ! m_parser.get() )
+	{
+		log_debug("NetStreamFfmpeg::seek(%d): no parser, no party", posSeconds);
+		return;
+	}
+
+	// Don't ask me why, but NetStream::seek() takes seconds...
+	boost::uint32_t pos = posSeconds*1000;
+
 
 	long newpos = 0;
 	double timebase = 0;
@@ -1173,18 +1218,11 @@
 	// Seek to new position
 	if (m_isFLV)
 	{
-		if (m_parser.get())
-		{
-			newpos = m_parser->seek(pos);
-		}
-		else
-		{
-			newpos = 0;
-		}
+		newpos = m_parser->seek(pos);
+		log_debug("m_parser->seek(%d) returned %d", pos, newpos);
 	}
 	else if (m_FormatCtx)
 	{
-
 		AVStream* videostream = m_FormatCtx->streams[m_video_index];
     		timebase = static_cast<double>(videostream->time_base.num / videostream->time_base.den);
 		newpos = static_cast<long>(pos / timebase);
@@ -1206,20 +1244,11 @@
 	{
 		m_last_video_timestamp = 0;
 		m_last_audio_timestamp = 0;
-		m_current_timestamp = 0;
-
-		m_start_clock = clocktime::getTicks();
-
 	}
 	else if (m_isFLV)
 	{
-
-		if (m_VCodecCtx) m_start_clock += m_last_video_timestamp - newpos;
-		else m_start_clock += m_last_audio_timestamp - newpos;
-
 		if (m_ACodecCtx) m_last_audio_timestamp = newpos;
 		if (m_VCodecCtx) m_last_video_timestamp = newpos;
-		m_current_timestamp = newpos;
 	}
 	else
 	{
@@ -1241,146 +1270,159 @@
     		av_free_packet( &Packet );
 
 		av_seek_frame(m_FormatCtx, m_video_index, newpos, 0);
-		boost::uint32_t newtime_ms = static_cast<boost::int32_t>(newtime / 1000.0);
-		m_start_clock += m_last_audio_timestamp - newtime_ms;
+		newpos = static_cast<boost::int32_t>(newtime / 1000.0);
 
-		m_last_audio_timestamp = newtime_ms;
-		m_last_video_timestamp = newtime_ms;
-		m_current_timestamp = newtime_ms;
+		m_last_audio_timestamp = newpos;
+		m_last_video_timestamp = newpos;
 	}
 	
-	// Flush the queues
-	m_qvideo.clear();
-	m_qaudio.clear();
+	// 'newpos' will always be on a keyframe (supposedly)
+	_playHead.seekTo(newpos);
 
-	decodingStatus(DEC_DECODING); // or ::refreshVideoFrame will send a STOPPED again
-	if ( playbackStatus() == PLAY_STOPPED )
-	{
-		// restart playback (if not paused)
-		playbackStatus(PLAY_PLAYING);
-	}
+	decodingStatus(DEC_BUFFERING); // make sure we have enough things in buffer
 	_qFillerResume.notify_all(); // wake it decoder is sleeping
 	
+	refreshVideoFrame(true);
 }
 
 void
-NetStreamFfmpeg::refreshVideoFrame()
+NetStreamFfmpeg::parseNextChunk()
+{
+	// TODO: parse as much as possible w/out blocking
+	//       (will always block currently..)
+	const int tagsPerChunk = 2;
+	for (int i=0; i<tagsPerChunk; ++i)
+		m_parser->parseNextTag();
+}
+
+void
+NetStreamFfmpeg::refreshVideoFrame(bool alsoIfPaused)
 {
-#ifdef GNASH_DEBUG_THREADS
-	log_debug("qMutex: waiting for lock in refreshVideoFrame");
-#endif
-	boost::mutex::scoped_lock lock(_qMutex);
-#ifdef GNASH_DEBUG_THREADS
-	log_debug("qMutex: lock obtained in refreshVideoFrame");
-#endif
 
-	// If we're paused (and we got the first imageframe), there is no need to do this
-	if (playbackStatus() == PLAY_PAUSED && m_imageframe)
+	if ( ! m_parser.get() )
 	{
-		log_debug("refreshVideoFrame doing nothing as playback is paused and we have an image frame already");
-#ifdef GNASH_DEBUG_THREADS
-		log_debug("qMutex: releasing lock in refreshVideoFrame");
-#endif
+		log_debug("%p.refreshVideoFrame: no parser, no party", this);
 		return;
 	}
 
-	// Loop until a good frame is found
-	do
+	if ( decodingStatus() == DEC_DECODING && bufferLength() == 0)
 	{
-		// Get video frame from queue, will have the lowest timestamp
-		// will return NULL if empty(). See multithread_queue::front
-    		media::raw_mediadata_t* video = m_qvideo.front();
-
-		// If the queue is empty either we're waiting for more data
-		// to be decoded or we're out of data
-		if (!video)
+		if ( ! m_parser->parsingCompleted() )
 		{
-			log_debug("refreshVideoFrame:: No more video frames in queue");
-
-			if ( decodingStatus() == DEC_STOPPED )
-			{
-				if ( playbackStatus() != PLAY_STOPPED )
-				{
-					playbackStatus(PLAY_STOPPED);
-//#ifdef GNASH_DEBUG_STATUS
-					log_debug("Setting playStop status");
-//#endif
-					setStatus(playStop);
-				}
-			}
-			else
-			{
-				// There no video but decoder is still running
-				// not much to do here except wait for next call
-				//assert(decodingStatus() == DEC_BUFFERING);
-			}
-
-			break;
+			log_debug("%p.refreshVideoFrame: buffer empty while decoding,"
+				" setting buffer to buffering and pausing playback clock",
+				this);
+			setStatus(bufferEmpty);
+			decodingStatus(DEC_BUFFERING);
+			_playbackClock->pause();
 		}
-
-		// Caclulate the current time
-		boost::uint32_t current_clock;
-		if (m_ACodecCtx && _soundHandler)
+		else
 		{
-			current_clock = m_current_timestamp;
+			// set playStop ? (will be done later for now)
 		}
-		else
+	}
+
+	if ( decodingStatus() == DEC_BUFFERING )
+	{
+		if ( bufferLength() < m_bufferTime )
 		{
-			current_clock = clocktime::getTicks() - m_start_clock;
-			m_current_timestamp = current_clock;
+			log_debug("%p.refreshVideoFrame: buffering"
+				" - position=%d, buffer=%d/%d",
+				this, _playHead.getPosition(), bufferLength(), m_bufferTime);
+			return;
 		}
+		log_debug("%p.refreshVideoFrame: buffer full, resuming playback clock"
+			" - position=%d, buffer=%d/%d",
+			this, _playHead.getPosition(), bufferLength(), m_bufferTime);
+		setStatus(bufferFull);
+		decodingStatus(DEC_DECODING);
+		_playbackClock->resume();
+	}
 
-		boost::uint32_t video_clock = video->m_pts;
+	if ( ! alsoIfPaused && _playHead.getState() == PlayHead::PLAY_PAUSED )
+	{
+		log_debug("%p.refreshVideoFrame: doing nothing as playhead is paused - "
+			"bufferLength=%d, bufferTime=%d",
+			this, bufferLength(), m_bufferTime);
+		return;
+	}
 
-		// If the timestamp on the videoframe is smaller than the
-		// current time, we put it in the output image.
-		if (current_clock >= video_clock)
-		{
+	if ( _playHead.isVideoConsumed() ) 
+	{
+		log_debug("%p.refreshVideoFrame: doing nothing "
+			"as current position was already decoded - "
+			"bufferLength=%d, bufferTime=%d",
+			this, bufferLength(), m_bufferTime);
+		return;
+	}
 
-			if (m_videoFrameFormat == render::YUV)
-			{
-				if ( ! m_imageframe ) m_imageframe  = new image::yuv(m_VCodecCtx->width, m_VCodecCtx->height);
-				// XXX m_imageframe might be a byte aligned buffer, while video is not!
-				static_cast<image::yuv*>(m_imageframe)->update(video->m_data);
-			}
-			else if (m_videoFrameFormat == render::RGB)
-			{
-				if ( ! m_imageframe ) m_imageframe  = new image::rgb(m_VCodecCtx->width, m_VCodecCtx->height);
-				image::rgb* imgframe = static_cast<image::rgb*>(m_imageframe);
-				rgbcopy(imgframe, video, m_VCodecCtx->width * 3);
-			}
+	// Caclulate the current time
+	boost::uint64_t curPos = _playHead.getPosition();
 
-			// Delete the frame from the queue
-			m_qvideo.pop();
-			delete video;
+	log_debug("%p.refreshVideoFrame: currentPosition=%d, playHeadState=%d, bufferLength=%d, bufferTime=%d",
+		this, curPos, _playHead.getState(), bufferLength(), m_bufferTime);
 
-			// wake up filler (TODO: do only if decoder is running)
-			// TODO2: resume only at end of loop ?
-			_qFillerResume.notify_all();
 
-			// A frame is ready for pickup
-			m_newFrameReady = true;
+	// Get next decoded video frame from parser, will have the lowest timestamp
+    	media::raw_mediadata_t* video = getDecodedVideoFrame(curPos);
 
+	// to be decoded or we're out of data
+	if (!video)
+	{
+		if ( decodingStatus() == DEC_STOPPED )
+		{
+			log_debug("%p.refreshVideoFrame(): no more video frames to decode, sending STOP event", this);
+//#ifdef GNASH_DEBUG_STATUS
+			log_debug("Setting playStop status");
+//#endif
+			setStatus(playStop);
 		}
 		else
 		{
-			// The timestamp on the first frame in the queue is greater
-			// than the current time, so no need to do anything.
-			break;
+			log_debug("%p.refreshVideoFrame(): last video frame was good enough for current position", this);
+			// There no video but decoder is still running
+			// not much to do here except wait for next call
+			//assert(decodingStatus() == DEC_BUFFERING);
 		}
 
-	} while(!m_qvideo.empty());
+	}
+	else
+	{
+
+		if (m_videoFrameFormat == render::YUV)
+		{
+			if ( ! m_imageframe ) m_imageframe  = new image::yuv(m_VCodecCtx->width, m_VCodecCtx->height);
+			// XXX m_imageframe might be a byte aligned buffer, while video is not!
+			static_cast<image::yuv*>(m_imageframe)->update(video->m_data);
+		}
+		else if (m_videoFrameFormat == render::RGB)
+		{
+			if ( ! m_imageframe ) m_imageframe  = new image::rgb(m_VCodecCtx->width, m_VCodecCtx->height);
+			image::rgb* imgframe = static_cast<image::rgb*>(m_imageframe);
+			rgbcopy(imgframe, video, m_VCodecCtx->width * 3);
+		}
+
+		// Delete the frame from the queue
+		delete video;
+
+		// A frame is ready for pickup
+		m_newFrameReady = true;
+	}
+
+	// We consumed video of current position, feel free to advance if needed
+	_playHead.setVideoConsumed();
+
 
-#ifdef GNASH_DEBUG_THREADS
-	log_debug("qMutex: releasing lock in refreshVideoFrame");
-#endif
 }
 
 
 void
 NetStreamFfmpeg::advance()
 {
-	//log_debug("advance");
+	log_debug("%p.advance : bufferLength=%d, bufferTime=%d",
+		this, bufferLength(), m_bufferTime);
+
+	if ( m_parser.get() ) parseNextChunk(); 
 
 	// Check if there are any new status messages, and if we should
 	// pass them to a event handler
@@ -1389,109 +1431,83 @@
 	// Find video frame with the most suited timestamp in the video queue,
 	// and put it in the output image frame.
 	refreshVideoFrame();
+
+	// Refill audio buffer to consume all samples
+	// up to current playhead
+	//refreshAudioBuffer();
 }
 
 boost::int32_t
 NetStreamFfmpeg::time()
 {
-
-	if (m_FormatCtx && m_FormatCtx->nb_streams > 0)
-	{
-    		double time = (double)m_FormatCtx->streams[0]->time_base.num / (double)m_FormatCtx->streams[0]->time_base.den * (double)m_FormatCtx->streams[0]->cur_dts;
-		return static_cast<boost::int32_t>(time);
-	}
-	else if
-	(m_isFLV)
-	{
-		return m_current_timestamp;
-	}
-	else
-	{
-		return 0;
-	}
+	return _playHead.getPosition();
 }
 
 void NetStreamFfmpeg::pausePlayback()
 {
 	GNASH_REPORT_FUNCTION;
 
-	if (playbackStatus() == PLAY_PAUSED) return;
-
-	playbackStatus(PLAY_PAUSED);
+	PlayHead::PlaybackStatus oldStatus = _playHead.setState(PlayHead::PLAY_PAUSED);
 
-	// Save the current time so we later can tell how long the pause lasted
-	m_time_of_pause = clocktime::getTicks();
-
-	// Disconnect the soundhandler so we don't play while paused
-	if ( _soundHandler ) _soundHandler->detach_aux_streamer((void*)this);
+	// Disconnect the soundhandler if we were playing before
+	if ( oldStatus == PlayHead::PLAY_PLAYING && _soundHandler )
+	{
+		_soundHandler->detach_aux_streamer((void*)this);
+	}
 }
 
 void NetStreamFfmpeg::unpausePlayback()
 {
 	GNASH_REPORT_FUNCTION;
 
-	if (playbackStatus() == PLAY_PLAYING) // already playing
-	{
-		log_debug("unpausePlayback: already playing");
-		return;
-	}
-
-	playbackStatus(PLAY_PLAYING);
+	PlayHead::PlaybackStatus oldStatus = _playHead.setState(PlayHead::PLAY_PLAYING);
 
-	if (m_current_timestamp == 0)
+	// Re-connect to the soundhandler if we were paused before
+	if ( oldStatus == PlayHead::PLAY_PAUSED && _soundHandler )
 	{
-		m_start_clock = clocktime::getTicks();
+		_soundHandler->attach_aux_streamer(audio_streamer, (void*) this);
 	}
-	else
-	{
-		// Add the paused time to the start time so that the playhead doesn't
-		// noticed that we have been paused
-		m_start_clock += clocktime::getTicks() - m_time_of_pause;
-	}
-
-	// (re)-connect to the soundhandler.
-	// It was disconnected in ::pausePlayback to avoid to keep playing sound while paused
-	if ( _soundHandler ) _soundHandler->attach_aux_streamer(audio_streamer, (void*) this);
 }
 
 
 long
 NetStreamFfmpeg::bytesLoaded ()
 {
-  	long ret_val = 0;
-
-  	if ( _netCon ) 
+  	if ( ! m_parser.get() )
 	{
-    		ret_val = _netCon->getBytesLoaded();
+		log_debug("bytesLoaded: no parser, no party");
+		return 0;
   	}
 
-  	return ret_val;
+	return m_parser->getBytesLoaded();
 }
 
-
 long
-NetStreamFfmpeg::bytesTotal ()
+NetStreamFfmpeg::bufferLength ()
 {
-  	long ret_val = 0;
-
-  	if ( _netCon ) 
+  	if ( ! m_parser.get() )
 	{
-    		ret_val = _netCon->getBytesTotal();
+		log_debug("bytesTotal: no parser, no party");
+		return 0;
   	}
 
-  	return ret_val;
+	boost::uint32_t maxTimeInBuffer = m_parser->getBufferLength();
+	boost::uint64_t curPos = _playHead.getPosition();
+
+	if ( maxTimeInBuffer < curPos ) return 0;
+	return maxTimeInBuffer-curPos;
 }
 
-NetStreamFfmpeg::PlaybackState
-NetStreamFfmpeg::playbackStatus(PlaybackState newstate)
+long
+NetStreamFfmpeg::bytesTotal ()
 {
-	boost::mutex::scoped_lock lock(_state_mutex);
-
-	if (newstate != PLAY_NONE) {
-		_playback_state = newstate;
-	}
+  	if ( ! m_parser.get() )
+	{
+		log_debug("bytesTotal: no parser, no party");
+		return 0;
+  	}
 
-	return _playback_state;
+	return m_parser->getBytesTotal();
 }
 
 NetStreamFfmpeg::DecodingState
@@ -1512,14 +1528,6 @@
 	GNASH_REPORT_FUNCTION;
 
 	{
-#ifdef GNASH_DEBUG_THREADS
-		log_debug("qMutex: waiting for lock in killDecodeThread");
-#endif
-		boost::mutex::scoped_lock lock(_qMutex);
-#ifdef GNASH_DEBUG_THREADS
-		log_debug("qMutex: lock obtained in killDecodeThread");
-#endif
-
 		_qFillerKillRequest = true;
 		_qFillerResume.notify_all(); // wake it up if waiting..
 	}
@@ -1537,7 +1545,6 @@
 bool
 NetStreamFfmpeg::decodeThreadKillRequested()
 {
-	boost::mutex::scoped_lock lock(_qMutex);
 	return _qFillerKillRequest;
 }
 
Index: server/asobj/NetStreamFfmpeg.h
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/NetStreamFfmpeg.h,v
retrieving revision 1.69
diff -u -r1.69 NetStreamFfmpeg.h
--- server/asobj/NetStreamFfmpeg.h	24 May 2008 22:03:32 -0000	1.69
+++ server/asobj/NetStreamFfmpeg.h	26 May 2008 16:39:52 -0000
@@ -36,6 +36,9 @@
 #include <boost/thread/condition.hpp>
 #include <boost/thread/barrier.hpp>
 
+#include <memory>
+#include <cassert>
+
 #include "impl.h"
 
 #ifdef HAVE_FFMPEG_AVFORMAT_H
@@ -53,6 +56,7 @@
 #include "image.h"
 #include "StreamProvider.h"	
 #include "NetStream.h" // for inheritance
+#include "VirtualClock.h"
 
 #include "ffmpegNetStreamUtil.h"
 
@@ -109,6 +113,7 @@
 
 	long bytesTotal();
 
+	long bufferLength();
 private:
 
 	enum PlaybackState {
@@ -125,7 +130,6 @@
 		DEC_BUFFERING,
 	};
 
-	PlaybackState _playback_state;
 	DecodingState _decoding_state;
 
 	// Mutex protecting _playback_state and _decoding_state
@@ -163,7 +167,11 @@
 	/// is that  refreshVideoFrame() is called right before get_video(). This is important
 	/// to ensure timing is correct..
 	///
-	void refreshVideoFrame();
+	/// @param alsoIfPaused
+	///	If true, video is consumed/refreshed even if playhead is paused.
+	///	By default this is false, but will be used on ::seek (user-reguested)
+	///
+	void refreshVideoFrame(bool alsoIfPaused=false);
 
 	// Used to decode and push the next available (non-FLV) frame to the audio or video queue
 	bool decodeMediaFrame();
@@ -197,35 +205,45 @@
 	///
 	bool decodeFLVFrame();
 
-	/// Used to decode a video frame and push it on the videoqueue
+	/// Decode next video frame fetching it MediaParser cursor
+	//
+	/// @return 0 on EOF or error, a decoded video otherwise
+	///
+	media::raw_mediadata_t* decodeNextVideoFrame();
+
+	/// Decode input frames up to the one with timestamp <= ts.
 	//
-	/// Also updates m_imageframe (why !??)
+	/// Decoding starts from "next" element in the parser cursor.
+	///
+	/// Return 0 if:
+	///	1. there's no parser active.
+	///	2. parser cursor is already on last frame.
+	///	3. next element in cursor has timestamp > tx
+	///	4. there was an error decoding
 	///
+	media::raw_mediadata_t* getDecodedVideoFrame(boost::uint32_t ts);
+
+	/// Used to decode a video frame 
+	//
 	/// This is a blocking call.
-	/// If no Video decoding context exists (m_VCodecCtx), false is returned.
-	/// On decoding (or converting) error, false is returned.
-	/// If renderer requested video format is render::NONE, false is returned.
-	/// In any other case, true is returned.
+	/// If no Video decoding context exists (m_VCodecCtx), 0 is returned.
+	/// On decoding (or converting) error, 0 is returned.
+	/// If renderer requested video format is render::NONE, 0 is returned.
+	/// In any other case, a decoded video frame is returned.
 	///
-	/// NOTE: (FIXME) if video queue is full, 
-	///       we'd still return true w/out pushing anything new there
-	/// 
 	/// TODO: return a more informative value to tell what happened.
 	///
-	bool decodeVideo( AVPacket* packet );
+	media::raw_mediadata_t* decodeVideo( AVPacket* packet );
 
-	/// Used to decode a audio frame and push it on the audioqueue
+	/// Used to decode an audio frame 
 	//
 	/// This is a blocking call.
-	/// If no Video decoding context exists (m_ACodecCtx), false is returned.
-	/// In any other case, true is returned.
+	/// If no Video decoding context exists (m_ACodecCtx), 0 is returned.
+	/// In any other case, a decoded audio frame is returned.
 	///
-	/// NOTE: (FIXME) if audio queue is full,
-	///       we'd still return true w/out pushing anything new there
-	/// 
 	/// TODO: return a more informative value to tell what happened.
 	///
-	bool decodeAudio( AVPacket* packet );
+	media::raw_mediadata_t* decodeAudio( AVPacket* packet );
 
 	// Used to calculate a decimal value from a ffmpeg fraction
 	inline double as_double(AVRational time)
@@ -233,7 +251,6 @@
 		return time.num / (double) time.den;
 	}
 
-	PlaybackState playbackStatus(PlaybackState newstate = PLAY_NONE);
 	DecodingState decodingStatus(DecodingState newstate = DEC_NONE);
 
 	int m_video_index;
@@ -286,24 +303,14 @@
 	// The timestamp of the last decoded audio frame, in seconds.
 	volatile boost::uint32_t m_last_audio_timestamp;
 
-	// The timestamp of the last played audio (default) or video (if no audio) frame.
-	// Misured in seconds.
-	boost::uint32_t m_current_timestamp;
-
-	/// The queues of audio and video data.
-	typedef media::ElementsOwningQueue<media::raw_mediadata_t*> MediaQueue;
-
-	MediaQueue m_qaudio;
-	MediaQueue m_qvideo;
-
-	/// Mutex protecting access to queues
-	boost::mutex _qMutex;
-
 	/// Queues filler will wait on this condition when queues are full
 	boost::condition _qFillerResume;
 
-	// The time we started playing in seconds (since VM start ?)
-	volatile boost::uint64_t m_start_clock;
+	/// Virtual clock used as playback clock source
+	std::auto_ptr<InterruptableVirtualClock> _playbackClock;
+
+	/// Playback control device 
+	PlayHead _playHead;
 
 	// When the queues are full, this is where we keep the audio/video frame
 	// there wasn't room for on its queue
@@ -311,16 +318,23 @@
 
 	ByteIOContext ByteIOCxt;
 
-	// Time of when pause started, in seconds since VM started
-	volatile boost::uint64_t m_time_of_pause;
-
-	bool m_start_onbuffer;
-
 	// Decoder buffer
 	boost::uint8_t* _decoderBuffer;
 
 	// Current sound handler
 	media::sound_handler* _soundHandler;
+
+	/// Parse a chunk of input
+	/// Currently blocks, ideally should parse as much
+	/// as possible w/out blocking
+	void parseNextChunk();
+
+	/// Input stream
+	//
+	/// This should just be a temporary variable, transferred
+	/// to MediaParser constructor.
+	///
+	std::auto_ptr<tu_file> _inputStream;
 };
 
 
