gnash-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Gnash-commit] gnash ChangeLog libmedia/FLVParser.cpp libmedia...


From: Sandro Santilli
Subject: [Gnash-commit] gnash ChangeLog libmedia/FLVParser.cpp libmedia...
Date: Mon, 16 Jun 2008 09:05:03 +0000

CVSROOT:        /sources/gnash
Module name:    gnash
Changes by:     Sandro Santilli <strk>  08/06/16 09:05:03

Modified files:
        .              : ChangeLog 
        libmedia       : FLVParser.cpp FLVParser.h MediaParser.cpp 
                         MediaParser.h 
        libmedia/ffmpeg: MediaParserFfmpeg.cpp MediaParserFfmpeg.h 
        server/asobj   : NetStream.cpp NetStreamFfmpeg.cpp 
                         NetStreamFfmpeg.h 

Log message:
        Load and parse media files in a separate thread (with compile-time 
opt-out)
        and other cleanups

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/ChangeLog?cvsroot=gnash&r1=1.6937&r2=1.6938
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/FLVParser.cpp?cvsroot=gnash&r1=1.17&r2=1.18
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/FLVParser.h?cvsroot=gnash&r1=1.16&r2=1.17
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/MediaParser.cpp?cvsroot=gnash&r1=1.2&r2=1.3
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/MediaParser.h?cvsroot=gnash&r1=1.24&r2=1.25
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/ffmpeg/MediaParserFfmpeg.cpp?cvsroot=gnash&r1=1.13&r2=1.14
http://cvs.savannah.gnu.org/viewcvs/gnash/libmedia/ffmpeg/MediaParserFfmpeg.h?cvsroot=gnash&r1=1.9&r2=1.10
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/NetStream.cpp?cvsroot=gnash&r1=1.98&r2=1.99
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/NetStreamFfmpeg.cpp?cvsroot=gnash&r1=1.150&r2=1.151
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/NetStreamFfmpeg.h?cvsroot=gnash&r1=1.75&r2=1.76

Patches:
Index: ChangeLog
===================================================================
RCS file: /sources/gnash/gnash/ChangeLog,v
retrieving revision 1.6937
retrieving revision 1.6938
diff -u -b -r1.6937 -r1.6938
--- ChangeLog   16 Jun 2008 08:50:21 -0000      1.6937
+++ ChangeLog   16 Jun 2008 09:05:00 -0000      1.6938
@@ -1,3 +1,17 @@
+2008-06-16 Sandro Santilli <address@hidden>
+
+       * libmedia/MediaParser.{cpp,h}: move more functionality to
+         the base class: management of encoded audio/video queues and
+         threaded loading (compile-time opt-out available);
+         change seek() signature to allow for InvalidTime return.
+       * libmedia/FLVParser.{cpp,h}: adapt to MediaParser changes.
+       * libmedia/ffmpeg/MediaParserFfmpeg.{cpp,h}: adapt to MediaParser
+         changes.
+       * server/asobj/NetStream.cpp: notify buffer time to parser, when
+         available (it'll take care of it).
+       * server/asobj/NetStreamFfmpeg.{cpp,h}: push time *ahead* to decoded
+         audio queue.
+
 2008-06-15 Benjamin Wolsey <address@hidden>
 
        * server/vm/ASHandlers.{cpp,h}: use std::string instead of C strings

Index: libmedia/FLVParser.cpp
===================================================================
RCS file: /sources/gnash/gnash/libmedia/FLVParser.cpp,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -b -r1.17 -r1.18
--- libmedia/FLVParser.cpp      10 Jun 2008 06:12:11 -0000      1.17
+++ libmedia/FLVParser.cpp      16 Jun 2008 09:05:01 -0000      1.18
@@ -43,6 +43,7 @@
        :
        MediaParser(lt),
        _lastParsedPosition(0),
+       _bytesLoaded(0),
        _nextAudioFrame(0),
        _nextVideoFrame(0),
        _audio(false),
@@ -58,27 +59,55 @@
 }
 
 
-boost::uint32_t
-FLVParser::seek(boost::uint32_t time)
+// would be called by main thread
+bool
+FLVParser::seek(boost::uint32_t& time)
 {
-       if ( time ) // seek to 0 should be fine..
+       //GNASH_REPORT_FUNCTION;
+
+       if ( time )
        {
-               LOG_ONCE( log_unimpl("%s", __PRETTY_FUNCTION__) );
+               LOG_ONCE( log_unimpl("FLVParser::seek with non-zero arg") );
+               return false;
        }
 
-       // In particular, what to do if there's no frames in queue ?
-       // just seek to the the later available first timestamp
-       _audioFrames.clear();
-       _videoFrames.clear();
-       _stream->seek(0);
-       _parsingComplete=false;
-       return 0;
+       boost::mutex::scoped_lock streamLock(_streamMutex);
+       // we might obtain this lock while the parser is pushing the last
+       // encoded frame on the queue, or while it is waiting on the wakeup
+       // condition
+
+       // Setting _seekRequested to true will make the parser thread
+       // take care of cleaning up the buffers before going on with
+       // parsing, thus fixing the case in which streamLock was obtained
+       // while the parser was pushing to queue
+       _seekRequested = true;
+
+       _lastParsedPosition=9; // 9 is FLV header size...
+       _parsingComplete=false; // or NetStream will send the Play.Stop event...
+
+       time = 0; // this is the only place we want to go to
+
+       // Finally, in case the parser is now waiting on the wakeup condition,
+       // we wake it up.
+       //
+       // WARNING: a race condition is pending here:
+       // If we notify *before* the parser waits, it'll still wait undefinitely
+       // here, or we might notify the wakeup before the thread
+       // starts waiting on it (it'll have _qMutex locked when this happens).
+       // 
+       //boost::mutex::scoped_lock qLock(_qMutex);
+
+       // we might just clean the buffers here..
+       _parserThreadWakeup.notify_all();
+
+       return true;
 }
 
