gnash-commit
[Top][All Lists]
Advanced

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

[Gnash-commit] /srv/bzr/gnash/trunk r11620: Make all movies and bitmap l


From: Sandro Santilli
Subject: [Gnash-commit] /srv/bzr/gnash/trunk r11620: Make all movies and bitmap loads non-blocking by using a single thread for socket initialization, magic number parsing and construction of appropriate definition.
Date: Thu, 12 Nov 2009 22:00:31 +0100
User-agent: Bazaar (1.16.1)

------------------------------------------------------------
revno: 11620 [merge]
committer: Sandro Santilli <address@hidden>
branch nick: trunk
timestamp: Thu 2009-11-12 22:00:31 +0100
message:
  Make all movies and bitmap loads non-blocking by using a single thread for 
socket initialization, magic number parsing and construction of appropriate 
definition.
added:
  libcore/MovieLoader.cpp
  libcore/MovieLoader.h
modified:
  NEWS
  libcore/Makefile.am
  libcore/MovieLibrary.h
  libcore/movie_root.cpp
  libcore/movie_root.h
  libcore/parser/SWFMovieDefinition.cpp
  libcore/parser/SWFMovieDefinition.h
  testsuite/misc-ming.all/loadMovieTestRunner.cpp
  testsuite/movies.all/gravity_embedded-TestRunner.cpp
  testsuite/swfdec/PASSING
  testsuite/swfdec/REALTIME
  utilities/processor.cpp
=== modified file 'NEWS'
--- a/NEWS      2009-11-08 11:57:40 +0000
+++ b/NEWS      2009-11-12 08:02:01 +0000
@@ -1,5 +1,16 @@
+20??-??-??  
+
+Gnash 0.8.6
+Improvements since 0.8.5 release are:
+
+ * Asynchronous load of all resources (XML, variables, movies, bitmaps)
+
+
 2009-03-04 Rob Savoye  <address@hidden>
 
+Gnash 0.8.5 released !
+Improvements since 0.8.4 release are:
+
 Due to better XML parsing compatibility and a minor fix to NetConnection,
 many more video sites work than in the last release. Support for new
 codecs keeps YouTube support up to date.

=== modified file 'libcore/Makefile.am'
--- a/libcore/Makefile.am       2009-10-01 13:19:43 +0000
+++ b/libcore/Makefile.am       2009-11-12 12:13:15 +0000
@@ -130,6 +130,7 @@
        RGBA.cpp        \
        asClass.cpp \
        asNamespace.cpp \
+       MovieLoader.cpp \
        $(FREETYPE_SOURCES) \
        $(NULL)
 
@@ -204,6 +205,7 @@
        swf_function.h \
        Timers.h \
        Video.h \
+       MovieLoader.h \
        $(NULL)
 
 if ENABLE_AVM2

=== modified file 'libcore/MovieLibrary.h'
--- a/libcore/MovieLibrary.h    2009-06-15 13:50:02 +0000
+++ b/libcore/MovieLibrary.h    2009-11-11 23:18:13 +0000
@@ -23,6 +23,7 @@
 
 #include <map>
 #include <algorithm>
+#include <boost/thread/thread.hpp>
 
 namespace gnash {
 
@@ -63,6 +64,7 @@
     bool get(const std::string& key,
             boost::intrusive_ptr<movie_definition>* ret)
     {
+        boost::mutex::scoped_lock lock(_mapMutex);
         LibraryContainer::iterator it = _map.find(key);
         if (it == _map.end()) return false;
         
@@ -75,6 +77,7 @@
     /// Mark all library elements as reachable (for GC)
     void markReachableResources() const
     {
+        boost::mutex::scoped_lock lock(_mapMutex);
         for (LibraryContainer::const_iterator i=_map.begin(), e=_map.end();
                 i!=e; ++i)
         {
@@ -95,11 +98,16 @@
         temp.def = mov;
         temp.hitCount = 0;
 
+        boost::mutex::scoped_lock lock(_mapMutex);
         _map[key] = temp;
     }
   
 
-    void clear() { _map.clear(); }
+    void clear()
+    {
+        boost::mutex::scoped_lock lock(_mapMutex);
+        _map.clear();
+    }
   
 private:
 
@@ -120,11 +128,14 @@
         }
 
         while (_map.size() > max) {
+            boost::mutex::scoped_lock lock(_mapMutex);
             _map.erase(std::min_element(_map.begin(), _map.end(),
                         &findWorstHitCount));
         }
     
     }
+
+       mutable boost::mutex _mapMutex;
   
 };
 

