Index: server/asobj/NetStream.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/NetStream.cpp,v
retrieving revision 1.76
diff -u -r1.76 NetStream.cpp
--- server/asobj/NetStream.cpp	23 Nov 2007 15:14:35 -0000	1.76
+++ server/asobj/NetStream.cpp	4 Dec 2007 12:27:51 -0000
@@ -27,16 +27,18 @@
 
 #include "log.h"
 #ifdef SOUND_GST
-# include "NetStreamGst.h"
+# include "MediaDecoderGst.h"
 #elif defined(USE_FFMPEG)
-# include "NetStreamFfmpeg.h"
+# include "MediaDecoderSdl.h"
 #endif
+
 #include "fn_call.h"
 #include "builtin_function.h"
 #include "GnashException.h"
 #include "NetConnection.h"
 #include "action.h" // for call_method
 #include "render.h"	// for gnash::render::videoFrameFormat()
+#include "sound_handler.h"
 #include "Object.h" // for getObjectInterface
 #include "namedStrings.h"
 #include "movie_root.h"
@@ -67,19 +69,20 @@
 	:
 	as_object(getNetStreamInterface()),
 	_netCon(NULL),
-	m_env(NULL),
-	m_bufferTime(100), // The default size needed to begin playback of media is 100 miliseconds
-	m_videoFrameFormat(gnash::render::videoFrameFormat()),
-	m_newFrameReady(false),
-	m_go(false),
-	m_imageframe(NULL),
-	m_pause(false),
-	m_parser(NULL),
-	m_isFLV(false),
-	m_start_onbuffer(false),
-	inputPos(0),
-	_lastStatus(invalidStatus)
-{
+	_env(NULL),
+	_videoFrameFormat(gnash::render::videoFrameFormat()),
+	_newFrameReady(false),
+	_imageframe(NULL),
+	_pause(false),
+	_startOnbuffer(false),
+	_lastStatus(media::invalidStatus),
+	_currentTimestamp(0),
+	_startTime(0),
+	_pauseTime(0)
+{
+	_buffer.reset(new media::MediaBuffer());
+	_decoder.reset(NULL);
+	_imageframe.reset(NULL);
 }
 
 static as_value
@@ -88,14 +91,7 @@
 
 	boost::intrusive_ptr<NetStream> netstream_obj;
        
-#ifdef SOUND_GST
-	netstream_obj = new NetStreamGst();
-#elif defined(USE_FFMPEG)
-	netstream_obj = new NetStreamFfmpeg();
-#else
 	netstream_obj = new NetStream();
-#endif
-
 
 	if (fn.nargs > 0)
 	{
@@ -291,7 +287,7 @@
 {
 	boost::intrusive_ptr<NetStream> ns = ensureType<NetStream>(fn.this_ptr);
 
-	long ret = ns->bytesLoaded();
+	uint32_t ret = ns->bytesLoaded();
 	return as_value(ret);
 }
 
@@ -301,7 +297,7 @@
 {
 	boost::intrusive_ptr<NetStream> ns = ensureType<NetStream>(fn.this_ptr);
 
-	long ret = ns->bytesTotal();
+	uint32_t ret = ns->bytesTotal();
 	return as_value(ret);
 }
 
@@ -451,8 +447,165 @@
 
 }
 
+NetStream::~NetStream()
+{
+	close();
+}
+
+void NetStream::play(const std::string& source)
+{
+	if (!_netCon) {
+		log_aserror("Trying to play using NetStream without a NetConnection element!");
+		return;
+	}
+
+	// Kill the decoder if it already exists, since it won't work when the
+	// connection/stream is changed.
+	_decoder.reset(NULL);
+
+	// Pass stuff from/to the NetConnection object.
+	if ( !_netCon->openConnection(source) ) {
+		log_error(_("Gnash could not open movie: %s"), source.c_str());
+		setStatus(media::streamNotFound);
+		return;
+	}
+
+	// Create the decoder
+
+#ifdef SOUND_GST
+	_decoder.reset(new media::MediaDecoderGst(_netCon->getStream(), _buffer.get(), (uint16_t)7 /*_vm->getSWFVersion()*/, _videoFrameFormat));
+#elif defined(USE_FFMPEG)
+	_decoder.reset(new media::MediaDecoderSdl(_netCon->getStream(), _buffer.get(), (uint16_t)7 /*_vm->getSWFVersion()*/, _videoFrameFormat));
+#else
+	_decoder.reset(new media::MediaDecoder(_netCon->getStream(), _buffer.get(), (uint16_t)7 /*_vm->getSWFVersion()*/, _videoFrameFormat));
+#endif
+
+	// Connect to the soundhandler
+	media::sound_handler* s = get_sound_handler();
+	if (s) {
+		s->attach_aux_streamer(getAudio, (void*) this);
+	} 
+}
+
+void NetStream::pause(int mode)
+{
+	// -1 : toogle mode
+	// 0 : switch to pause
+	// 1 : switch to play
+	if (mode == -1)
+	{
+		_pause = !_pause;
+	} else {
+		if (mode == 0) _pause = true;
+		else _pause = false;
+	}
+
+	if (_pause && _startTime > 0 && _pauseTime == 0) {
+		_pauseTime = tu_timer::get_ticks();
+	}
+
+	if (!_pause && _startTime != 0 && _pauseTime != 0) {
+		_startTime = tu_timer::get_ticks() - _pauseTime;
+		_pauseTime = 0;
+	}
+
+}
+
+void NetStream::close()
+{
+	_decoder.reset(NULL);
+	_buffer->flush();
+}
+
+uint32_t NetStream::time()
+{
+	return _currentTimestamp;
+}
+
+void NetStream::seek(uint32_t pos)
+{
+	_currentTimestamp = _decoder->seek(pos);
+}
+
+void
+NetStream::refreshVideoFrame()
+{
+
+	// If we're paused or not running, there is no need to do this
+	if (_pause || !_decoder.get()) {
+		return;
+	}
+	
+	// If video decoder setup failed, what do we do?	
+	if (!_decoder->gotVideo()) return;
+
+	// Loop until a good frame is found
+	while(1)
+	{
+		// If the queue is empty we have nothing to do
+		if (_buffer->videoSize() == 0)
+		{
+			_decoder->wakeUp();
+			return;
+		}
+
+		// Get video frame from queue, will have the lowest timestamp
+		// will return NULL if empty(). See multithread_queue::front
+		media::raw_videodata_t* video = _buffer->videoFront();
+
+		// Caclulate the current time
+		uint64_t current_clock;
+		if (_decoder->gotAudio() && get_sound_handler()) {
+			current_clock = _currentTimestamp;
+		} else {
+			if (_startTime == 0) _startTime = tu_timer::get_ticks();
+			current_clock = tu_timer::get_ticks() - _startTime;
+			_currentTimestamp = current_clock;
+		}
+
+		uint64_t video_clock = video->timestamp;
+
+		if (_buffer->getReqBufferTime() *0.5 > _buffer->getBufferTime()) {
+			_decoder->wakeUp();
+		}
+
+		// If the timestamp on the videoframe is smaller than the
+		// current time, we put it in the output image.
+		if (current_clock >= video_clock)
+		{
+			_imageframe.reset(NULL);
+			_imageframe = video->image->clone();
+
+			// Delete the frame from the queue
+			_buffer->videoPop();
+			delete video;
+
+			// A frame is ready for pickup
+			_newFrameReady = true;
+
+		} else {
+			// The timestamp on the first frame in the queue is greater
+			// than the current time, so no need to do anything.
+			return;
+		}
+
+	}
+}
+
+void
+NetStream::advance()
+{
+	// Check if there are any new status messages, and if we should
+	// pass them to a event handler
+	processOnStatusNotifications();
+
+	// Find video frame with the most suited timestamp in the video queue,
+	// and put it in the output image frame.
+	refreshVideoFrame();
+}
+
 void
-NetStream::processStatusNotifications()
+NetStream::processOnStatusNotifications()
 {
 	// TODO: check for System.onStatus too ! use a private getStatusHandler() method for this.
 	as_value status;
@@ -462,42 +615,43 @@
 		return;
 	}
 
-	size_t initialStackSize = m_env->stack_size();
+	if (!_decoder.get()) return;
+
+	size_t initialStackSize = _env->stack_size();
 	if ( initialStackSize > 0 )
 	{
-		log_debug("NetStream environment stack not empty at start of processStatusNotifications");
+		//log_debug("NetStream environment stack not empty at start of processStatusNotifications");
 	}
 
-	StatusCode code;
-	while (1)
+	std::vector<media::StatusCode> eventList = _decoder->getOnStatusEvents();
+
+	for(uint32_t i = 0; i < eventList.size(); ++i)
 	{
-		code = popNextPendingStatusNotification();
-		if ( code == invalidStatus ) break; // no more pending notifications
 
 #ifdef GNASH_DEBUG_STATUS
 		log_debug(" Invoking onStatus(%s)", getStatusCodeInfo(code).first);
 #endif
 
 		// TODO: optimize by reusing the same as_object ?
-		boost::intrusive_ptr<as_object> o = getStatusObject(code);
-
-		m_env->push_val(as_value(o.get()));
-		call_method(status, m_env, this, 1, m_env->get_top_index() );
+		boost::intrusive_ptr<as_object> o = getStatusObject(eventList[i]);
 
+		_env->push_val(as_value(o.get()));
+		call_method(status, _env, this, 1, _env->get_top_index() );
+		
 	}
 
 	// clear the stack after method execution
-	if ( m_env->stack_size() > initialStackSize )
+	if ( _env->stack_size() > initialStackSize )
 	{
-		log_debug("NetStream environment stack not empty at end of processStatusNotifications");
-		m_env->drop(m_env->stack_size() - initialStackSize);
+		//log_debug("NetStream environment stack not empty at end of processStatusNotifications");
+		_env->drop(_env->stack_size() - initialStackSize);
 	}
 
 
 }
 
 void