+// would be called by parser thread
 bool
 FLVParser::parseNextChunk()
 {
-       static const int tagsPerChunk=10;
+       static const int tagsPerChunk=1;
        for (int i=0; i<tagsPerChunk; ++i)
        {
                if ( ! parseNextTag() ) return false;
@@ -86,10 +115,22 @@
        return true;
 }
 
+// would be called by parser thread
 bool FLVParser::parseNextTag()
 {
+       // lock the stream while reading from it, so actionscript
+       // won't mess with the parser on seek  or on getBytesLoaded
+       boost::mutex::scoped_lock streamLock(_streamMutex);
+
        if ( _parsingComplete ) return false;
 
+       if ( _seekRequested )
+       {
+               clearBuffers();
+               _seekRequested = false;
+       }
+
+
        // Seek to next frame and skip the size of the last tag
        if ( _stream->seek(_lastParsedPosition+4) )
        {
@@ -116,6 +157,11 @@
 
        _lastParsedPosition += 15 + bodyLength;
 
+       if ( _lastParsedPosition > _bytesLoaded ) {
+               boost::mutex::scoped_lock lock(_bytesLoadedMutex);
+               _bytesLoaded = _lastParsedPosition;
+       }
+
        // check for empty tag
        if (bodyLength == 0) return true;
 
@@ -130,7 +176,16 @@
        {
                std::auto_ptr<EncodedAudioFrame> frame = 
readAudioFrame(bodyLength-1, timestamp);
                if ( ! frame.get() ) { log_error("could not read audio 
frame?"); }
-               else _audioFrames.push_back(frame.release());
+               else
+               {
+                       // Release the stream lock 
+                       // *before* pushing the frame as that 
+                       // might block us waiting for buffers flush
+                       // the _qMutex...
+                       // We've done using the stream for this tag parsing 
anyway
+                       streamLock.unlock();
+                       pushEncodedAudioFrame(frame);
+               }
 
                // If this is the first audioframe no info about the
                // audio format has been noted, so we do that now
@@ -149,9 +204,10 @@
                        _audioInfo.reset( new AudioInfo((tag[11] & 0xf0) >> 4, 
samplerate, samplesize, (tag[11] & 0x01) >> 0, 0, FLASH) );
                }
 
-
+               return true;
        }
-       else if (tag[0] == VIDEO_TAG)
+
+       if (tag[0] == VIDEO_TAG)
        {
                bool isKeyFrame = (tag[11] & 0xf0) >> 4;
                UNUSED(isKeyFrame); // may be used for building seekable 
indexes...
@@ -159,8 +215,11 @@
                size_t dataPosition = _stream->tell();
 
                std::auto_ptr<EncodedVideoFrame> frame = 
readVideoFrame(bodyLength-1, timestamp);
-               if ( ! frame.get() ) { log_error("could not read video 
frame?"); }
-               else _videoFrames.push_back(frame.release());
+               if ( ! frame.get() )
+               {
+                       log_error("could not read video frame?");
+                       return true;
+               }
 
                // If this is the first videoframe no info about the
                // video format has been noted, so we do that now
@@ -179,7 +238,7 @@
 
                                size_t bkpos = _stream->tell();
                                if ( _stream->seek(dataPosition) ) {
-                                       log_error(" Couldn't seek to VideoTag 
data position");
+                                       log_error(" Couldn't seek to VideoTag 
data position -- should never happen, as we just read that!");
                                        _parsingComplete=true;
                                        return false;
                                }
@@ -235,7 +294,19 @@
                        _videoInfo.reset( new VideoInfo(codec, width, height, 0 
/*frameRate*/, 0 /*duration*/, FLASH /*codec type*/) );
                }
 
-       } else if (tag[0] == META_TAG) {
+               // Release the stream lock 
+               // *before* pushing the frame as that 
+               // might block us waiting for buffers flush
+               // the _qMutex...
+               streamLock.unlock();
+               pushEncodedVideoFrame(frame);
+
+               return true;
+
+       }
+
+       if (tag[0] == META_TAG)
+       {
                LOG_ONCE( log_unimpl("FLV MetaTag parser") );
                // Extract information from the meta tag
                /*_stream->seek(_lastParsedPosition+16);
@@ -251,15 +322,18 @@
                amf::AMF* amfParser = new amf::AMF();
                amfParser->parseAMF(metaTag);*/
 
-       } else {
+               return true;
+
+       }
+
                log_error("Unknown FLV tag type %d", tag[0]);
                //_parsingComplete = true;
                //return false;
-       }
 
        return true;
 }
 
+// would be called by MAIN thread
 bool FLVParser::parseHeader()
 {
        // seek to the begining of the file
@@ -272,7 +346,7 @@
                log_error("FLVParser::parseHeader: couldn't read 9 bytes of 
header");
                return false;
        }
-       _lastParsedPosition = 9;
+       _lastParsedPosition = _bytesLoaded = 9;
 
        // Check if this is really a FLV file
        if (header[0] != 'F' || header[1] != 'L' || header[2] != 'V') return 
false;
@@ -309,10 +383,12 @@
 boost::uint64_t
 FLVParser::getBytesLoaded() const
 {
-       return _lastParsedPosition;
-       //return _stream->getBytesLoaded();
+       boost::mutex::scoped_lock lock(_bytesLoadedMutex);
+       //log_debug("FLVParser::getBytesLoaded returning %d/%d", _bytesLoaded, 
_stream->size()); // _stream->size would need mutex-protection..
+       return _bytesLoaded;
 }
 
+// would be called by parser thread
 /*private*/
 std::auto_ptr<EncodedAudioFrame>
 FLVParser::readAudioFrame(boost::uint32_t dataSize, boost::uint32_t timestamp)
@@ -339,6 +415,7 @@
        return frame;
 }
 
+// would be called by parser thread
 /*private*/
 std::auto_ptr<EncodedVideoFrame>
 FLVParser::readVideoFrame(boost::uint32_t dataSize, boost::uint32_t timestamp)

Index: libmedia/FLVParser.h
===================================================================
RCS file: /sources/gnash/gnash/libmedia/FLVParser.h,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -b -r1.16 -r1.17
--- libmedia/FLVParser.h        9 Jun 2008 14:31:53 -0000       1.16
+++ libmedia/FLVParser.h        16 Jun 2008 09:05:01 -0000      1.17
@@ -29,6 +29,8 @@
 #include <vector>
 #include <memory>
 
+#include <boost/thread/mutex.hpp>
+
 namespace gnash {
 namespace media {
 
@@ -175,18 +177,8 @@
        /// Kills the parser...
        ~FLVParser();
 
-       /// \brief
-       /// Seeks to the closest possible position the given position,
-       /// and returns the new position.
-       //
-       ///
-       /// TODO: throw something for sending Seek.InvalidTime ?
-       ///       (triggered by seeks beyond the end of video or beyond what's
-       ///        downloaded so far)
-       ///
-       boost::uint32_t seek(boost::uint32_t);
-
-       virtual bool parseNextChunk();
+       // see dox in MediaParser.h
+       virtual bool seek(boost::uint32_t&);
 
        /// Parses next tag from the file
        //
@@ -200,6 +192,8 @@
 
 private:
 
+       virtual bool parseNextChunk();
+
        /// Parses the header of the file
        bool parseHeader();
 
@@ -207,8 +201,20 @@
        inline boost::uint32_t getUInt24(boost::uint8_t* in);
 
        /// The position where the parsing should continue from.
+       /// Will be reset on seek, and will be protected by the _streamMutex
        boost::uint64_t _lastParsedPosition;
 
+       /// On seek, this flag will be set, while holding a lock on 
_streamMutex.
+       /// The parser, when obtained a lock on _streamMutex, will check this
+       /// flag, if found to be true will clear the buffers and reset to false.
+       bool _seekRequested;
+
+       /// Number of bytes loaded
+       boost::uint64_t _bytesLoaded;
+
+       /// Mutex protecting _bytesLoaded (read by main, set by parser)
+       mutable boost::mutex _bytesLoadedMutex;
+
        /// Audio frame cursor position 
        //
        /// This is the video frame number that will

Index: libmedia/MediaParser.cpp
===================================================================
RCS file: /sources/gnash/gnash/libmedia/MediaParser.cpp,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -b -r1.2 -r1.3
--- libmedia/MediaParser.cpp    10 Jun 2008 06:12:12 -0000      1.2
+++ libmedia/MediaParser.cpp    16 Jun 2008 09:05:01 -0000      1.3
@@ -21,6 +21,8 @@
 #include "MediaParser.h"
 #include "log.h"
 
+#include <boost/bind.hpp>
+
 namespace gnash {
 namespace media {
 
@@ -29,15 +31,31 @@
        _isAudioMp3(false),
        _isAudioNellymoser(false),
        _stream(stream),
-       //_stream(new LoadThread),
-       _parsingComplete(false)
-{
-       //_stream->setStream(stream);
+       _parsingComplete(false),
+       _parserThread(0),
+       _parserThreadStartBarrier(2),
+       _parserThreadKillRequested(false),
+       _bufferTime(100) // 100 ms 
+{
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       log_debug("Starting MediaParser thread");
+       _parserThread.reset( new boost::thread(boost::bind(parserLoopStarter, 
this)) );
+       _parserThreadStartBarrier.wait();
+#endif
 }
 
 boost::uint64_t
 MediaParser::getBufferLength() const
 {
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       boost::mutex::scoped_lock lock(_qMutex);
+#endif
+       return getBufferLengthNoLock();
+}
+
+boost::uint64_t
+MediaParser::getBufferLengthNoLock() const
+{
        bool hasVideo = _videoInfo.get();
        bool hasAudio = _audioInfo.get();
 
@@ -77,11 +95,16 @@
 const EncodedVideoFrame*
 MediaParser::peekNextVideoFrame() const
 {
-       if (_videoFrames.empty())
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       boost::mutex::scoped_lock lock(_qMutex);
+#else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
        {
-               log_debug("MediaParser::peekNextVideoFrame: no more video 
frames here...");
-               return 0;
+               const_cast<MediaParser*>(this)->parseNextChunk();
        }
+#endif
+
+       if (!_videoInfo.get() || _videoFrames.empty()) return 0;
        return _videoFrames.front();
 }
 
@@ -97,20 +120,46 @@
 std::auto_ptr<EncodedVideoFrame>
 MediaParser::nextVideoFrame()
 {
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       boost::mutex::scoped_lock lock(_qMutex);
+#else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
+       {
+               const_cast<MediaParser*>(this)->parseNextChunk();
+       }
+#endif
+
        std::auto_ptr<EncodedVideoFrame> ret;
        if (_videoFrames.empty()) return ret;
        ret.reset(_videoFrames.front());
        _videoFrames.pop_front();
+#ifdef GNASH_DEBUG_MEDIAPARSER
+       log_debug("nextVideoFrame: waking up parser (in case it was sleeping)");
+#endif // GNASH_DEBUG_MEDIAPARSER
+       _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, 
SHOULDN'T WE HOLD A LoCK HERE?
        return ret;
 }
 
 std::auto_ptr<EncodedAudioFrame>
 MediaParser::nextAudioFrame()
 {
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       boost::mutex::scoped_lock lock(_qMutex);
+#else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
+       {
+               const_cast<MediaParser*>(this)->parseNextChunk();
+       }
+#endif
+
        std::auto_ptr<EncodedAudioFrame> ret;
        if (_audioFrames.empty()) return ret;
        ret.reset(_audioFrames.front());
        _audioFrames.pop_front();
+#ifdef GNASH_DEBUG_MEDIAPARSER
+       log_debug("nextAudioFrame: waking up parser (in case it was sleeping)");
+#endif // GNASH_DEBUG_MEDIAPARSER
+       _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, 
SHOULDN'T WE HOLD A LoCK HERE?
        return ret;
 }
 
@@ -126,12 +175,26 @@
 const EncodedAudioFrame*
 MediaParser::peekNextAudioFrame() const
 {
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       boost::mutex::scoped_lock lock(_qMutex);
+#else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
+       {
+               const_cast<MediaParser*>(this)->parseNextChunk();
+       }
+#endif
        if (!_audioInfo.get() || _audioFrames.empty()) return 0;
        return _audioFrames.front();
 }
 
 MediaParser::~MediaParser()
 {
+       if ( _parserThread.get() )
+       {
+               requestParserThreadKill();
+               _parserThread->join();
+       }
+
        for (VideoFrames::iterator i=_videoFrames.begin(),
                e=_videoFrames.end(); i!=e; ++i)
        {
@@ -145,6 +208,135 @@
        }
 }
 
+void
+MediaParser::clearBuffers()
+{
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       boost::mutex::scoped_lock lock(_qMutex);
+#endif
+
+       for (VideoFrames::iterator i=_videoFrames.begin(),
+               e=_videoFrames.end(); i!=e; ++i)
+       {
+               delete (*i);
+       }
+
+       for (AudioFrames::iterator i=_audioFrames.begin(),
+               e=_audioFrames.end(); i!=e; ++i)
+       {
+               delete (*i);
+       }
+
+       _audioFrames.clear();
+       _videoFrames.clear();
+
+       _parserThreadWakeup.notify_all(); // wake it up, to refill the buffer
+}
+
+void
+MediaParser::pushEncodedAudioFrame(std::auto_ptr<EncodedAudioFrame> frame)
+{
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       boost::mutex::scoped_lock lock(_qMutex);
+#endif
+
+       // If last frame on queue has a timestamp > then this one, that's 
either due
+       // to seek-back (most commonly) or a wierdly encoded media file.
+       // In any case, we'll flush the queue and restart from the new timestamp
+       if ( ! _audioFrames.empty() && _audioFrames.back()->timestamp > 
frame->timestamp )
+       {
+               log_debug("Timestamp of last audio frame in queue (%d) "
+                       "greater then timestamp in the frame being "
+                       "pushed to it (%d). Flushing %d queue elements.",
+                       _audioFrames.back()->timestamp, frame->timestamp,
+                       _audioFrames.size());
+               for (AudioFrames::iterator i=_audioFrames.begin(),
+                       e=_audioFrames.end(); i!=e; ++i)
+               {
+                       delete (*i);
+               }
+               _audioFrames.clear();
+       }
+       
+       _audioFrames.push_back(frame.release());
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       waitIfNeeded(lock); // if the push reaches a "buffer full" condition, 
wait to be waken up
+#endif
+}
+
+void
+MediaParser::pushEncodedVideoFrame(std::auto_ptr<EncodedVideoFrame> frame)
+{
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       boost::mutex::scoped_lock lock(_qMutex);
+#endif
+
+       // If last frame on queue has a timestamp > then this one, that's 
either due
+       // to seek-back (most commonly) or a wierdly encoded media file.
+       // In any case, we'll flush the queue and restart from the new timestamp
+       if ( ! _videoFrames.empty() && _videoFrames.back()->timestamp() > 
frame->timestamp() )
+       {
+               log_debug("Timestamp of last video frame in queue (%d) "
+                       "greater then timestamp in the frame being "
+                       "pushed to it (%d). Flushing %d queue elements.",
+                       _videoFrames.back()->timestamp(), frame->timestamp(),
+                       _videoFrames.size());
+               for (VideoFrames::iterator i=_videoFrames.begin(),
+                       e=_videoFrames.end(); i!=e; ++i)
+               {
+                       delete (*i);
+               }
+               _videoFrames.clear();
+       }
+
+       _videoFrames.push_back(frame.release());
+#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       waitIfNeeded(lock); // if the push reaches a "buffer full" condition, 
wait to be waken up
+#endif
+}
+
+void
+MediaParser::waitIfNeeded(boost::mutex::scoped_lock& lock) 
+{
+       //  We hold a lock on the queue here...
+       bool pc=parsingCompleted();
+       bool bf=bufferFull();
+       if ( pc||bf ) // TODO: or seekRequested ?
+       {
+#ifdef GNASH_DEBUG_MEDIAPARSER
+               log_debug("Parser thread waiting on wakeup lock, 
parsingComplete=%d, bufferFull=%d", pc, bf);
+#endif // GNASH_DEBUG_MEDIAPARSER
+               _parserThreadWakeup.wait(lock);
+#ifdef GNASH_DEBUG_MEDIAPARSER
+               log_debug("Parser thread finished waiting on wakeup lock");
+#endif // GNASH_DEBUG_MEDIAPARSER
+       }
+}
+
+bool
+MediaParser::bufferFull() const
+{
+       // Callers are expected to hold a lock on _qMutex
+       int bl = getBufferLengthNoLock();
+       int bt = getBufferTime();
+#ifdef GNASH_DEBUG_MEDIAPARSER
+       log_debug("bufferFull: %d/%d", bl, bt);
+#endif // GNASH_DEBUG_MEDIAPARSER
+       return bl >= bt;
+}
+
+void
+MediaParser::parserLoop()
+{
+       _parserThreadStartBarrier.wait();
+       while (!parserThreadKillRequested())
+       {
+               parseNextChunk();
+               usleep(100); // no rush....
+       }
+}
+
+
 } // end of gnash::media namespace
 } // end of gnash namespace
 

Index: libmedia/MediaParser.h
===================================================================
RCS file: /sources/gnash/gnash/libmedia/MediaParser.h,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -b -r1.24 -r1.25
--- libmedia/MediaParser.h      10 Jun 2008 06:12:12 -0000      1.24
+++ libmedia/MediaParser.h      16 Jun 2008 09:05:02 -0000      1.25
@@ -28,9 +28,16 @@
 #include "dsodefs.h" // DSOEXPORT
 
 #include <boost/scoped_array.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/condition.hpp>
+#include <boost/thread/barrier.hpp>
 #include <memory>
 #include <deque>
 
+// Undefine this to load/parse media files in main thread
+#define LOAD_MEDIA_IN_A_SEPARATE_THREAD 1
+
+
 namespace gnash {
 namespace media {
 
@@ -235,6 +242,22 @@
        //
        virtual ~MediaParser();
 
+       /// \brief
+       /// Seeks to the closest possible position the given position,
+       /// and returns the new position.
+       //
+       ///
+       /// TODO: throw something for sending Seek.InvalidTime ?
+       ///       (triggered by seeks beyond the end of video or beyond what's
+       ///        downloaded so far)
+       ///
+       /// @param time input/output parameter, input requests a time, output
+       ///        return the actual time seeked to.
+       /// 
+       /// @return true if the seek was valid, false otherwise
+       ///
+       virtual bool seek(boost::uint32_t& time)=0;
+
        /// Returns mininum length of available buffers in milliseconds
        //
        /// TODO: FIXME: NOTE: this is currently used by NetStream.bufferLength
@@ -246,6 +269,20 @@
        ///
        DSOEXPORT boost::uint64_t getBufferLength() const;
 
+       /// Return the time we want the parser thread to maintain in the buffer
+       DSOEXPORT boost::uint64_t getBufferTime() const
+       {
+               boost::mutex::scoped_lock lock(_bufferTimeMutex);
+               return _bufferTime;
+       }
+
+       /// Set the time we want the parser thread to maintain in the buffer
+       DSOEXPORT void setBufferTime(boost::uint64_t t)
+       {
+               boost::mutex::scoped_lock lock(_bufferTimeMutex);
+               _bufferTime=t;
+       }
+
        /// Get timestamp of the video frame which would be returned on 
nextVideoFrame
        //
        /// @return false if there no video frame left
@@ -308,51 +345,13 @@
        ///
        AudioInfo* getAudioInfo() { return _audioInfo.get(); }
 
-       /// Seeks to the closest possible position the given position.
-       //
-       /// Valid seekable position are constrained by key-frames when
-       /// video data is available. Actual seek position should be always
-       /// less of equal the requested one.
-       ///
-       /// @return the position the seek reached
-       ///
-       virtual boost::uint32_t seek(boost::uint32_t) { return 0; }
-
-       /// Returns the framedelay from the last to the current
-       /// audioframe in milliseconds. This is used for framerate.
-       //
-       /// @return the diff between the current and last frame
-       ///
-       //virtual boost::uint32_t audioFrameDelay() { return 0; }
-
-       /// Returns the framedelay from the last to the current
-       /// videoframe in milliseconds. 
-       //
-       /// @return the diff between the current and last frame
-       ///
-       //virtual boost::uint32_t videoFrameDelay() { return 0; }
-
-       /// Returns the framerate of the video
-       //
-       /// @return the framerate of the video
-       ///
-       //virtual boost::uint16_t videoFrameRate() { return 0; }
-
-       /// Returns the last parsed position in the file in bytes
-       //virtual boost::uint32_t getLastParsedPos() { return 0; }
-
-       /// Parse next input chunk
-       //
-       /// Returns true if something was parsed, false otherwise.
-       /// See parsingCompleted().
-       ///
-       virtual bool parseNextChunk() { return false; }
-
        /// Return true of parsing is completed
        //
        /// If this function returns true, any call to nextVideoFrame()
        /// or nextAudioFrame() will always return NULL
        ///
+       /// TODO: make thread-safe
+       ///
        bool parsingCompleted() const { return _parsingComplete; }
 
        /// Return number of bytes parsed so far
@@ -364,22 +363,28 @@
                return _stream->size();
        }
 
+       /// Parse next chunk of input
+       //
+       /// The implementations are required to parse a small chunk
+       /// of input, so to avoid blocking too much if parsing conditions
+       /// change (ie: seek or destruction requested)
+       ///
+       /// When LOAD_MEDIA_IN_A_SEPARATE_THREAD is defined, this should
+       /// never be called by users (consider protected).
+       ///
+       virtual bool parseNextChunk()=0;
+
 protected:
 
-       typedef std::deque<EncodedVideoFrame*> VideoFrames;
-       typedef std::deque<EncodedAudioFrame*> AudioFrames;
 
-       /// Queue of video frames (the video buffer)
-       //
-       /// Elements owned by this class.
-       ///
-       VideoFrames _videoFrames;
+       /// Clear the a/v buffers
+       void clearBuffers();
 
-       /// Queue of audio frames (the audio buffer)
-       //
-       /// Elements owned by this class.
-       ///
-       AudioFrames _audioFrames;
+       /// Push an encoded audio frame to buffer, will wait on a condition if 
buffer is full
+       void pushEncodedAudioFrame(std::auto_ptr<EncodedAudioFrame> frame);
+
+       /// Push an encoded video frame to buffer, will wait on a condition if 
buffer is full
+       void pushEncodedVideoFrame(std::auto_ptr<EncodedVideoFrame> frame);
 
        /// Return pointer to next encoded video frame in buffer
        //
@@ -413,17 +418,99 @@
 
        /// The stream used to access the file
        std::auto_ptr<IOChannel> _stream;
+       mutable boost::mutex _streamMutex;
 
        /// Whether the parsing is complete or not
        bool _parsingComplete;
 
+       static void parserLoopStarter(MediaParser* mp)
+       {
+               mp->parserLoop();
+       }
+
+       /// The parser loop runs in a separate thread
+       /// and calls parseNextChunk until killed.
+       ///
+       /// parseNextChunk is expected to push encoded frames
+       /// on the queue, which may trigger the thread to be
+       /// put to sleep when queues are full or parsing
+       /// was completed.
+       ///
+       void parserLoop();
+
+       bool parserThreadKillRequested() const
+       {
+               boost::mutex::scoped_lock lock(_parserThreadKillRequestMutex);
+               return _parserThreadKillRequested;
+       }
+
+       boost::uint64_t _bufferTime;
+       mutable boost::mutex _bufferTimeMutex;
+
+       std::auto_ptr<boost::thread> _parserThread;
+       boost::barrier _parserThreadStartBarrier;
+       mutable boost::mutex _parserThreadKillRequestMutex;
+       bool _parserThreadKillRequested;
+       boost::condition _parserThreadWakeup;
+
+       bool _seekRequest;
+       mutable boost::mutex _seekRequestMutex;
+
+       bool seekRequested() const;
+
+       void seekBegin();
+       void seekEnd();
+       
+       /// Wait on the _parserThreadWakeup condition if buffer is full
+       /// or parsing was completed.
+       /// 
+       /// Callers *must* pass a locked lock on _qMutex
+       ///
+       void waitIfNeeded(boost::mutex::scoped_lock& qMutexLock);
+
+       void wakeupParserThread();
+
+       /// mutex protecting access to the a/v encoded frames queues
+       mutable boost::mutex _qMutex;
+
 private:
 
+       // Method to check if buffer is full w/out locking the _qMutex
+       // This is intended for being called by waitIfNeeded, which 
+       // is passed a locked lock on _qMutex
+       bool bufferFull() const;
+
+       typedef std::deque<EncodedVideoFrame*> VideoFrames;
+       typedef std::deque<EncodedAudioFrame*> AudioFrames;
+
+       /// Queue of video frames (the video buffer)
+       //
+       /// Elements owned by this class.
+       ///
+       VideoFrames _videoFrames;
+
+       /// Queue of audio frames (the audio buffer)
+       //
+       /// Elements owned by this class.
+       ///
+       AudioFrames _audioFrames;
+
+       void requestParserThreadKill()
+       {
+               boost::mutex::scoped_lock lock(_parserThreadKillRequestMutex);
+               _parserThreadKillRequested=true;
+               _parserThreadWakeup.notify_all();
+       }
+
        /// Return diff between timestamp of last and first audio frame
        boost::uint64_t audioBufferLength() const;
 
        /// Return diff between timestamp of last and first video frame
        boost::uint64_t videoBufferLength() const;
+
+       /// A getBufferLength method not locking the _qMutex (expected to be 
locked by caller already).
+       boost::uint64_t getBufferLengthNoLock() const;
+       
 };
 
 

Index: libmedia/ffmpeg/MediaParserFfmpeg.cpp
===================================================================
RCS file: /sources/gnash/gnash/libmedia/ffmpeg/MediaParserFfmpeg.cpp,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -b -r1.13 -r1.14
--- libmedia/ffmpeg/MediaParserFfmpeg.cpp       10 Jun 2008 06:12:12 -0000      
1.13
+++ libmedia/ffmpeg/MediaParserFfmpeg.cpp       16 Jun 2008 09:05:02 -0000      
1.14
@@ -82,9 +82,12 @@
        return ret;
 }
 
-boost::uint32_t
-MediaParserFfmpeg::seek(boost::uint32_t pos)
+bool
+MediaParserFfmpeg::seek(boost::uint32_t& pos)
 {
+       LOG_ONCE(log_unimpl("MediaParserFfmpeg::seek()"));
+       return false;
+
        log_debug("MediaParserFfmpeg::seek(%d) TESTING", pos);
 
        AVStream* videostream = _formatCtx->streams[_videoStreamIndex];
@@ -150,7 +153,7 @@
        info->dataPosition = pos;
        info->timestamp = timestamp;
 
-       _videoFrames.push_back(info); // takes ownership
+       pushEncodedVideoFrame(info);
 
        return true;
 #endif
@@ -180,7 +183,7 @@
        frame->dataSize = packet.size
        frame->timestamp = timestamp;
 
-       _audioFrames.push_back(frame.release()); // takes ownership
+       pushEncodedAudioFrame(frame); 
 
        return true;
 #endif
@@ -244,13 +247,6 @@
        {
                _lastParsedPosition = curPos;
        }
-       log_debug("parseNextFrame: parsed %d+%d/%d bytes (byteIOCxt: pos:%d, 
buf_ptr:%p, buf_end:%p); "
-               " AVFormatContext: data_offset:%d, cur_ptr:%p,; "
-               "%d video frames, %d audio frames",
-               curPos, _formatCtx->cur_ptr-_formatCtx->cur_pkt.data, 
_stream->size(),
-               _byteIOCxt.pos, (void*)_byteIOCxt.buf_ptr, 
(void*)_byteIOCxt.buf_end,
-               _formatCtx->data_offset, (void*)_formatCtx->cur_ptr,
-               _videoFrames.size(), _audioFrames.size());
 
        return ret;
 }

Index: libmedia/ffmpeg/MediaParserFfmpeg.h
===================================================================
RCS file: /sources/gnash/gnash/libmedia/ffmpeg/MediaParserFfmpeg.h,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -b -r1.9 -r1.10
--- libmedia/ffmpeg/MediaParserFfmpeg.h 9 Jun 2008 14:31:53 -0000       1.9
+++ libmedia/ffmpeg/MediaParserFfmpeg.h 16 Jun 2008 09:05:02 -0000      1.10
@@ -65,7 +65,7 @@
        ~MediaParserFfmpeg();
 
        // See dox in MediaParser.h
-       virtual boost::uint32_t seek(boost::uint32_t);
+       virtual bool seek(boost::uint32_t&);
 
        // See dox in MediaParser.h
        virtual bool parseNextChunk();

Index: server/asobj/NetStream.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/NetStream.cpp,v
retrieving revision 1.98
retrieving revision 1.99
diff -u -b -r1.98 -r1.99
--- server/asobj/NetStream.cpp  6 Jun 2008 21:06:55 -0000       1.98
+++ server/asobj/NetStream.cpp  16 Jun 2008 09:05:02 -0000      1.99
@@ -191,6 +191,9 @@
        {
                time = fn.arg(0).to_number();
        }
+
+       // TODO: don't allow a limit < 100 
+
        ns->setBufferTime(boost::uint32_t(time*1000));
 
        return as_value();
@@ -523,6 +526,7 @@
 {
        // The argument is in milliseconds,
        m_bufferTime = time;
+       if ( m_parser.get() ) m_parser->setBufferTime(time);
 }
 
 long

Index: server/asobj/NetStreamFfmpeg.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/NetStreamFfmpeg.cpp,v
retrieving revision 1.150
retrieving revision 1.151
diff -u -b -r1.150 -r1.151
--- server/asobj/NetStreamFfmpeg.cpp    9 Jun 2008 18:12:57 -0000       1.150
+++ server/asobj/NetStreamFfmpeg.cpp    16 Jun 2008 09:05:02 -0000      1.151
@@ -36,6 +36,7 @@
 #include "VideoDecoder.h"
 #include "AudioDecoder.h"
 #include "MediaHandler.h"
+#include "VM.h"
 
 #include "SystemClock.h"
 #include "gnash.h" // get_sound_handler()
@@ -59,7 +60,7 @@
 //#define GNASH_DEBUG_STATUS
 
 // Define the following macro to have decoding activity  debugged
-//#define GNASH_DEBUG_DECODING
+//#define GNASH_DEBUG_DECODING 1
 
 namespace gnash {
 
@@ -69,22 +70,14 @@
 
        _decoding_state(DEC_NONE),
 
-#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
-       _parserThread(NULL),
-       _parserThreadBarrier(2), // main and decoder threads
-       _parserKillRequest(false),
-#endif
-
-       m_last_video_timestamp(0),
-       m_last_audio_timestamp(0),
-
+       // TODO: if audio is available, use _audioClock instead of SystemClock
+       // as additional source
        _playbackClock(new InterruptableVirtualClock(new SystemClock)),
        _playHead(_playbackClock.get()), 
 
-       m_unqueued_data(NULL),
-
        _soundHandler(get_sound_handler()),
-       _mediaHandler(media::MediaHandler::get())
+       _mediaHandler(media::MediaHandler::get()),
+       _audioQueueSize(0)
 {
 }
 
@@ -119,10 +112,6 @@
 {
        GNASH_REPORT_FUNCTION;
 
-#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
-       killParserThread();
-#endif
-
        // When closing gnash before playback is finished, the soundhandler 
        // seems to be removed before netstream is destroyed.
        if (_soundHandler)
@@ -132,9 +121,6 @@
 
        m_imageframe.reset();
 
-       delete m_unqueued_data;
-       m_unqueued_data = NULL;
-
 }
 
 void
@@ -196,12 +182,6 @@
        if (_soundHandler)
                _soundHandler->attach_aux_streamer(audio_streamer, this);
 
-#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
-       // This starts the parser thread
-       _parserThread = new 
boost::thread(boost::bind(NetStreamFfmpeg::parseAllInput, this)); 
-       _parserThreadBarrier.wait();
-#endif // LOAD_MEDIA_IN_A_SEPARATE_THREAD
-
        return;
 }
 
@@ -267,6 +247,8 @@
                return false;
        }
 
+       m_parser->setBufferTime(m_bufferTime);
+
        initVideoDecoder(*m_parser); 
        initAudioDecoder(*m_parser); 
 
@@ -274,6 +256,7 @@
        _playHead.setState(PlayHead::PLAY_PLAYING);
 
        decodingStatus(DEC_BUFFERING);
+       _playbackClock->pause(); // NOTE: should be paused already
 
 #ifdef GNASH_DEBUG_STATUS
        log_debug("Setting playStart status");
@@ -284,59 +267,6 @@
 }
 
 
-#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
-// to be run in parser thread
-void NetStreamFfmpeg::parseAllInput(NetStreamFfmpeg* ns)
-{
-       //GNASH_REPORT_FUNCTION;
-
-       ns->_parserThreadBarrier.wait();
-
-       // Parse in a thread...
-       while ( 1 )
-       {
-               // this one will lock _parserKillRequestMutex
-               if ( ns->parserThreadKillRequested() ) break;
-
-               {
-                       boost::mutex::scoped_lock lock(ns->_parserMutex);
-                       if ( ns->m_parser->parsingCompleted() ) break;
-                       ns->m_parser->parseNextTag();
-               }
-
-               usleep(10); // task switch (after lock was released!)
-       }
-}
-
-void
-NetStreamFfmpeg::killParserThread()
-{
-       GNASH_REPORT_FUNCTION;
-
-       {
-               boost::mutex::scoped_lock lock(_parserKillRequestMutex);
-               _parserKillRequest = true;
-       }
-
-       // might as well be never started
-       if ( _parserThread )
-       {
-               _parserThread->join();
-       }
-
-       delete _parserThread;
-       _parserThread = NULL;
-}
-
-bool
-NetStreamFfmpeg::parserThreadKillRequested()
-{
-       boost::mutex::scoped_lock lock(_parserKillRequestMutex);
-       return _parserKillRequest;
-}
-
-#endif // LOAD_MEDIA_IN_A_SEPARATE_THREAD
-
 // audio callback is running in sound handler thread
 bool NetStreamFfmpeg::audio_streamer(void *owner, boost::uint8_t *stream, int 
len)
 {
@@ -376,6 +306,8 @@
                        ns->_audioQueue.pop_front();
                }
 
+               ns->_audioQueueSize -= n; // we consumed 'n' bytes here 
+
        }
 
        return true;
@@ -396,12 +328,25 @@
        }
 
        boost::uint64_t nextTimestamp;
+       bool parsingComplete = m_parser->parsingCompleted();
        if ( ! m_parser->nextVideoFrameTimestamp(nextTimestamp) )
        {
 #ifdef GNASH_DEBUG_DECODING
-               log_debug("getDecodedVideoFrame(%d): no more video frames in 
input (nextVideoFrameTimestamp returned false)");
+               log_debug("getDecodedVideoFrame(%d): "
+                       "no more video frames in input "
+                       "(nextVideoFrameTimestamp returned false, "
+                       "parsingComplete=%d)",
+                       ts, parsingComplete);
 #endif // GNASH_DEBUG_DECODING
+
+               if ( parsingComplete )
+               {
                decodingStatus(DEC_STOPPED);
+#ifdef GNASH_DEBUG_STATUS
+                       log_debug("getDecodedVideoFrame setting playStop status 
(parsing complete and nextVideoFrameTimestamp() returned false)");
+#endif
+                       setStatus(playStop);
+               }
                return video;
        }
 
@@ -473,6 +418,18 @@
                return video;
        }
 
+#if 0 // TODO: check if the video is a cue point, if so, call 
processNotify(onCuePoint, object..)
+      // NOTE: should only be done for SWF>=8 ?
+       if ( 1 ) // frame->isKeyFrame() )
+       {
+               as_object* infoObj = new as_object();
+               string_table& st = getVM().getStringTable();
+               infoObj->set_member(st.find("time"), 
as_value(double(frame->timestamp())));
+               infoObj->set_member(st.find("type"), as_value("navigation"));
+               processNotify("onCuePoint", infoObj);
+       }
+#endif
+
        assert( _videoDecoder.get() ); // caller should check this
        assert( ! _videoDecoder->peek() ); // everything we push, we'll pop 
too..
 
@@ -530,10 +487,6 @@
 {
        GNASH_REPORT_FUNCTION;
 
-#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
-       boost::mutex::scoped_lock lock(_parserMutex);
-#endif // LOAD_MEDIA_IN_A_SEPARATE_THREAD
-
        // We'll mess with the input here
        if ( ! m_parser.get() )
        {
@@ -544,8 +497,6 @@
        // Don't ask me why, but NetStream::seek() takes seconds...
        boost::uint32_t pos = posSeconds*1000;
 
-       long newpos = 0;
-
        // We'll pause the clock source and mark decoders as buffering.
        // In this way, next advance won't find the source time to 
        // be a lot of time behind and chances to get audio buffer
@@ -553,14 +504,21 @@
        // ::advance will resume the playbackClock if DEC_BUFFERING...
        //
        _playbackClock->pause();
-       decodingStatus(DEC_BUFFERING); 
 
        // Seek to new position
-       newpos = m_parser->seek(pos);
+       boost::uint32_t newpos = pos;
+       if ( ! m_parser->seek(newpos) )
+       {
+               //log_error("Seek to invalid time");
+#ifdef GNASH_DEBUG_STATUS
+               log_debug("Setting invalidTime status");
+#endif
+               setStatus(invalidTime);
+               _playbackClock->resume(); // we won't be *BUFFERING*, so resume 
now
+               return;
+       }
        log_debug("m_parser->seek(%d) returned %d", pos, newpos);
 
-       m_last_audio_timestamp = m_last_video_timestamp = newpos;
-
        { // cleanup audio queue, so won't be consumed while seeking
                boost::mutex::scoped_lock lock(_audioQueueMutex);
                for (AudioQueue::iterator i=_audioQueue.begin(), 
e=_audioQueue.end();
@@ -573,7 +531,7 @@
        
        // 'newpos' will always be on a keyframe (supposedly)
        _playHead.seekTo(newpos);
-       _qFillerResume.notify_all(); // wake it decoder is sleeping
+       decodingStatus(DEC_BUFFERING); 
        
        refreshVideoFrame(true);
 }
@@ -581,8 +539,12 @@
 void
 NetStreamFfmpeg::parseNextChunk()
 {
-       // TODO: parse as much as possible w/out blocking
-       //       (will always block currently..)
+       // If we parse too much we might block
+       // the main thread, if we parse too few
+       // we'll get bufferEmpty often.
+       // I guess 2 chunks (frames) would be fine..
+       //
+       m_parser->parseNextChunk();
        m_parser->parseNextChunk();
 }
 
@@ -603,8 +565,8 @@
        {
 #ifdef GNASH_DEBUG_DECODING
                log_debug("%p.refreshAudioBuffer: doing nothing as playhead is 
paused - "
-                       "bufferLength=%d, bufferTime=%d",
-                       this, bufferLen, m_bufferTime);
+                       "bufferLength=%d/%d",
+                       this, bufferLength(), m_bufferTime);
 #endif // GNASH_DEBUG_DECODING
                return;
        }
@@ -614,7 +576,7 @@
 #ifdef GNASH_DEBUG_DECODING
                log_debug("%p.refreshAudioBuffer: doing nothing "
                        "as current position was already decoded - "
-                       "bufferLength=%d, bufferTime=%d",
+                       "bufferLength=%d/%d",
                        this, bufferLen, m_bufferTime);
 #endif // GNASH_DEBUG_DECODING
                return;
@@ -641,25 +603,129 @@
        // nothing to do if we don't have an audio decoder
        if ( ! _audioDecoder.get() ) return;
 
+       // just flush any pending audio frame if we're
+       // not willing to play sounds anyway
+       if ( ! _soundHandler )
+       {
+               while ( media::raw_mediadata_t* audio = decodeNextAudioFrame() )
+                       delete audio;
+               _playHead.setAudioConsumed();
+               return;
+       }
+
        bool consumed = false;
 
        boost::uint64_t nextTimestamp;
        while ( 1 )
        {
+
+               boost::mutex::scoped_lock lock(_audioQueueMutex);
+
+               // The sound_handler mixer will pull decoded
+               // audio frames off the _audioQueue whenever 
+               // new audio has to be played.
+               // This is done based on the output frequency,
+               // currently hard-coded to be 44100 samples per second.
+               //
+               // Our job here would be to provide that much data.
+               // We're in an ::advance loop, so must provide enough
+               // data for the mixer to fetch till next advance.
+               // Assuming we know the ::advance() frame rate (which we don't
+               // yet) the computation would be something along these lines:
+               //
+               //    44100/1 == samplesPerAdvance/secsPerAdvance
+               //    samplesPerAdvance = secsPerAdvance*(44100/1)
+               //
+               // For example, at 12FPS we have:
+               //
+               //   secsPerAdvance = 1/12 = .083333
+               //   samplesPerAdvance = .08333*44100 =~ 3675
+               //
+               // Now, to know how many samples are on the queue
+               // we need to know the size in bytes of each sample.
+               // If I'm not wrong this is again hard-coded to 2 bytes,
+               // so we'd have:
+               //
+               //   bytesPerAdvance = samplesPerAdvance / sampleSize
+               //   bytesPerAdvance = 3675 / 2 =~ 1837
+               //
+               // Finally we'll need to find number of bytes in the
+               // queue to really tell how many there are (don't think
+               // it's a fixed size for each element).
+               //
+               // For now we use the hard-coded value of 20, arbitrarely
+               // assuming there is an average of 184 samples per frame.
+               //
+               // - If we push too few samples, we'll hear silence gaps 
(underrun)
+               // - If we push too many samples the audio mixer consumer
+               //   won't be able to consume all before our next filling
+               //   iteration (overrun)
+               //
+               // For *underrun* conditions we kind of have an handling, that 
is
+               // sending the BufferEmpty event and closing the time tap (this 
is
+               // done by ::advance directly).
+               //
+               // For *overrun* conditions we currently don't have any 
handling.
+               // One possibility could be closing the time tap till we've done
+               // consuming the queue.
+               //
+               //
+
+               float swfFPS = 25; // TODO: get this host app (gnash -d affects 
this)
+               double msecsPerAdvance = 10000/swfFPS;
+
+               static const int outSampleSize = 2;     // <--- 2 is output 
sample size
+               static const int outSampleFreq = 44100; // <--- 44100 is output 
audio frequency
+               //int samplesPerAdvance = 
(int)std::floor(secsPerAdvance*outSampleFreq); // round up
+               //unsigned int bufferLimit = outSampleSize*samplesPerAdvance;
+
+               unsigned int bufferLimit = 20;
+               unsigned int bufferSize = _audioQueue.size();
+               if ( bufferSize > bufferLimit )
+               {
+                       // we won't buffer more then 'bufferLimit' frames in 
the queue
+                       // to avoid ending up with a huge queue which will take 
some
+                       // time before being consumed by audio mixer, but still 
marked
+                       // as "consumed". Keeping decoded frames buffer low 
would also
+                       // reduce memory use.
+                       //
+                       // The alternative would be always decode on demand 
from the
+                       // audio consumer thread, but would introduce a lot of 
thread-safety
+                       // issues: playhead would need protection, input would 
need protection.
+                       //
+//#ifdef GNASH_DEBUG_DECODING
+                       log_debug("%p.pushDecodedAudioFrames(%d) : buffer 
overrun (%d/%d).",
+                               this, ts, bufferSize, bufferLimit);
+//#endif // GNASH_DEBUG_DECODING
+
+                       // we may want to pause the playbackClock here...
+                       _playbackClock->pause();
+
+                       return;
+               }
+
+               lock.unlock(); // no need to keep the audio queue locked while 
decoding..
+
+               bool parsingComplete = m_parser->parsingCompleted();
                if ( ! m_parser->nextAudioFrameTimestamp(nextTimestamp) )
                {
 #ifdef GNASH_DEBUG_DECODING
                        log_debug("%p.pushDecodedAudioFrames(%d): "
                                "no more audio frames in input "
-                               "(nextAudioFrameTimestamp returned false)",
-                               this, ts);
+                               "(nextAudioFrameTimestamp returned false, 
parsingComplete=%d)",
+                               this, ts, parsingComplete);
 #endif // GNASH_DEBUG_DECODING
+
+                       if ( parsingComplete )
+                       {
                        consumed = true;
                        decodingStatus(DEC_STOPPED);
 #ifdef GNASH_DEBUG_STATUS
-                       log_debug("Setting playStop status");
+                               log_debug("pushDecodedAudioFrames setting 
playStop status (parsing complete and nextAudioFrameTimestamp returned false)");
 #endif
                        setStatus(playStop);
+                       }
+
                        break;
                }
 
@@ -671,30 +737,8 @@
                                this, ts, nextTimestamp);
 #endif // GNASH_DEBUG_DECODING
                        consumed = true;
-                       break; // next frame is in the future
-               }
 
-               boost::mutex::scoped_lock lock(_audioQueueMutex);
-
-               static const unsigned int bufferLimit = 20;
-               if ( _audioQueue.size() > bufferLimit )
-               {
-                       // we won't buffer more then 'bufferLimit' frames in 
the queue
-                       // to avoid ending up with a huge queue which will take 
some
-                       // time before being consumed by audio mixer, but still 
marked
-                       // as "consumed". Keeping decoded frames buffer low 
would also
-                       // reduce memory use.
-                       //
-                       // The alternative would be always decode on demand 
from the
-                       // audio consumer thread, but would introduce a lot of 
thread-safety
-                       // issues: playhead would need protection, input would 
need protection.
-                       //
-//#ifdef GNASH_DEBUG_DECODING
-                       log_debug("%p.pushDecodedAudioFrames(%d) : queue size 
over limit (%d), "
-                               "audio won't be consumed (buffer overrun?)",
-                               this, ts, bufferLimit);
-//#endif // GNASH_DEBUG_DECODING
-                       return;
+                       if ( nextTimestamp > ts+msecsPerAdvance ) break; // 
next frame is in the future
                }
 
                media::raw_mediadata_t* audio = decodeNextAudioFrame();
@@ -706,16 +750,35 @@
                        break;
                }
 
+               lock.lock(); // now needs locking
+
 #ifdef GNASH_DEBUG_DECODING
                // this one we might avoid :) -- a less intrusive logging could
                // be take note about how many things we're pushing over
                log_debug("pushDecodedAudioFrames(%d) pushing %dth frame with 
timestamp %d", ts, _audioQueue.size()+1, nextTimestamp); 
 #endif
                _audioQueue.push_back(audio);
+               _audioQueueSize += audio->m_size;
+       }
+
+       // If we consumed audio of current position, feel free to advance if 
needed,
+       // resuming playbackClock too..
+       if ( consumed )
+       {
+               // resume the playback clock, assuming the
+               // only reason for it to be paused is we
+               // put in pause mode due to buffer overrun
+               // (ie: the sound handler is slow at consuming
+               // the audio data).
+#ifdef GNASH_DEBUG_DECODING
+               log_debug("resuming playback clock on audio consume");
+#endif // GNASH_DEBUG_DECODING
+               assert(decodingStatus()!=DEC_BUFFERING);
+               _playbackClock->resume();
+
+               _playHead.setAudioConsumed();
        }
 
-       // If we consumed audio of current position, feel free to advance if 
needed
-       if ( consumed ) _playHead.setAudioConsumed();
 }
 
 
@@ -736,7 +799,7 @@
        // so this is to avoid that.
        boost::uint32_t parserTime = m_parser->getBufferLength();
        boost::uint32_t playHeadTime = time();
-       boost::uint32_t bufferLen = parserTime > playHeadTime ? 
parserTime-playHeadTime : 0;
+       boost::uint32_t bufferLen = bufferLength();
 #endif
 
        if ( ! alsoIfPaused && _playHead.getState() == PlayHead::PLAY_PAUSED )
@@ -778,14 +841,10 @@
                {
 #ifdef GNASH_DEBUG_DECODING
                        log_debug("%p.refreshVideoFrame(): "
-                               "no more video frames to decode, "
-                               "sending STOP event",
+                               "no more video frames to decode "
+                               "(DEC_STOPPED, null from getDecodedVideoFrame)",
                                this);
 #endif // GNASH_DEBUG_DECODING
-#ifdef GNASH_DEBUG_STATUS
-                       log_debug("Setting playStop status");
-#endif
-                       setStatus(playStop);
                }
                else
                {
@@ -823,33 +882,27 @@
        // pass them to a event handler
        processStatusNotifications();
 
-#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
-       // stop parser thread while advancing
-       boost::mutex::scoped_lock lock(_parserMutex);
-#endif // LOAD_MEDIA_IN_A_SEPARATE_THREAD
-
        // Nothing to do if we don't have a parser
        if ( ! m_parser.get() ) return;
 
-       // bufferLength() would lock the mutex (which we already hold),
-       // so this is to avoid that.
-       boost::uint32_t parserTime = m_parser->getBufferLength();
-       boost::uint32_t playHeadTime = time();
-       boost::uint32_t bufferLen = parserTime > playHeadTime ? 
parserTime-playHeadTime : 0;
-
-#ifndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
-       // Fill the buffer some more if not full ...
-       if ( bufferLen < m_bufferTime && ! m_parser->parsingCompleted() )
+       if ( decodingStatus() == DEC_STOPPED )
        {
-               parseNextChunk(); 
+               //log_debug("NetStreamFfmpeg::advance: dec stopped...");
+               // nothing to do if we're stopped...
+               return;
        }
-#endif // LOAD_MEDIA_IN_A_SEPARATE_THREAD
 
+       bool parsingComplete = m_parser->parsingCompleted();
+#ifndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
+       if ( ! parsingComplete ) parseNextChunk();
+#endif
+
+       size_t bufferLen = bufferLength();
 
        // Check decoding status 
        if ( decodingStatus() == DEC_DECODING && bufferLen == 0 )
        {
-               if ( ! m_parser->parsingCompleted() )
+               if ( ! parsingComplete )
                {
 #ifdef GNASH_DEBUG_DECODING
                        log_debug("%p.advance: buffer empty while decoding,"
@@ -875,7 +928,7 @@
 
        if ( decodingStatus() == DEC_BUFFERING )
        {
-               if ( bufferLen < m_bufferTime && ! m_parser->parsingCompleted() 
)
+               if ( bufferLen < m_bufferTime && ! parsingComplete )
                {
 #ifdef GNASH_DEBUG_DECODING
                        log_debug("%p.advance: buffering"
@@ -962,10 +1015,6 @@
 long
 NetStreamFfmpeg::bytesTotal ()
 {
-#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
-       boost::mutex::scoped_lock lock(_parserMutex);
-#endif // LOAD_MEDIA_IN_A_SEPARATE_THREAD
-
        if ( ! m_parser.get() )
        {
                log_debug("bytesTotal: no parser, no party");

Index: server/asobj/NetStreamFfmpeg.h
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/NetStreamFfmpeg.h,v
retrieving revision 1.75
retrieving revision 1.76
diff -u -b -r1.75 -r1.76
--- server/asobj/NetStreamFfmpeg.h      9 Jun 2008 14:31:55 -0000       1.75
+++ server/asobj/NetStreamFfmpeg.h      16 Jun 2008 09:05:02 -0000      1.76
@@ -52,10 +52,6 @@
 #include <memory>
 #include <cassert>
 
-
-/// Uncomment the following to load media in a separate thread
-//#define LOAD_MEDIA_IN_A_SEPARATE_THREAD
-
 // Forward declarations
 namespace gnash {
        class IOChannel;
@@ -96,11 +92,6 @@
        // See dox in NetStream.h
        void advance();
 
-#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
-       /// The parsing thread. Sets up the decoder, and decodes.
-       static void parseAllInput(NetStreamFfmpeg* ns);
-#endif
-
        /// Callback used by sound_handler to get audio data
        //
        /// This is a sound_handler::aux_streamer_ptr type.
@@ -126,7 +117,7 @@
                DEC_NONE,
                DEC_STOPPED,
                DEC_DECODING,
-               DEC_BUFFERING,
+               DEC_BUFFERING
        };
 
        /// Gets video info from the parser and initializes _videoDecoder
@@ -234,58 +225,12 @@
        /// Audio decoder
        std::auto_ptr<media::AudioDecoder> _audioDecoder;
 
-#ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
-       /// The parser thread
-       boost::thread* _parserThread;
-
-       /// Barrier to synchronize thread and thread starter
-       boost::barrier _parserThreadBarrier;
-
-       /// Mutex serializing access to parser,
-       /// when reading from a separate thread
-       boost::mutex _parserMutex;
-
-       /// Kill decoder thread, if any
-       //
-       /// POSTCONDITIONS:
-       ///     _decodeThread is NULL
-       ///     decoder thread is not running
-       ///
-       /// Uses the _qMutex
-       ///
-       void killParserThread();
-
-       /// Return true if kill of parser thread was requested
-       bool parserThreadKillRequested();
-
-       /// Protected by _parserKillRequestMutex
-       bool _parserKillRequest;
-
-       /// Mutex protecting _parserKillRequest
-       boost::mutex _parserKillRequestMutex;
-
-#endif // LOAD_MEDIA_IN_A_SEPARATE_THREAD
-
-
-       // The timestamp of the last decoded video frame, in seconds.
-       volatile boost::uint32_t m_last_video_timestamp;
-
-       // The timestamp of the last decoded audio frame, in seconds.
-       volatile boost::uint32_t m_last_audio_timestamp;
-
-       /// Queues filler will wait on this condition when queues are full
-       boost::condition _qFillerResume;
-
        /// 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
-       media::raw_mediadata_t* m_unqueued_data;
-
        // Current sound handler
        media::sound_handler* _soundHandler;
 
@@ -310,6 +255,9 @@
        /// and consumed by sound_handler callback (audio_streamer)
         AudioQueue _audioQueue;
 
+       /// Number of bytes in the audio queue, protected by _audioQueueMutex
+       size_t _audioQueueSize;
+
        /// The queue needs to be protected as sound_handler callback
        /// is invoked by a separate thread (dunno if it makes sense actually)
        boost::mutex _audioQueueMutex;




reply via email to

[Prev in Thread] Current Thread [Next in Thread]