=== added file 'libcore/MovieLoader.cpp'
--- a/libcore/MovieLoader.cpp   1970-01-01 00:00:00 +0000
+++ b/libcore/MovieLoader.cpp   2009-11-12 12:13:15 +0000
@@ -0,0 +1,508 @@
+#include "log.h"
+#include "MovieFactory.h"
+#include "movie_root.h"
+#include "MovieFactory.h"
+#include "DisplayObject.h"
+#include "as_value.h"
+#include "as_object.h"
+#include "movie_definition.h"
+#include "Movie.h"
+#include "MovieClip.h"
+#include "URL.h"
+#include "namedStrings.h"
+#include "ExecutableCode.h"
+
+#include <memory> // for auto_ptr
+#include <boost/bind.hpp>
+
+//#define GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING 1
+//#define GNASH_DEBUG_LOCKING 1
+
+namespace gnash {
+
+MovieLoader::MovieLoader(movie_root& mr)
+    :
+    _movieRoot(mr),
+    _thread(0),
+    _barrier(2) // main and loader thread
+{
+}
+
+// private
+// runs in loader thread
+void
+MovieLoader::processRequests()
+{
+       // let _thread assignment happen before going on
+    _barrier.wait();
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+    log_debug("Starting movie loader thread");
+#endif
+
+
+    while (1) {
+
+        // check for shutdown/cancel request
+        if ( killed() )
+        {
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+            log_debug("Loader thread killed");
+#endif
+            return;
+        }
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("processRequests: lock on requests: trying");
+#endif
+
+        boost::mutex::scoped_lock lock(_requestsMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("processRequests: lock on requests: obtained");
+#endif
+
+        // Find first non-completed request (the others we'll wait)
+        Requests::iterator endIt = _requests.end();
+        Requests::iterator it = find_if(_requests.begin(), endIt,
+                                        boost::bind(&Request::pending, _1));
+
+        if (it == endIt)
+        {
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+            log_debug("Movie loader thread getting to sleep (nothing more to 
do)");
+#endif
+            // all completed, we can get to sleep
+            _wakeup.wait(lock);
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+            log_debug("Movie loader thread waked up");
+#endif
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("processRequests: lock on requests: release");
+#endif
+
+            continue;
+        }
+
+        Request* lr = *it;
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("processRequests: lock on requests: release");
+#endif
+
+        lock.unlock(); // now main thread can continue to push requests
+
+        processRequest(*lr);
+
+    }
+
+}
+
+// private
+// runs in loader thread
+void
+MovieLoader::processRequest(Request& r)
+{
+    const URL& url = r.getURL();
+    bool usePost = r.usePost();
+    const std::string* postdata = usePost ? &(r.getPostData()) : 0;
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+    log_debug("Movie loader thread processing request for target %s",
+        r.getTarget());
+#endif
+
+       boost::intrusive_ptr<movie_definition> md (
+        MovieFactory::makeMovie(url, _movieRoot.runResources(),
+                                NULL, true, postdata)
+    );
+    r.setCompleted(md);
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+    log_debug("Movie loader thread completed request for target %s",
+        r.getTarget());
+#endif
+}
+
+// public
+// runs in main thread
+void
+MovieLoader::clear()
+{
+    if ( _thread.get() )
+    {
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("clear: lock on requests: trying");
+#endif
+
+        boost::mutex::scoped_lock requestsLock(_requestsMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("clear: lock on requests: obtained");
+#endif
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("clear: lock on kill: trying");
+#endif
+
+        boost::mutex::scoped_lock lock(_killMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("clear: lock on kill: obtained");
+#endif
+
+        _killed=true;
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("clear: lock on kill: release for kill");
+#endif
+
+        lock.unlock();
+
+log_debug("waking up loader thread");
+
+        _wakeup.notify_all(); // in case it was sleeping
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("clear: lock on requests: release ater notify_all");
+#endif
+        requestsLock.unlock(); // allow the thread to die
+
+log_debug("notified, joining");
+        _thread->join();
+log_debug("joined");
+        _thread.reset();
+    }
+
+    // no thread now, can clean w/out locking
+    clearRequests();
+
+#ifdef GNASH_DEBUG_LOCKING
+    log_debug("clear: lock on requests: release if not after notify_all");
+#endif
+}
+
+// private, no locking
+// runs in main thread
+void
+MovieLoader::clearRequests()
+{
+    for (Requests::iterator it=_requests.begin(),
+            end = _requests.end(); it != end; ++it)
+    {
+        delete *it;
+    }
+    _requests.clear();
+}
+
+// private 
+// runs in main thread
+bool
+MovieLoader::processCompletedRequest(const Request& r)
+{
+    //GNASH_REPORT_FUNCTION;
+
+    boost::intrusive_ptr<movie_definition> md;
+    if ( ! r.getCompleted(md) ) return false; // not completed yet
+
+    const std::string& target = r.getTarget();
+    DisplayObject* targetDO = _movieRoot.findCharacterByTarget(target);
+    as_object* handler = r.getHandler();
+
+    if ( ! md )
+    {
+
+        if ( targetDO && handler )
+        {
+            // Signal load error
+            // Tested not to happen if target isn't found at time of loading
+            //
+
+            as_value arg1(getObject(targetDO));
+
+            // FIXME: docs suggest the string can be either "URLNotFound" or
+            // "LoadNeverCompleted". This is neither of them:
+            as_value arg2("Failed to load movie or jpeg");
+
+            // FIXME: The last argument is HTTP status, or 0 if no connection
+            // was attempted (sandbox) or no status information is available
+            // (supposedly the Adobe mozilla plugin).
+            as_value arg3(0.0);
+
+            handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadError",
+                    arg1, arg2, arg3);
+        }
+        return true; // nothing to do, but completed
+    }
+
+    const URL& url = r.getURL();
+
+       Movie* extern_movie = md->createMovie(*_movieRoot.getVM().getGlobal());
+       if (!extern_movie)
+    {
+               log_error(_("Can't create Movie instance "
+                    "for definition loaded from %s"), url);
+               return true; // completed in any case...
+       }
+
+       // Parse query string
+       MovieClip::MovieVariables vars;
+       url.parse_querystring(url.querystring(), vars);
+    extern_movie->setVariables(vars);
+
+    if (targetDO)
+    {
+        targetDO->getLoadedMovie(extern_movie);
+    }
+    else
+    {
+        unsigned int levelno;
+        if (_movieRoot.isLevelTarget(target, levelno))
+        {
+            log_debug(_("processCompletedRequest: _level loading "
+                    "(level %u)"), levelno);
+               extern_movie->set_depth(levelno + 
DisplayObject::staticDepthOffset);
+            _movieRoot.setLevel(levelno, extern_movie);
+        }
+        else
+        {
+            log_debug("Target %s of a loadMovie request doesn't exist at "
+                  "load complete time", target);
+            return true;
+        }
+    }
+
+    if (handler && targetDO)
+    {
+        // Dispatch onLoadStart
+        // FIXME: should be signalled before starting to load
+        //        (0/-1 bytes loaded/total) but still with *new*
+        //        display object as target (ie: the target won't
+        //        contain members set either before or after loadClip.
+        handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadStart",
+            getObject(targetDO));
+
+        // Dispatch onLoadProgress
+        // FIXME: should be signalled on every readNonBlocking()
+        //        with a buffer size of 65535 bytes.
+        //
+        size_t bytesLoaded = md->get_bytes_loaded();
+        size_t bytesTotal = md->get_bytes_total();
+        handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadProgress",
+            getObject(targetDO), bytesLoaded, bytesTotal);
+
+        // Dispatch onLoadComplete
+        // FIXME: find semantic of last arg
+        handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadComplete",
+            getObject(targetDO), as_value(0.0));
+
+
+        // Dispatch onLoadInit
+        
+        // This event must be dispatched when actions
+        // in first frame of loaded clip have been executed.
+        //
+        // Since getLoadedMovie or setLevel above will invoke
+        // stagePlacementCallback and thus queue all actions in first
+        // frame, we'll queue the
+        // onLoadInit call next, so it happens after the former.
+        //
+        std::auto_ptr<ExecutableCode> code(
+                new DelayedFunctionCall(handler, NSV::PROP_BROADCAST_MESSAGE, 
+                    "onLoadInit", getObject(targetDO)));
+
+        getRoot(*handler).pushAction(code, movie_root::apDOACTION);
+    }
+
+    return true;
+
+}
+
+// private 
+// runs in main thread
+void
+MovieLoader::processCompletedRequests()
+{
+    //GNASH_REPORT_FUNCTION;
+
+    for (;;)
+    {
+
+#ifdef GNASH_DEBUG_LOCKING
+log_debug("processCompletedRequests: lock on requests: trying");
+#endif
+
+    boost::mutex::scoped_lock requestsLock(_requestsMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+log_debug("processCompletedRequests: lock on requests: obtained");
+#endif
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+    log_debug("Checking %d requests for completeness",
+        _requests.size());
+#endif
+
+
+    Request* firstCompleted = 0;
+    Requests::iterator endIt = _requests.end();
+    Requests::iterator it = find_if(_requests.begin(), endIt,
+                                    boost::bind(&Request::completed, _1));
+    if ( it != endIt ) firstCompleted=*it;
+
+#ifdef GNASH_DEBUG_LOCKING
+    log_debug("processCompletedRequests: lock on requests: releasing");
+#endif
+
+    requestsLock.unlock();
+
+    if ( firstCompleted )
+    {
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+        log_debug("Load request for target %s completed",
+            firstCompleted->getTarget());
+#endif
+
+        bool checkit = processCompletedRequest(*firstCompleted);
+        assert(checkit);
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("processCompletedRequests: lock on requests for removal: "
+                  "trying");
+#endif
+
+        requestsLock.lock();
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("processCompletedRequests: lock on requests for removal: "
+                  "obtained");
+#endif
+
+        _requests.remove(firstCompleted);
+        delete firstCompleted;
+
+#ifdef GNASH_DEBUG_LOCKING
+        log_debug("processCompletedRequests: lock on requests for removal: "
+                  "release");
+#endif
+    }
+    else
+    {
+        break;
+    }
+
+    }
+}
+
+// private
+// runs in loader thread
+bool
+MovieLoader::killed()
+{
+    boost::mutex::scoped_lock lock(_killMutex);
+    return _killed;
+}
+
+// public
+// runs in main thread
+void
+MovieLoader::loadMovie(const std::string& urlstr,
+                               const std::string& target,
+                               const std::string& data,
+                               MovieClip::VariablesMethod method,
+                               as_object* handler)
+{
+
+    /// URL security is checked in StreamProvider::getStream() down the
+    /// chain.
+    URL url(urlstr, _movieRoot.runResources().baseURL());
+
+    /// If the method is MovieClip::METHOD_NONE, we send no data.
+    if (method == MovieClip::METHOD_GET)
+    {
+        std::string varsToSend(urlstr);
+        /// GET: append data to query string.
+        std::string qs = url.querystring();
+        if ( qs.empty() ) varsToSend.insert(0, 1, '?');
+        else varsToSend.insert(0, 1, '&');
+        url.set_querystring(qs + varsToSend);
+    }
+
+    log_debug("MovieLoader::loadMovie(%s, %s)", url.str(), target);
+
+    const std::string* postdata = (method == MovieClip::METHOD_POST) ? &data
+                                                                     : 0;
+
+#ifdef GNASH_DEBUG_LOCKING
+log_debug("loadMovie: lock on requests: trying");
+#endif
+
+    boost::mutex::scoped_lock lock(_requestsMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+log_debug("loadMovie: lock on requests: obtained");
+#endif
+
+    _requests.push_front(
+        new Request(url, target, postdata, handler)
+    );
+
+    // Start or wake up the loader thread 
+    if ( ! _thread.get() )
+    {
+        _killed=false;
+        _thread.reset(new boost::thread(boost::bind(
+                        &MovieLoader::processRequests, this)));
+           _barrier.wait(); // let execution start before proceeding
+    }
+    else
+    {
+        log_debug("loadMovie: waking up existing thread");
+        _wakeup.notify_all();
+    }
+
+#ifdef GNASH_DEBUG_LOCKING
+    log_debug("loadMovie: lock on requests: release");
+#endif
+}
+
+// public
+// runs in main thread
+MovieLoader::~MovieLoader()
+{
+    clear(); // will kill the thread
+}
+
+// public
+// runs in main thread
+void
+MovieLoader::setReachable() const
+{
+
+#ifdef GNASH_DEBUG_LOCKING
+    log_debug("setReachable: lock on requests: trying");
+#endif
+
+    boost::mutex::scoped_lock lock(_requestsMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+    log_debug("setReachable: lock on requests: obtained");
+#endif
+
+    for (Requests::const_iterator it=_requests.begin(),
+            end = _requests.end(); it != end; ++it)
+    {
+        (*it)->setReachable();
+    }
+
+#ifdef GNASH_DEBUG_LOCKING
+    log_debug("setReachable: lock on requests: release");
+#endif
+}
+
+
+} // namespace gnash

=== added file 'libcore/MovieLoader.h'
--- a/libcore/MovieLoader.h     1970-01-01 00:00:00 +0000
+++ b/libcore/MovieLoader.h     2009-11-12 12:13:15 +0000
@@ -0,0 +1,219 @@
+// 
+//   Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+// 
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+#ifndef GNASH_MOVIE_LOADER_H
+#define GNASH_MOVIE_LOADER_H
+
+#include "URL.h" // for Request composition
+#include "as_object.h" // for setReachable inline
+#include "movie_definition.h" // for use in intrusive_ptr (inline)
+#include "MovieClip.h" // for MovieClip::VariablesMethod type
+
+#include <list>
+#include <string>
+#include <boost/noncopyable.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/thread/condition.hpp>
+#include <boost/thread/barrier.hpp>
+
+// Forward declarations
+namespace gnash {
+    class movie_root;
+    class movie_definition;
+}
+
+namespace gnash {
+
+/// Movie loader
+//
+/// All public functions are intended to be called by the main thread
+/// Hide the asynchonous mechanism of movies loading.
+/// Currently implemented using threads, could be refactored to use
+/// non-blocking reads.
+///
+class DSOEXPORT MovieLoader : boost::noncopyable {
+
+public:
+
+    MovieLoader(movie_root& mr);
+
+    ~MovieLoader();
+
+    /// Queue a request for loading a movie
+    //
+    /// This function constructs the URL and, if required, the postdata
+    /// from the arguments. The variables to send should *not* be appended
+    /// to @param urlstr before calling this function.
+    //
+    /// @param urlstr   The url exactly as requested. This may already
+    ///                 contain a query string.
+    /// @param target   Target for request.
+    /// @param data     The variables data to send, URL encoded in
+    ///                 key/value pairs
+    /// @param method   The VariablesMethod to use for sending the data. If
+    ///                 MovieClip::METHOD_NONE, no data will be sent.
+    /// @param handler  An object which will be signalled of load
+    ///                 events (onLoadStart, onLoadComplete, onLoadInit,
+    ///                 onLoadError). Can be null if caller doesn't care.
+    ///                 
+    void loadMovie(const std::string& url, const std::string& target,
+            const std::string& data, MovieClip::VariablesMethod method,
+            as_object* handler=0);
+
+    /// Drop all requests and kill the thread
+    void clear();
+
+    /// Process all completed movie load requests.
+    void processCompletedRequests();
+
+    void setReachable() const;
+
+private:
+
+    /// A movie load request
+    class Request : boost::noncopyable {
+    public:
+        /// @param postdata
+        ///   If not null POST method will be used for HTTP.
+        ///
+        Request(const URL& u, const std::string& t,
+                const std::string* postdata, as_object* handler)
+                :
+                _target(t),
+                _url(u),
+                _usePost(false),
+                _mdef(0),
+                _mutex(),
+                _handler(handler),
+                _completed(false)
+        {
+            if ( postdata )
+            {
+                _postData = *postdata;
+                _usePost = true;
+            }
+        }
+
+        const std::string& getTarget() const { return _target; }
+        const URL& getURL() const { return _url; }
+        const std::string& getPostData() const { return _postData; }
+        bool usePost() const { return _usePost; }
+        as_object* getHandler() const { return _handler; }
+        void setReachable() const {
+            if (_handler) _handler->setReachable();
+        }
+
+        /// Get the loaded movie definition, if any
+        //
+        /// @param md the loaded movie_definition is copied here
+        ///           if it was impossible to create one.
+        ///
+        /// @return true if the request was completed, false otherwise.
+        ///
+        /// RULE: if return is FALSE param 'md' will be set to 0.
+        /// RULE: if return is TRUE  param 'md' may be set to 0 or non 0.
+        /// RULE: if parameter 'md' is set to non 0, TRUE must be returned.
+        ///
+        /// locks _mutex
+        ///
+        bool getCompleted(boost::intrusive_ptr<movie_definition>& md) const
+        {
+            boost::mutex::scoped_lock lock(_mutex);
+            md = _mdef;
+            return _completed;
+        }
+
+        /// Only check if request is completed
+        bool pending() const
+        {
+            boost::mutex::scoped_lock lock(_mutex);
+            return !_completed;
+        }
+
+        /// Only check if request is completed
+        bool completed() const
+        {
+            boost::mutex::scoped_lock lock(_mutex);
+            return _completed;
+        }
+
+        /// Complete the request
+        //
+        /// @param md the loaded movie_definition, or 0 if
+        ///           it was impossible to create one.
+        ///
+        /// locks _mutex
+        ///
+        void setCompleted(boost::intrusive_ptr<movie_definition> md)
+        {
+            boost::mutex::scoped_lock lock(_mutex);
+            _mdef = md;
+            _completed = true;
+        }
+
+    private:
+        std::string _target;
+        URL _url;
+        bool _usePost;
+        std::string _postData;
+        boost::intrusive_ptr<movie_definition> _mdef;
+        mutable boost::mutex _mutex;
+        as_object* _handler;
+        bool _completed;
+    };
+
+    /// Load requests
+    typedef std::list<Request*> Requests;
+    Requests _requests;
+
+       mutable boost::mutex _requestsMutex;
+
+    void processRequests();
+    void processRequest(Request& r);
+    void clearRequests();
+
+    /// Check a Request and process if completed.
+    //
+    /// @return true if the request was completely processed.
+    ///
+    bool processCompletedRequest(const Request& r);
+
+    /// Was thread kill requested ?
+    bool killed();
+
+    bool _killed;
+
+       boost::mutex _killMutex;
+
+    boost::condition _wakeup;
+
+    /// needed for some facilities like find_character_by_target
+    movie_root& _movieRoot;
+
+    std::auto_ptr<boost::thread> _thread;
+
+       // Barrier to ensure that _thread
+       // is initialized before the loader thread
+       // continues execution
+       boost::barrier _barrier;
+
+};
+
+} // namespace gnash
+
+#endif // GNASH_MOVIE_LOADER_H

=== modified file 'libcore/movie_root.cpp'
--- a/libcore/movie_root.cpp    2009-11-12 10:47:18 +0000
+++ b/libcore/movie_root.cpp    2009-11-12 21:00:31 +0000
@@ -139,7 +139,8 @@
        _timeoutLimit(15),
        _movieAdvancementDelay(83), // ~12 fps by default
        _lastMovieAdvancement(0),
-       _unnamedInstance(0)
+       _unnamedInstance(0),
+    _movieLoader(*this)
 {
     // This takes care of informing the renderer (if present) too.
     setQuality(QUALITY_HIGH);
@@ -187,22 +188,11 @@
        _intervalTimers.clear();
 }
 
-void
-movie_root::clearLoadMovieRequests()
-{
-    for (LoadMovieRequests::iterator it=_loadMovieRequests.begin(),
-            end = _loadMovieRequests.end(); it != end; ++it)
-    {
-        delete *it;
-    }
-    _loadMovieRequests.clear();
-}
-
 movie_root::~movie_root()
 {
        clearActionQueue();
        clearIntervalTimers();
-       clearLoadMovieRequests();
+       _movieLoader.clear();
 
        assert(testInvariant());
 }
@@ -518,7 +508,7 @@
        clearIntervalTimers();
 
     // remove all loadMovie requests
-       clearLoadMovieRequests();
+       _movieLoader.clear();
 
        // remove key/mouse listeners
        _keyListeners.clear();
@@ -888,7 +878,8 @@
     // NOTE: processing loadMovie requests after advanceLiveChars
     //       is known to fix more tests in misc-mtasc.all/levels.swf
     //       to be checked if it keeps the swfdec testsuite safe
-    processLoadMovieRequests();
+    //
+    _movieLoader.processCompletedRequests();
 
     // Process queued actions
     // NOTE: can throw ActionLimitException
@@ -1676,11 +1667,7 @@
     }
 
     // Mark LoadMovieRequest handlers as reachable
-    for (LoadMovieRequests::const_iterator it=_loadMovieRequests.begin(),
-            end = _loadMovieRequests.end(); it != end; ++it)
-    {
-        (*it)->setReachable();
-    }
+    _movieLoader.setReachable();
 
     // Mark resources reachable by queued action code
     for (int lvl=0; lvl<apSIZE; ++lvl)
@@ -2065,210 +2052,6 @@
 
 }
 