-NetStream::setStatus(StatusCode status)
+NetStream::setStatus(media::StatusCode status)
 {
 	// Get a lock to avoid messing with statuses while processing them
 	boost::mutex::scoped_lock lock(statusMutex);
@@ -513,38 +667,46 @@
 NetStream::setBufferTime(uint32_t time)
 {
 	// The argument is in milliseconds,
-	m_bufferTime = time;
+	//if (_buffer.get()) {
+		_buffer->setBufferTime(time);
+	//}
 }
 
-long
+uint32_t NetStream::bufferTime()
+{
+	// Must return length of requested buffer in seconds
+	return _buffer->getReqBufferTime();
+}
+
+uint32_t
 NetStream::bytesLoaded()
 {
-	if (_netCon == NULL) return 0;
-	return _netCon->getBytesLoaded();
+	if (_decoder.get() == NULL) return 0;
+	return _decoder->getBytesLoaded();
 }
 
-long
+uint32_t
 NetStream::bytesTotal()
 {
-	if (_netCon == NULL) return 0;
-	return _netCon->getBytesTotal();
+	if (_decoder.get() == NULL) return 0;
+	return _decoder->getBytesTotal();
 }
 
-long
+uint32_t
 NetStream::bufferLength()
 {
-	if (m_parser.get() == NULL) return 0;
+	if (_decoder.get() == NULL) return 0;
 
-	// m_parser will lock a mutex
-	// FLVParser::getBufferLength returns milliseconds already
-	return m_parser->getBufferLength();
+	// _decoder will lock a mutex
+	// MediaBuffer::getBufferLength returns milliseconds already
+	return _buffer->getBufferTime();
 }
 
 bool
 NetStream::newFrameReady()
 {
-	if (m_newFrameReady) {
-		m_newFrameReady = false;
+	if (_newFrameReady) {
+		_newFrameReady = false;
 		return true;
 	} else {
 		return false;
@@ -552,44 +714,42 @@
 }
 
 std::auto_ptr<image::image_base>
-NetStream::get_video()
+NetStream::getVideo()
 {
-	boost::mutex::scoped_lock lock(image_mutex);
-
-	if (!m_imageframe) return std::auto_ptr<image::image_base>(0);
+	if (!_imageframe.get()) return std::auto_ptr<image::image_base>(0);
 
 	// TODO: inspect if we could return m_imageframe directly...
-	return m_imageframe->clone();
+	return _imageframe->clone();
 }
 
 std::pair<const char*, const char*>
-NetStream::getStatusCodeInfo(StatusCode code)
+NetStream::getStatusCodeInfo(media::StatusCode code)
 {
 	switch (code)
 	{
 	
-		case bufferEmpty:
+		case media::bufferEmpty:
 			return std::pair<const char*, const char*>("NetStream.Buffer.Empty", "status");
 
-		case bufferFull:
+		case media::bufferFull:
 			return std::pair<const char*, const char*>("NetStream.Buffer.Full", "status");
 
-		case bufferFlush:
+		case media::bufferFlush:
 			return std::pair<const char*, const char*>("NetStream.Buffer.Flush", "status");
 
-		case playStart:
+		case media::playStart:
 			return std::pair<const char*, const char*>("NetStream.Play.Start", "status");
 
-		case playStop:
+		case media::playStop:
 			return std::pair<const char*, const char*>("NetStream.Play.Stop", "status");
 
-		case seekNotify:
+		case media::seekNotify:
 			return std::pair<const char*, const char*>("NetStream.Seek.Notify", "status");
 
-		case streamNotFound:
+		case media::streamNotFound:
 			return std::pair<const char*, const char*>("NetStream.Play.StreamNotFound", "error");
 
-		case invalidTime:
+		case media::invalidTime:
 			return std::pair<const char*, const char*>("NetStream.Seek.InvalidTime", "error");
 
 		default:
@@ -598,7 +758,7 @@
 }
 
 boost::intrusive_ptr<as_object>
-NetStream::getStatusObject(StatusCode code)
+NetStream::getStatusObject(media::StatusCode code)
 {
 	// code, level
 	std::pair<const char*, const char*> info = getStatusCodeInfo(code);
@@ -610,16 +770,16 @@
 	return o;
 }
 
-NetStream::StatusCode
+media::StatusCode
 NetStream::popNextPendingStatusNotification()
 {
 	// Get an exclusive lock on the queue
 	boost::mutex::scoped_lock lock(statusMutex);
 
 	// No queued statuses to notify ...
-	if ( _statusQueue.empty() ) return invalidStatus;
+	if ( _statusQueue.empty() ) return media::invalidStatus;
 
-	StatusCode nextCode = _statusQueue.front();
+	media::StatusCode nextCode = _statusQueue.front();
 	_statusQueue.pop_front();
 	return nextCode;
 }
@@ -640,13 +800,53 @@
 
 	if ( _netCon ) _netCon->setReachable();
 
-	if ( m_statusHandler ) m_statusHandler->setReachable();
+	if ( _statusHandler ) _statusHandler->setReachable();
 
-	if ( m_env ) m_env->markReachableResources();
+	if ( _env ) _env->markReachableResources();
 
 	// Invoke generic as_object marker
 	markAsObjectReachable();
 }
 #endif // GNASH_USE_GC
 
+// audio callback is running in sound handler thread
+bool NetStream::getAudio(void *owner, uint8_t *stream, int len)
+{
+
+	NetStream* ns = static_cast<NetStream*>(owner);
+
+	if (!ns->_decoder.get()) return false;
+
+	if (ns->_pause)
+	{
+		return true;
+	}
+
+	while (len > 0 && ns->_buffer->audioSize() > 0)
+	{
+		media::raw_audiodata_t* samples = ns->_buffer->audioFront();
+
+		int n = imin(samples->m_size, len);
+		memcpy(stream, samples->m_ptr, n);
+		stream += n;
+		samples->m_ptr += n;
+		samples->m_size -= n;
+		len -= n;
+
+		ns->_currentTimestamp = samples->m_pts;
+
+		if (samples->m_size == 0)
+		{
+			ns->_buffer->audioPop();
+			delete samples;
+		}
+
+	}
+
+	if (ns->_buffer->getReqBufferTime() *0.5 > ns->_buffer->getBufferTime()) ns->_decoder->wakeUp();
+
+	return true;
+}
+
 } // end of gnash namespace
+