-void
-movie_root::loadMovie(const std::string& urlstr, const std::string& target,
-        const std::string& data, MovieClip::VariablesMethod method,
-        as_object* handler)
-{
-
-    /// URL security is checked in StreamProvider::getStream() down the
-    /// chain.
-    URL url(urlstr, _runResources.baseURL());
-
-    /// If the method is MovieClip::METHOD_NONE, we send no data.
-    if (method == MovieClip::METHOD_GET)
-    {
-        std::string varsToSend(urlstr);
-        /// GET: append data to query string.
-        std::string qs = url.querystring();
-        if ( qs.empty() ) varsToSend.insert(0, 1, '?');
-        else varsToSend.insert(0, 1, '&');
-        url.set_querystring(qs + varsToSend);
-    }
-
-    log_debug("movie_root::loadMovie(%s, %s)", url.str(), target);
-
-    const std::string* postdata = (method == MovieClip::METHOD_POST) ? &data
-                                                                     : 0;
-
-    _loadMovieRequests.push_front(
-        new LoadMovieRequest(url, target, postdata, handler)
-    );
-
-    // TODO: start thread 
-}
-
-void
-movie_root::processLoadMovieRequest(LoadMovieRequest& r)
-{
-    const URL& url = r.getURL();
-    bool usePost = r.usePost();
-    const std::string* postdata = usePost ? &(r.getPostData()) : 0;
-
-    // TODO: make this load asyncronous !!
-       boost::intrusive_ptr<movie_definition> md (
-        MovieFactory::makeMovie(url, _runResources, NULL, true, postdata));
-    r.setCompleted(md);
-
-    bool completed = processCompletedLoadMovieRequest(r);
-    assert(completed);
-}
-
-/* private */
-bool
-movie_root::processCompletedLoadMovieRequest(const LoadMovieRequest& r)
-{
-    GNASH_REPORT_FUNCTION;
-
-    boost::intrusive_ptr<movie_definition> md;
-    if ( ! r.getCompleted(md) ) return false; // not completed yet
-
-    const std::string& target = r.getTarget();
-    DisplayObject* targetDO = findCharacterByTarget(target);
-    as_object* handler = r.getHandler();
-
-    if ( ! md )
-    {
-
-        if ( targetDO && handler )
-        {
-            // Signal load error
-            // Tested not to happen if target isn't found at time of loading
-            //
-
-            as_value arg1(getObject(targetDO));
-
-            // FIXME: docs suggest the string can be either "URLNotFound" or
-            // "LoadNeverCompleted". This is neither of them:
-            as_value arg2("Failed to load movie or jpeg");
-
-            // FIXME: The last argument is HTTP status, or 0 if no connection
-            // was attempted (sandbox) or no status information is available
-            // (supposedly the Adobe mozilla plugin).
-            as_value arg3(0.0);
-
-            handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadError",
-                    arg1, arg2, arg3);
-        }
-        return true; // nothing to do, but completed
-    }
-
-    const URL& url = r.getURL();
-
-       Movie* extern_movie = md->createMovie(*_vm.getGlobal());
-       if (!extern_movie)
-    {
-               log_error(_("Can't create Movie instance "
-                    "for definition loaded from %s"), url);
-               return true; // completed in any case...
-       }
-
-       // Parse query string
-       MovieClip::MovieVariables vars;
-       url.parse_querystring(url.querystring(), vars);
-    extern_movie->setVariables(vars);
-
-    if (targetDO)
-    {
-        targetDO->getLoadedMovie(extern_movie);
-    }
-    else
-    {
-        unsigned int levelno;
-        if (isLevelTarget(target, levelno))
-        {
-            log_debug(_("processCompletedLoadMovieRequest: _level loading "
-                    "(level %u)"), levelno);
-               extern_movie->set_depth(levelno + 
DisplayObject::staticDepthOffset);
-            setLevel(levelno, extern_movie);
-        }
-        else
-        {
-            log_debug("Target %s of a loadMovie request doesn't exist at "
-                  "load complete time", target);
-            return true;
-        }
-    }
-
-    if (handler && targetDO)
-    {
-        // Dispatch onLoadStart
-        // FIXME: should be signalled before starting to load
-        //        (0/-1 bytes loaded/total) but still with *new*
-        //        display object as target (ie: the target won't
-        //        contain members set either before or after loadClip.
-        handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadStart",
-            getObject(targetDO));
-
-        // Dispatch onLoadProgress
-        // FIXME: should be signalled on every readNonBlocking()
-        //        with a buffer size of 65535 bytes.
-        //
-        size_t bytesLoaded = md->get_bytes_loaded();
-        size_t bytesTotal = md->get_bytes_total();
-        handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadProgress",
-            getObject(targetDO), bytesLoaded, bytesTotal);
-
-        // Dispatch onLoadComplete
-        // FIXME: find semantic of last arg
-        handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadComplete",
-            getObject(targetDO), as_value(0.0));
-
-
-        // Displatch onLoadInit
-        
-        // This event must be dispatched when actions
-        // in first frame of loaded clip have been executed.
-        //
-        // Since getLoadedMovie or setLevel above will invoke
-        // stagePlacementCallback and thus queue all actions in first
-        // frame, we'll queue the
-        // onLoadInit call next, so it happens after the former.
-        //
-        std::auto_ptr<ExecutableCode> code(
-                new DelayedFunctionCall(handler, NSV::PROP_BROADCAST_MESSAGE, 
-                    "onLoadInit", getObject(targetDO)));
-
-        getRoot(*handler).pushAction(code, movie_root::apDOACTION);
-    }
-
-    return true;
-
-}
-
-/* private */
-void
-movie_root::processCompletedLoadMovieRequests()
-{
-    GNASH_REPORT_FUNCTION;
-
-    for (LoadMovieRequests::iterator it=_loadMovieRequests.begin();
-            it != _loadMovieRequests.end(); )
-    {
-        const LoadMovieRequest* lr=*it;
-        if ( processCompletedLoadMovieRequest(*lr) )
-        {
-            it = _loadMovieRequests.erase(it);
-            delete lr;
-        }
-    }
-}
-
-void
-movie_root::processLoadMovieRequests()
-{
-#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
-    log_debug("Processing %d loadMovie requests", _loadMovieRequests.size());
-#endif
-    for (LoadMovieRequests::iterator it=_loadMovieRequests.begin();
-            it != _loadMovieRequests.end(); )
-    {
-        LoadMovieRequest* lr=*it;
-        processLoadMovieRequest(*lr);
-        it = _loadMovieRequests.erase(it);
-        delete lr;
-    }
-}
 
 bool
 movie_root::isLevelTarget(const std::string& name, unsigned int& levelno)

=== modified file 'libcore/movie_root.h'
--- a/libcore/movie_root.h      2009-11-12 10:48:38 +0000
+++ b/libcore/movie_root.h      2009-11-12 21:00:31 +0000
@@ -81,6 +81,7 @@
 #include "gnash.h" // Quality
 #include "MovieClip.h"
 #include "SimpleBuffer.h" // for LoadCallback
+#include "MovieLoader.h"
 
 #ifdef USE_SWFTREE
 # include "tree.hh"
@@ -187,6 +188,14 @@
     ///
     Movie* getLevel(unsigned int num) const;
 
+    /// Put the given movie at the given level 
+    //
+    /// @param movie
+    /// The Movie to store at the given level.
+    /// Its depth will be set to <num>+DisplayObject::staticDepthOffset and
+    /// its name to _level<num>
+    void setLevel(unsigned int num, Movie* movie);
+
     /// Replace an existing level with a new movie
     //
     /// Depth will be assigned to external_movie by this function.
@@ -708,7 +717,10 @@
     ///                 
     void loadMovie(const std::string& url, const std::string& target,
             const std::string& data, MovieClip::VariablesMethod method,
-            as_object* handler=0);
+            as_object* handler=0)
+    {
+        _movieLoader.loadMovie(url, target, data, method, handler);
+    }
 
     /// Send a request to the hosting application (e.g. browser).
     //
@@ -928,103 +940,6 @@
     /// Registered FsCommand handler, if any
     AbstractFsCallback* _fsCommandHandler;
 
-    /// A load movie request
-    class LoadMovieRequest : boost::noncopyable {
-    public:
-        /// @param postdata
-        ///   If not null POST method will be used for HTTP.
-        ///
-        LoadMovieRequest(const URL& u, const std::string& t,
-                const std::string* postdata, as_object* handler)
-                :
-                _target(t),
-                _url(u),
-                _usePost(false),
-                _mdef(0),
-                _completed(false),
-                _mutex(),
-                _handler(handler)
-        {
-            if ( postdata )
-            {
-                _postData = *postdata;
-                _usePost = true;
-            }
-        }
-
-        const std::string& getTarget() const { return _target; }
-        const URL& getURL() const { return _url; }
-        const std::string& getPostData() const { return _postData; }
-        bool usePost() const { return _usePost; }
-        as_object* getHandler() const { return _handler; }
-        void setReachable() const {
-            if (_handler) _handler->setReachable();
-        }
-
-        /// Get the loaded movie definition, if any
-        //
-        /// @param md the loaded movie_definition is copied here
-        ///           if it was impossible to create one.
-        ///
-        /// @return true if the request was completed, false otherwise.
-        ///
-        /// RULE: if return is FALSE param 'md' will be set to 0.
-        /// RULE: if return is TRUE  param 'md' may be set to 0 or non 0.
-        /// RULE: if parameter 'md' is set to non 0, TRUE must be returned.
-        ///
-        /// locks _mutex
-        ///
-        bool getCompleted(boost::intrusive_ptr<movie_definition>& md) const
-        {
-            boost::mutex::scoped_lock lock(_mutex);
-            md = _mdef;
-            return _completed;
-        }
-
-        /// Complete the request
-        //
-        /// @param md the loaded movie_definition, or 0 if
-        ///           it was impossible to create one.
-        ///
-        /// locks _mutex
-        ///
-        void setCompleted(boost::intrusive_ptr<movie_definition> md)
-        {
-            boost::mutex::scoped_lock lock(_mutex);
-            _mdef = md;
-            _completed = true;
-        }
-
-    private:
-        std::string _target;
-        URL _url;
-        bool _usePost;
-        std::string _postData;
-        boost::intrusive_ptr<movie_definition> _mdef;
-        bool _completed;
-        mutable boost::mutex _mutex;
-        as_object* _handler;
-    };
-
-    /// Load movie requests
-    typedef std::list<LoadMovieRequest*> LoadMovieRequests;
-    LoadMovieRequests _loadMovieRequests;
-
-    /// Process all load movie requests
-    void processLoadMovieRequests();
-
-    /// Process a single load movie request
-    void processLoadMovieRequest(LoadMovieRequest& r);
-
-    /// Check a LoadMovieRequest and process if completed.
-    //
-    /// @return true if the request was completely processed.
-    ///
-    bool processCompletedLoadMovieRequest(const LoadMovieRequest& r);
-
-    /// Process all completed loadMovie requests.
-    void processCompletedLoadMovieRequests();
-
     /// Listeners container
     typedef std::list<DisplayObject*> Listeners;
 
@@ -1037,9 +952,6 @@
     /// Delete all elements on the timers list
     void clearIntervalTimers();
 
-    /// Delete all LoadMovieRequests
-    void clearLoadMovieRequests();
-
     /// Execute expired timers
     void executeAdvanceCallbacks();
     
@@ -1103,14 +1015,6 @@
     /// Advance all non-unloaded live chars
     void advanceLiveChars();
 
-    /// Put the given movie at the given level 
-    //
-    /// @param movie
-    /// The Movie to store at the given level.
-    /// Its depth will be set to <num>+DisplayObject::staticDepthOffset and
-    /// its name to _level<num>
-    void setLevel(unsigned int num, Movie* movie);
-
     /// Boundaries of the Stage are always world boundaries
     /// and are only invalidated by changes in the background
     /// color.
@@ -1270,6 +1174,8 @@
 
     size_t _unnamedInstance;
 
+    MovieLoader _movieLoader;
+
 };
 
 DSOEXPORT short stringToStageAlign(const std::string& s);

=== modified file 'libcore/parser/SWFMovieDefinition.cpp'
--- a/libcore/parser/SWFMovieDefinition.cpp     2009-11-04 16:55:59 +0000
+++ b/libcore/parser/SWFMovieDefinition.cpp     2009-11-12 12:13:15 +0000
@@ -71,7 +71,7 @@
     template<typename T> void markMappedResources(const T& t);
 }
 
-MovieLoader::MovieLoader(SWFMovieDefinition& md)
+SWFMovieLoader::SWFMovieLoader(SWFMovieDefinition& md)
        :
        _movie_def(md),
        _thread(NULL),
@@ -79,7 +79,7 @@
 {
 }
 
-MovieLoader::~MovieLoader()
+SWFMovieLoader::~SWFMovieLoader()
 {
        // we should assert _movie_def._loadingCanceled
        // but we're not friend yet (anyone introduce us ?)
@@ -91,7 +91,7 @@
 }
 
 bool
-MovieLoader::started() const
+SWFMovieLoader::started() const
 {
        boost::mutex::scoped_lock lock(_mutex);
 
@@ -99,7 +99,7 @@
 }
 
 bool
-MovieLoader::isSelfThread() const
+SWFMovieLoader::isSelfThread() const
 {
        boost::mutex::scoped_lock lock(_mutex);
 
@@ -117,19 +117,19 @@
 
 // static..
 void
-MovieLoader::execute(MovieLoader& ml, SWFMovieDefinition* md)
+SWFMovieLoader::execute(SWFMovieLoader& ml, SWFMovieDefinition* md)
 {
        ml._barrier.wait(); // let _thread assignment happen before going on
        md->read_all_swf();
 }
 
 bool
-MovieLoader::start()
+SWFMovieLoader::start()
 {
 #ifndef LOAD_MOVIES_IN_A_SEPARATE_THREAD
     std::abort();
 #endif
-       // don't start MovieLoader thread() which rely
+       // don't start SWFMovieLoader thread() which rely
        // on boost::thread() returning before they are executed. Therefore,
        // we must employ locking.
        // Those tests do seem a bit redundant, though...

=== modified file 'libcore/parser/SWFMovieDefinition.h'
--- a/libcore/parser/SWFMovieDefinition.h       2009-11-04 15:03:15 +0000
+++ b/libcore/parser/SWFMovieDefinition.h       2009-11-12 12:13:15 +0000
@@ -63,13 +63,13 @@
 /// SWFMovieDefinition helper class handling start and execution of
 /// an SWF loading thread
 ///
-class MovieLoader
+class SWFMovieLoader
 {
 public:
 
-       MovieLoader(SWFMovieDefinition& md);
+       SWFMovieLoader(SWFMovieDefinition& md);
 
-       ~MovieLoader();
+       ~SWFMovieLoader();
 
        /// Start loading thread.
        //
@@ -79,10 +79,10 @@
        ///
        bool start();
 
-       /// Return true if the MovieLoader thread was started
+       /// Return true if the loader thread was started
        bool started() const;
 
-       /// Return true if called from the MovieLoader thread.
+       /// Return true if called from the loader thread.
        bool isSelfThread() const;
 
 private:
@@ -98,7 +98,7 @@
        boost::barrier _barrier;
 
        /// Entry point for the actual thread
-       static void execute(MovieLoader& ml, SWFMovieDefinition* md);
+       static void execute(SWFMovieLoader& ml, SWFMovieDefinition* md);
 
 };
 
@@ -512,7 +512,7 @@
     size_t _swf_end_pos;
 
        /// asyncronous SWF loader and parser
-       MovieLoader _loader;
+       SWFMovieLoader _loader;
 
        /// \brief
        /// Increment loaded frames count, signaling frame reached condition if

=== modified file 'testsuite/misc-ming.all/loadMovieTestRunner.cpp'
--- a/testsuite/misc-ming.all/loadMovieTestRunner.cpp   2009-10-28 13:33:35 
+0000
+++ b/testsuite/misc-ming.all/loadMovieTestRunner.cpp   2009-11-11 23:17:29 
+0000
@@ -116,8 +116,8 @@
        tester->movePointerTo(80, 80);
        check(tester->isMouseOverMouseEntity());
        tester->pressMouseButton();
+       usleep(5000); // give it some time... TODO: drop this test and use a 
self-containment instead
        tester->advance(); // loads (should) happen on next advance
-       usleep(500); // give it some time... TODO: drop this test and use a 
self-containment instead
        coverartch = 
const_cast<DisplayObject*>(tester->findDisplayItemByName(*root, "coverart"));
        check(coverart != coverartch->to_movie());
        coverart = coverartch->to_movie();
@@ -135,8 +135,8 @@
        tester->movePointerTo(280, 80);
        check(tester->isMouseOverMouseEntity());
        tester->click();
+       usleep(5000); // give it some time... TODO: drop this test and use a 
self-containment instead
        tester->advance(); // loads (should) happen on next advance
-       usleep(500); // give it some time... TODO: drop this test and use a 
self-containment instead
        coverartch = 
const_cast<DisplayObject*>(tester->findDisplayItemByName(*root, "coverart"));
        coverart = coverartch->to_movie();
        check_equals(coverart->get_root()->url(), greenURL.str());
@@ -154,8 +154,8 @@
        tester->movePointerTo(480, 80);
        check(tester->isMouseOverMouseEntity());
        tester->click();
+       usleep(5000); // give it some time... TODO: drop this test and use a 
self-containment instead
        tester->advance(); // loads (should) happen on next advance
-       usleep(500); // give it some time... TODO: drop this test and use a 
self-containment instead
        coverartch = 
const_cast<DisplayObject*>(tester->findDisplayItemByName(*root, "coverart"));
        coverart = coverartch->to_movie();
        check_equals(coverart->get_root()->url(), offspringURL.str());

=== modified file 'testsuite/movies.all/gravity_embedded-TestRunner.cpp'
--- a/testsuite/movies.all/gravity_embedded-TestRunner.cpp      2009-11-04 
13:40:12 +0000
+++ b/testsuite/movies.all/gravity_embedded-TestRunner.cpp      2009-11-11 
23:17:29 +0000
@@ -31,6 +31,7 @@
 #include "check.h"
 #include <string>
 #include <cassert>
+#include <unistd.h> // for usleep
 
 using namespace gnash;
 using namespace std;
@@ -56,8 +57,9 @@
 
        check_equals(root->get_frame_count(), 1);
 
-       // give loader time to load the actual gravity.swf movie ?
-       //sleep(1);
+       // give loader time to load the actual gravity.swf movie 
+       usleep(5000);
+       tester.advance(); // have load processed
 
        // used to get members
        as_value tmp;

=== modified file 'testsuite/swfdec/PASSING'
--- a/testsuite/swfdec/PASSING  2009-11-10 00:59:35 +0000
+++ b/testsuite/swfdec/PASSING  2009-11-11 23:18:13 +0000
@@ -308,6 +308,8 @@
 crash-0.5.5-stylesheet-update-7.swf:c112cfe1b313fb437fe45cb0c3a7b665
 crash-0.5.5-stylesheet-update-8.swf:73a61afbd582fabd2dbba360cc3763e4
 crash-0.5.90-empty-action.swf:c564ca09c4bc71fb89b042bc07d247eb
+crash-0.6.0-moviecliploader-7.swf:c829c5b17aed2c9d8a1720f826299e67
+crash-0.6.0-moviecliploader-8.swf:38c04c5f96ffedeba09ee5a2aea17a3f
 crash-0.6.2-replaceText-5.swf:f9a3132918e7d3daaca3bb8122b8b847
 crash-0.6.2-replaceText-6.swf:26a8a906a7ab6dc2411d0c72d84cbdfa
 crash-0.6.2-replaceText-7.swf:da9ccc92ab99681f702edc62d143edf7

=== modified file 'testsuite/swfdec/REALTIME'
--- a/testsuite/swfdec/REALTIME 2008-05-28 15:23:26 +0000
+++ b/testsuite/swfdec/REALTIME 2009-11-11 23:35:17 +0000
@@ -1,13 +1,28 @@
+instance-name-loaded-5.swf
+instance-name-loaded-6.swf
+instance-name-loaded-7.swf
+loadmovie-case-5.swf
+loadmovie-case-6.swf
+loadmovie-case-7.swf
 loadvariables-5.swf
 loadvariables-6.swf
 loadvariables-7.swf
-netstream-onmetadata.swf
-netstream-onstatus.swf
-netstream-dimensions.swf
+movieclip-get-swf-version-load-5.swf
+movieclip-get-swf-version-load-6.swf
+movieclip-get-swf-version-load-7.swf
+movieclip-get-swf-version-load-8.swf
+moviecliploader-known-image-size-7.swf
+moviecliploader-known-image-size-8.swf
+movieclip-lockroot-loadmovie-6.swf
+movieclip-lockroot-loadmovie-7.swf
+movieclip-lockroot-loadmovie-8.swf
 movieclip-version-5.swf
 movieclip-version-6.swf
 movieclip-version-7.swf
 movieclip-version-8.swf
+netstream-dimensions.swf
+netstream-onmetadata.swf
+netstream-onstatus.swf
 sec-0.6.2-local-access-5.swf
 sec-0.6.2-local-access-6.swf
 sec-0.6.2-local-access-7.swf

=== modified file 'utilities/processor.cpp'
--- a/utilities/processor.cpp   2009-10-21 07:24:55 +0000
+++ b/utilities/processor.cpp   2009-11-11 23:18:05 +0000
@@ -601,6 +601,9 @@
                 cl.elapsed(), localDelay);
         gnashSleep(localDelay);
     }
+
+    // clear movie_root (too early?)
+    m.clear();
  
     // Clear resources.
     gnash::clear();


reply via email to

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