diff --git a/SConstruct b/SConstruct index 8a2316996..7a1aa2592 100644 --- a/SConstruct +++ b/SConstruct @@ -257,7 +257,7 @@ def configurePlatform(env): if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): problems.append('Unable to configure Lib glib--, exiting.') - if not conf.CheckPkgConfig('gthread-2.0', '2.16'): + if not conf.CheckPkgConfig('gthread-2.0', '2.12.4'): problems.append('Need gthread support lib for glib-- based thread handling.') if not conf.CheckPkgConfig('cairomm-1.0', 0.6): diff --git a/configure.ac b/configure.ac index 1b2ec96dc..0cf1d61bb 100644 --- a/configure.ac +++ b/configure.ac @@ -141,8 +141,12 @@ PKG_CHECK_MODULES(LUMIERA_COMMON_LIBS, [sigc++-2.0 >= 2.0.17]) # gtk+-2.0 >= 2.12 gtkmm-2.4 >= 2.12 for Debian Lenny compatibility PKG_CHECK_MODULES(LUMIERA_GUI, [ - gtk+-2.0 >= 2.8 gtkmm-2.4 >= 2.8 gdl-1.0 >= 0.6.1 - cairomm-1.0 >= 0.6.0 gavl >= 0.2.5 librsvg-2.0 >= 2.18.1 + gtk+-2.0 >= 2.8 + gtkmm-2.4 >= 2.8 + cairomm-1.0 >= 0.6.0 + librsvg-2.0 >= 2.18.1 + gdl-1.0 >= 0.6.1 + gavl >= 0.2.5 gthread-2.0 >= 2.12.4]) # END Gtk Dependancies diff --git a/doc/devel/draw/PlayerArch-1.svg b/doc/devel/draw/PlayerArch-1.svg new file mode 100644 index 000000000..519d5317d --- /dev/null +++ b/doc/devel/draw/PlayerArch-1.svg @@ -0,0 +1,468 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + AllocationCluster + + + Ichthyostega + + + design sketch: Structure of the AllocationCluster mem manager + 2008 + + + + + + + + + + + + + + + + + play thread + put(Frame&) + PlaybackController + + + start(...) + PlayProcess + Player (interface) + start_playback_thread() + + + yields + GUI (Plugin) + Proc (or Backend?) + + Displayer (Proxy) + PlayerFacade (Proxy) + + + actuallytalks to... + libCommon + + + PlayContext + PlayerService + + + + PlayContext + PlayContext + PlayContext + + diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index 3c60cda09..d4dfc1e70 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -42,8 +42,89 @@ namespace lib { using std::tr1::function; using lumiera::Literal; + typedef struct nobug_flag* NoBugFlag; + + class Thread; /** + * Brainstorming-in-code: how I would like to shape the API for joining threads. + * Intended use: This non-copyable handle has to be created within the thread which + * wants to wait-blocking on the termination of another thread. You then pass it + * into the ctor of the Thread starting wrapper class (see below), which causes + * the embedded lock/condition var to be used to sync on the end of the newly + * created thread. Note, after ending the execution, the newly created thread + * will be on hold until either the #join() function is called or this handle + * goes out of scope altogether. Explanation: this is implemented by locking + * the embedded monitor immediately in the ctor. Thus, unless entering the + * wait state, the contained mutex remains locked and prevents the thread + * manager from invoking the broadcast() on the condition var. + * + * @note this is a draft. It doesn't even work, because Cehteh is still planning + * details of the thread handling and didn't implement the waiting feature. + */ + class JoinHandle + : public Sync + , Sync::Lock + { + typedef Sync SyncBase; + + bool isWaiting_; + volatile bool armed_; + + friend class Thread; + + LumieraCondition + accessLockedCondition() + { + ASSERT (!armed_, "Lifecycle error, JoinHandle used for several threads."); + armed_ = true; + return accessMonitor().accessCond(); + } + + bool + wakeupCheck() + { + if (!armed_) + throw lumiera::error::Logic ("no thread created blocking on this JoinHandle"); + + if (!isWaiting_) + { + isWaiting_ = true; + return false; // causes entering the blocking wait + } + TODO ("any possibility to detect spurious wakeups? can they happen?"); + return true; // causes end of the blocking wait + } + + + public: + /** Create a promise, that the current thread will or may + * wait-blocking on another not-yet existing thread to terminate. + * When passed in on creation of the other thread, as long as this + * handle lives, the other thread will be on hold after termination. + */ + JoinHandle() + : SyncBase::Lock(this) + , isWaiting_(false) + , armed_(false) + { } + + /** put the current thread into a blocking wait until another thread + * has terminated. This other thread needs to be created by the Thread + * wrapper, passing this JoinHandle as ctor parameter. + * @throws error::Logic if no thread has been registered to block on this + */ + void + join() + { + accessMonitor().wait (*this, &JoinHandle::wakeupCheck); + } + }; + + + + + /**************************************************************************** * A thin convenience wrapper for dealing with threads, * as implemented by the backend (on top of pthread). * Using this wrapper... @@ -63,8 +144,8 @@ namespace lib { * is superfluous in the final application. Re-evaluate this! */ class Thread - : public Sync, - boost::noncopyable + : public Sync + , boost::noncopyable { volatile bool started_; @@ -87,17 +168,15 @@ namespace lib { } - public: - Thread (Literal& purpose, Operation const& operation, struct nobug_flag *logging_flag = &NOBUG_FLAG(operate)) - : started_(false), - operation_(operation) + void + start_thread (lumiera_thread_class kind, Literal& purpose, NoBugFlag logging_flag, LumieraCondition joinCond=0) { Lock sync(this); LumieraThread res = - lumiera_thread_run ( LUMIERA_THREAD_INTERACTIVE + lumiera_thread_run ( kind , &run // invoking the run helper and.. , this // passing this start context as parameter - , 0 // no condition variable provided (for now...) + , joinCond // maybe wait-blocking for the thread to terminate , purpose.c_str() , logging_flag ); @@ -109,6 +188,40 @@ namespace lib { // prior to leaving and thereby possibly destroying this local context sync.wait (started_); } + + public: + /** Create a new thread to execute the given operation. + * The new thread starts up synchronously, i.e. when the ctor returns, the new thread + * has started running and taken over (copied) the operation functor passed in. The + * thread will be created by lumiera_thread_run (declared in threads.h), it can't + * be cancelled and it can't be joined. + * @param purpose fixed char string used to denote the thread for diagnostics + * @param logging_flag NoBug flag to receive diagnostics regarding the new thread + * @param operation defining what to execute within the new thread. Any functor + * which can be bound to function. Note this functor will be + * copied onto the stack of the new thread, thus it can be transient. + * + */ + Thread (Literal& purpose, Operation const& operation, NoBugFlag logging_flag = &NOBUG_FLAG(operate)) ///TODO: define a dedicated flag for threads + : started_(false), + operation_(operation) + { + start_thread (LUMIERA_THREAD_INTERACTIVE, purpose, logging_flag); + } + + /** Variant of the standard case, used to register a JoinHandle in addition to starting a thread. + * @param join ref to a JoinHandle, which needs to be created in the thread which plans + * to wait-blocking on the termination of this newly created thread + * + */ + Thread (Literal& purpose, Operation const& operation, + JoinHandle& join, NoBugFlag logging_flag = &NOBUG_FLAG(operate)) ///TODO: define a dedicated flag for threads + : started_(false), + operation_(operation) + { + start_thread (LUMIERA_THREAD_INTERACTIVE, purpose, logging_flag, + join.accessLockedCondition()); + } }; diff --git a/src/common/guifacade.cpp b/src/common/guifacade.cpp index 408c7b1d0..4adb83d47 100644 --- a/src/common/guifacade.cpp +++ b/src/common/guifacade.cpp @@ -60,18 +60,18 @@ namespace gui { : theGUI_("lumieraorg_Gui", 1, 1, "lumieraorg_GuiStarterPlugin") // load GuiStarterPlugin { ASSERT (theGUI_); - this->kickOff (terminationHandle); + bool res = this->kickOff (terminationHandle); - if (lumiera_error_peek()) + if (!res || lumiera_error_peek()) throw lumiera::error::Fatal("failed to bring up GUI",lumiera_error()); } ~GuiRunner () { } - void kickOff (Subsys::SigTerm& terminationHandle) + bool kickOff (Subsys::SigTerm& terminationHandle) { - theGUI_->kickOff (reinterpret_cast (&terminationHandle)); + return theGUI_->kickOff (reinterpret_cast (&terminationHandle)); } }; diff --git a/src/common/interfaceproxy.cpp b/src/common/interfaceproxy.cpp index 6cbc8b4cb..bf7607f17 100644 --- a/src/common/interfaceproxy.cpp +++ b/src/common/interfaceproxy.cpp @@ -40,6 +40,17 @@ namespace gui { +#include "include/dummy-player-facade.h" + +namespace proc { + + /** storage for the DummyPlayer facade proxy factory... */ + lumiera::facade::Accessor DummyPlayer::facade; + +} // namespace gui + + + namespace lumiera { namespace facade { @@ -107,7 +118,9 @@ namespace lumiera { - typedef InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_GuiNotification, 1) + /* ==================== GuiNotification =================================== */ + + typedef InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_GuiNotification, 0) , gui::GuiNotification > Handle_GuiNotification; @@ -127,12 +140,55 @@ namespace lumiera { }; - - template void openProxy (Handle_GuiNotification const&); template void closeProxy (void); + + + + + + /* ==================== DummyPlayer ======================================= */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , proc::DummyPlayer + > Handle_DummyPlayer; + + + template<> + class Proxy + : public Holder + { + //----Proxy-Implementation-of-DummyPlayer-------- + typedef proc::DummyPlayer::Process Process; + + /** @note as an optimisation we hand out a direct reference + * to the implementing process object. While this ref could + * still be passed as handle to the C Language interface, using + * it directly within the client (=GUI) retains only on level + * of indirection, irrespective which interface is used. */ + Process& start() + { + Process* pP = static_cast (_i_.startPlay()); + + if (!pP || lumiera_error_peek()) + throw lumiera::error::State("failed to start DummyPlayer", lumiera_error()); + + return *pP; + } + + + + public: + Proxy (IHandle const& iha) : THolder(iha) {} + }; + + + template void openProxy (Handle_DummyPlayer const&); + template void closeProxy (void); + + } // namespace facade } // namespace lumiera diff --git a/src/gui/controller/controller.hpp b/src/gui/controller/controller.hpp index 27f185e97..594f5e19e 100644 --- a/src/gui/controller/controller.hpp +++ b/src/gui/controller/controller.hpp @@ -23,11 +23,12 @@ ** This file contains the definition of the controller object */ -#include "playback-controller.hpp" - #ifndef CONTROLLER_HPP #define CONTROLLER_HPP +#include "playback-controller.hpp" + + namespace gui { namespace model { diff --git a/src/gui/controller/playback-controller.cpp b/src/gui/controller/playback-controller.cpp index 10e370cd3..f360ca6a5 100644 --- a/src/gui/controller/playback-controller.cpp +++ b/src/gui/controller/playback-controller.cpp @@ -1,5 +1,5 @@ /* - timeline-track.cpp - Implementation of the timeline track object + playback-controller.cpp - Implementation of the playback controller object Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -20,52 +20,79 @@ * *****************************************************/ -#include "playback-controller.hpp" -#include "../gtk-lumiera.hpp" +#include "gui/controller/playback-controller.hpp" +#include "lib/error.hpp" +#include namespace gui { namespace controller { PlaybackController::PlaybackController() : + thread(0), finish_playback_thread(false), - playing(false) -{ - start_playback_thread(); -} + playing(false), + playHandle(0) +{ } + PlaybackController::~PlaybackController() { - mutex.lock(); - finish_playback_thread = true; - mutex.unlock(); - thread->join(); + end_playback_thread(); } void PlaybackController::play() { - Glib::Mutex::Lock lock(mutex); - playing = true; + if (playing && thread && playHandle) + { + playHandle->pause(false); + return; + } + if (thread) + end_playback_thread(); + + { + Lock sync(this); + try + { + playHandle = & (proc::DummyPlayer::facade().start()); + start_playback_thread(); + playing = true; + } + catch (lumiera::error::State& err) + { + WARN (operate, "failed to start playback: %s" ,err.what()); + lumiera_error(); + playing = false; + } + } } void PlaybackController::pause() { - Glib::Mutex::Lock lock(mutex); + Lock sync(this); playing = false; + if (playHandle) + playHandle->pause(true); } void PlaybackController::stop() { - Glib::Mutex::Lock lock(mutex); - playing = false; + { + Lock sync(this); + playing = false; + playHandle = 0; + // TODO: stop player somehow? + } + end_playback_thread(); } bool PlaybackController::is_playing() { - Glib::Mutex::Lock lock(mutex); + Lock sync(this); return playing; } @@ -73,10 +100,25 @@ void PlaybackController::start_playback_thread() { dispatcher.connect(sigc::mem_fun(this, &PlaybackController::on_frame)); + finish_playback_thread = false; thread = Glib::Thread::create (sigc::mem_fun( this, &PlaybackController::playback_thread), true); } +void +PlaybackController::end_playback_thread() +{ + { + Lock sync(this); + finish_playback_thread = true; + playing = false; + } + if (thread) + thread->join(); + thread = 0; + finish_playback_thread = false; +} + void PlaybackController::attach_viewer( const sigc::slot& on_frame) @@ -90,115 +132,43 @@ PlaybackController::playback_thread() for(;;) { { - Glib::Mutex::Lock lock(mutex); + Lock sync(this); if(finish_playback_thread) return; } if(is_playing()) pull_frame(); - - Glib::Thread::yield(); + + usleep(40000); // ca 25 frames pre second } } -typedef unsigned char byte; - -inline int -clamp(const int &val, const int &maxval, const int &minval) -{ - if(val > maxval) return maxval; - if(val < minval) return minval; - return val; -} - -inline void -rgb_to_yuv(int r, int g, int b, byte &y, byte &u, byte &v) -{ - // This code isn't great, but it does the job - y = (byte)clamp((299 * r + 587 * g + 114 * b) / 1000, 235, 16); - v = (byte)clamp((500 * r - 419 * g - 81 * b) / 1000 + 127, 255, 0); - u = (byte)clamp((-169 * r - 331 * g + 500 * b) / 1000 + 127, 255, 0); -} - -void rgb_buffer_to_yuy2(unsigned char *in, unsigned char *out) -{ - for(int i = 0; i < 320*240*2; i+=4) - { - byte y0, u0, v0; - const byte r0 = *(in++); - const byte g0 = *(in++); - const byte b0 = *(in++); - rgb_to_yuv(r0, g0, b0, y0, u0, v0); - - byte y1, u1, v1; - const byte r1 = *(in++); - const byte g1 = *(in++); - const byte b1 = *(in++); - rgb_to_yuv(r1, g1, b1, y1, u1, v1); - - out[i] = y0; - out[i + 1] = u0; - out[i + 2] = y1; - out[i + 3] = v0; - } -} void PlaybackController::pull_frame() { - static int frame = 0; - unsigned char in[320 * 240 * 3]; + REQUIRE (is_playing()); + REQUIRE (playHandle); - frame--; + unsigned char * newBuffer = reinterpret_cast (playHandle->getFrame()); - if(frame <= 0) - frame = 200; - - if(frame > 150) - { - for(int i = 0; i < 320*240*3; i+=3) - { - byte value = (byte)rand(); - in[i] = value; - in[i+1] = value; - in[i+2] = value; - } - } - else - { - unsigned char row[320 * 3]; - - for(int x = 0; x < 320; x++) - { - byte &r = row[x*3]; - byte &g = row[x*3+1]; - byte &b = row[x*3+2]; - - if(x < 1*320/7) r = 0xC0, g = 0xC0, b = 0xC0; - else if(x < 2*320/7) r = 0xC0, g = 0xC0, b = 0x00; - else if(x < 3*320/7) r = 0x00, g = 0xC0, b = 0xC0; - else if(x < 4*320/7) r = 0x00, g = 0xC0, b = 0x00; - else if(x < 5*320/7) r = 0xC0, g = 0x00, b = 0xC0; - else if(x < 6*320/7) r = 0xC0, g = 0x00, b = 0x00; - else r = 0x00, g = 0x00, b = 0xC0; - } - - for(int y = 0; y < 240; y++) + if (newBuffer != currentBuffer) { - memcpy(in + y*320*3, row, sizeof(row)); + currentBuffer = newBuffer; + dispatcher.emit(); + } + else + { + TRACE (render, "frame dropped?"); } - } - - rgb_buffer_to_yuy2(in, buffer); - - dispatcher.emit(); } + void PlaybackController::on_frame() { - frame_signal.emit(buffer); + frame_signal.emit(currentBuffer); } } // namespace controller diff --git a/src/gui/controller/playback-controller.hpp b/src/gui/controller/playback-controller.hpp index 9333fcbc2..8b9a0916d 100644 --- a/src/gui/controller/playback-controller.hpp +++ b/src/gui/controller/playback-controller.hpp @@ -23,16 +23,26 @@ ** This file contains the definition of the playback controller object */ -#include -#include - #ifndef PLAYBACK_CONTROLLER_HPP #define PLAYBACK_CONTROLLER_HPP +#include "include/dummy-player-facade.h" +#include "lib/sync.hpp" + +#include +#include +#include + namespace gui { -namespace controller { +namespace controller { + +using lib::Sync; +using lib::RecursiveLock_NoWait; + class PlaybackController + : boost::noncopyable, + public Sync { public: @@ -54,6 +64,8 @@ private: void start_playback_thread(); + void end_playback_thread(); + void playback_thread(); void pull_frame(); @@ -64,15 +76,15 @@ private: Glib::Thread *thread; - Glib::StaticMutex mutex; - Glib::Dispatcher dispatcher; volatile bool finish_playback_thread; volatile bool playing; - unsigned char buffer[320 * 240 * 4]; + proc::DummyPlayer::Process *playHandle; + + unsigned char * currentBuffer; sigc::signal frame_signal; }; diff --git a/src/gui/guifacade.hpp b/src/gui/guifacade.hpp index 5afa230f0..212a62b47 100644 --- a/src/gui/guifacade.hpp +++ b/src/gui/guifacade.hpp @@ -44,6 +44,8 @@ extern "C" { #include "common/interface.h" } +#include + namespace gui { @@ -69,6 +71,7 @@ namespace gui { * */ class GuiFacade + : boost::noncopyable { public: @@ -91,7 +94,7 @@ namespace gui { * @internal this function is invoked automatically during the GUI * loading and startup process. Don't call it manually. */ - virtual void kickOff (lumiera::Subsys::SigTerm&) =0; + virtual bool kickOff (lumiera::Subsys::SigTerm&) =0; protected: @@ -100,7 +103,7 @@ namespace gui { /** interface of the GuiStarterPlugin */ LUMIERA_INTERFACE_DECLARE (lumieraorg_Gui, 1, - LUMIERA_INTERFACE_SLOT (void, kickOff, (void*)) + LUMIERA_INTERFACE_SLOT (bool, kickOff, (void*)) ); diff --git a/src/gui/guistart.cpp b/src/gui/guistart.cpp index e0e3b2930..08311797c 100644 --- a/src/gui/guistart.cpp +++ b/src/gui/guistart.cpp @@ -70,6 +70,7 @@ using std::string; using lib::Thread; using std::tr1::bind; using lumiera::Subsys; +using lumiera::error::LUMIERA_ERROR_STATE; using gui::LUMIERA_INTERFACE_INAME(lumieraorg_Gui, 1); @@ -77,7 +78,7 @@ namespace gui { namespace { // implementation details - /** + /****************************************************************************** * Implement the necessary steps for actually making the Lumiera Gui available. * Open the business interface(s) and start up the GTK GUI main event loop. */ @@ -133,10 +134,22 @@ namespace gui { } // (End) impl details - void + + + bool kickOff (Subsys::SigTerm& terminationHandle) { - Thread ("GUI-Main", bind (&runGUI, terminationHandle)); + try + { + Thread ("GUI-Main", bind (&runGUI, terminationHandle)); + return true; // if we reach this line... + } + catch(...) + { + if (!lumiera_error_peek()) + LUMIERA_ERROR_SET (gui, STATE, "unexpected error when starting the GUI thread"); + return false; + } } } // namespace gui @@ -226,9 +239,9 @@ extern "C" { /* ================== define an lumieraorg_Gui instance =========== , NULL /* on open */ , NULL /* on close */ , LUMIERA_INTERFACE_INLINE (kickOff, "\255\142\006\244\057\170\152\312\301\372\220\323\230\026\200\065", - void, (void* termSig), + bool, (void* termSig), { - gui::kickOff (*reinterpret_cast (termSig)); + return gui::kickOff (*reinterpret_cast (termSig)); } ) ) diff --git a/src/gui/notification-service.cpp b/src/gui/notification-service.cpp index f713d7390..eca7c268f 100644 --- a/src/gui/notification-service.cpp +++ b/src/gui/notification-service.cpp @@ -139,7 +139,7 @@ namespace gui { - LUMIERA_INTERFACE_INSTANCE (lumieraorg_GuiNotification, 1 + LUMIERA_INTERFACE_INSTANCE (lumieraorg_GuiNotification, 0 ,lumieraorg_GuiNotificationFacade , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_GuiNotificationFacade_descriptor) , NULL /* on open */ @@ -171,7 +171,7 @@ namespace gui { NotificationService::NotificationService () : implInstance_(this,_instance), - serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_GuiNotification, 1,lumieraorg_GuiNotificationFacade)) + serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_GuiNotification, 0,lumieraorg_GuiNotificationFacade)) { INFO (operate, "GuiNotification Facade opened."); } diff --git a/src/gui/notification-service.hpp b/src/gui/notification-service.hpp index 41e404e46..63ef677a4 100644 --- a/src/gui/notification-service.hpp +++ b/src/gui/notification-service.hpp @@ -73,7 +73,7 @@ namespace gui { /* === Interface Lifecycle === */ - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_GuiNotification, 1) + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_GuiNotification, 0) , GuiNotification > ServiceInstanceHandle; diff --git a/src/include/dummy-player-facade.h b/src/include/dummy-player-facade.h new file mode 100644 index 000000000..2899096c0 --- /dev/null +++ b/src/include/dummy-player-facade.h @@ -0,0 +1,122 @@ +/* + DUMMY-PLAYER-FACADE.hpp - access point to a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef PROC_INTERFACE_DUMMYPLAYER_H +#define PROC_INTERFACE_DUMMYPLAYER_H + + + +struct lumiera_playprocess_struct { }; +typedef struct lumiera_playprocess_struct lumiera_playprocess; +typedef lumiera_playprocess* LumieraPlayProcess; + + + + +#ifdef __cplusplus /* ============== C++ Interface ================= */ + +#include "common/subsys.hpp" +#include "include/interfaceproxy.hpp" + +#include + + + +namespace proc { + + + + /****************************************************************** + * Interface Proc-Layer (or maybe the backend?): + * Global access point for starting a dummy playback, generating + * some test image data for the GUI to display in a viewer window. + * + * This is a mockup service we created 1/2009 to collect some + * experiences regarding integration of the application layers. + * Lumiera is not yet able actually to deliver rendered video data. + * + */ + class DummyPlayer + { + public: + /** provide a descriptor for lumiera::AppState, + * wired accordingly to allow main to deal with + * the dummy player as independent subsystem. */ + static lumiera::Subsys& getDescriptor(); + + /** get an implementation instance of this service */ + static lumiera::facade::Accessor facade; + + + /** + * Continuous playback process, which has been started with a specific + * output size, format and framerate. It is a handle to a calculation process, + * which is about to produce a stream of frames to be retrieved by calling + * the #getFrame function on this handle. + * + * @todo solve the lifecycle and ownership! + */ + class Process + : public lumiera_playprocess + , boost::noncopyable + { + public: + virtual void pause(bool) =0; + virtual void* const getFrame() =0; + + virtual ~Process(); + }; + + + //////////////////TODO: define some dummy negotiation about size and framerate.... + + virtual Process& start() =0; + + virtual ~DummyPlayer(); + }; + + + + +} // namespace proc + + + +extern "C" { +#endif /* =========================== CL Interface ===================== */ + + +#include "common/interface.h" + +LUMIERA_INTERFACE_DECLARE (lumieraorg_DummyPlayer, 0 + , LUMIERA_INTERFACE_SLOT (LumieraPlayProcess, startPlay,(void) ) + , LUMIERA_INTERFACE_SLOT (void, pausePlay,(LumieraPlayProcess, bool)) + , LUMIERA_INTERFACE_SLOT (void, terminate,(LumieraPlayProcess) ) + , LUMIERA_INTERFACE_SLOT (void *, getFrame, (LumieraPlayProcess) ) +); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/guinotificationfacade.h b/src/include/guinotificationfacade.h index 9f4f91474..f3e334bff 100644 --- a/src/include/guinotificationfacade.h +++ b/src/include/guinotificationfacade.h @@ -85,7 +85,7 @@ extern "C" { #include "common/interface.h" -LUMIERA_INTERFACE_DECLARE (lumieraorg_GuiNotification, 1, +LUMIERA_INTERFACE_DECLARE (lumieraorg_GuiNotification, 0, LUMIERA_INTERFACE_SLOT (void, displayInfo, (const char*)), LUMIERA_INTERFACE_SLOT (void, triggerGuiShutdown, (const char*)), ); diff --git a/src/include/nobugcfg.h b/src/include/nobugcfg.h index 37b336503..a8d3d7d5a 100644 --- a/src/include/nobugcfg.h +++ b/src/include/nobugcfg.h @@ -83,15 +83,19 @@ namespace lumiera { /* declare flags used throughout the code base */ NOBUG_DECLARE_FLAG (all); NOBUG_DECLARE_FLAG (lumiera_all); + NOBUG_DECLARE_FLAG (lib_all); NOBUG_DECLARE_FLAG (lumiera); ///< master log, informative console output NOBUG_DECLARE_FLAG (operate); ///< logging channel reporting what the application does NOBUG_DECLARE_FLAG (render); ///< logging channel focusing on the render engine's workings NOBUG_DECLARE_FLAG (config); ///< logging channel covering application and session configuration NOBUG_DECLARE_FLAG (memory); ///< logging channel covering memory management issues - NOBUG_DECLARE_FLAG (sync); ///< especially for tracing synchronisation NOBUG_DECLARE_FLAG (test); + /* further flags which don't fit into any specific translation unit */ + NOBUG_DECLARE_FLAG (sync); ///< especially for tracing synchronisation + + #endif /*NOBUGCFG_H ======= (End) Part 1: DECLARATIONS ======== */ @@ -105,15 +109,18 @@ namespace lumiera { /* flags used throughout the code base... */ NOBUG_CPP_DEFINE_FLAG (all); NOBUG_CPP_DEFINE_FLAG_PARENT (lumiera_all, all); + NOBUG_CPP_DEFINE_FLAG_PARENT (lib_all, all); + NOBUG_CPP_DEFINE_FLAG_PARENT (lumiera, lumiera_all); NOBUG_CPP_DEFINE_FLAG_PARENT (config, lumiera); NOBUG_CPP_DEFINE_FLAG_PARENT (operate, lumiera); NOBUG_CPP_DEFINE_FLAG_PARENT_LIMIT (render, lumiera, LOG_WARNING); NOBUG_CPP_DEFINE_FLAG_PARENT_LIMIT (memory, lumiera, LOG_WARNING); - NOBUG_CPP_DEFINE_FLAG_PARENT_LIMIT (sync, memory, LOG_WARNING); NOBUG_CPP_DEFINE_FLAG_PARENT_LIMIT (test, all, LOG_ERR); + NOBUG_CPP_DEFINE_FLAG_PARENT_LIMIT (sync, lib_all, LOG_WARNING); + #endif /*NOBUG_INIT_DEFS_ ==== (End) Part 2: DEFINITIONS ========= */ diff --git a/src/lib/exception.cpp b/src/lib/exception.cpp index d59928025..091319110 100644 --- a/src/lib/exception.cpp +++ b/src/lib/exception.cpp @@ -79,7 +79,7 @@ namespace lumiera { desc_ (description), cause_ ("") { - lumiera_error_set (this->id_, description.c_str ()); + lumiera_error_set (this->id_, description.c_str ()); } diff --git a/src/lib/singleton-ref.hpp b/src/lib/singleton-ref.hpp index f3fad3921..1adf14698 100644 --- a/src/lib/singleton-ref.hpp +++ b/src/lib/singleton-ref.hpp @@ -78,7 +78,7 @@ namespace lib { } - typedef void* _ThisType::*unspecified_bool_type; + typedef TY* _ThisType::*unspecified_bool_type; /** implicit conversion to "bool" */ operator unspecified_bool_type() const // never throws diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index ef08e7f83..4abc55993 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -331,6 +331,8 @@ namespace lib { void setTimeout(ulong relative) {timeout_.setOffset(relative);} bool isTimedWait() {return (timeout_);} + + LumieraCondition accessCond() {return static_cast (this);} }; typedef Mutex NonrecursiveLock_NoWait; @@ -419,6 +421,11 @@ namespace lib { /** for creating a ClassLock */ Lock(Monitor& m) : mon_(m) { mon_.acquireLock(); } + + /** for controlled access to the + * underlying sync primitives */ + Monitor& + accessMonitor() { return mon_; } }; diff --git a/src/lumiera/main.cpp b/src/lumiera/main.cpp index 9d0317102..05e732a9f 100644 --- a/src/lumiera/main.cpp +++ b/src/lumiera/main.cpp @@ -30,6 +30,7 @@ #include "backend/enginefacade.hpp" #include "backend/netnodefacade.hpp" #include "backend/scriptrunnerfacade.hpp" +#include "include/dummy-player-facade.h" #include "proc/facade.hpp" #include "gui/guifacade.hpp" @@ -42,6 +43,7 @@ namespace { Subsys& engine = backend::EngineFacade::getDescriptor(); Subsys& netNode = backend::NetNodeFacade::getDescriptor(); Subsys& script = backend::ScriptRunnerFacade::getDescriptor(); + Subsys& player = proc::DummyPlayer::getDescriptor(); Subsys& builder = proc::Facade::getBuilderDescriptor(); Subsys& session = proc::Facade::getSessionDescriptor(); Subsys& lumigui = gui::GuiFacade::getDescriptor(); @@ -66,6 +68,7 @@ main (int argc, const char* argv[]) netNode.depends (engine); // lumigui.depends (session); //////TODO commented out in order to be able to start up a dummy GuiStarterPlugin // lumigui.depends (engine); + lumigui.depends (player); script.depends (session); script.depends (engine); diff --git a/src/proc/Makefile.am b/src/proc/Makefile.am index c0058adfa..ad41212ee 100644 --- a/src/proc/Makefile.am +++ b/src/proc/Makefile.am @@ -122,6 +122,18 @@ liblumiproccontrol_la_SOURCES = \ $(liblumiproccontrol_la_srcdir)/stypemanager.cpp +liblumiprocplay_la_srcdir = $(top_srcdir)/src/proc/play +noinst_LTLIBRARIES += liblumiprocplay.la + +liblumiprocplay_la_CFLAGS = $(AM_CFLAGS) -std=gnu99 -Wall -Wextra -Werror +liblumiprocplay_la_CXXFLAGS = $(AM_CXXFLAGS) -Wall -Wextra + +liblumiprocplay_la_SOURCES = \ + $(liblumiprocplay_la_srcdir)/dummy-player-service.cpp \ + $(liblumiprocplay_la_srcdir)/dummy-image-generator.cpp + + + liblumiprocmobjectsession_la_srcdir = $(top_srcdir)/src/proc/mobject/session noinst_LTLIBRARIES += liblumiprocmobjectsession.la @@ -193,6 +205,8 @@ noinst_HEADERS += \ $(liblumiproc_la_srcdir)/mobject/builderfacade.hpp \ $(liblumiproc_la_srcdir)/control/pathmanager.hpp \ $(liblumiproc_la_srcdir)/control/renderstate.hpp \ + $(liblumiproc_la_srcdir)/play/dummy-player-service.cpp \ + $(liblumiproc_la_srcdir)/play/dummy-image-generator.hpp \ $(liblumiproc_la_srcdir)/mobject/interpolator.hpp \ $(liblumiproc_la_srcdir)/mobject/parameter.hpp \ $(liblumiproc_la_srcdir)/mobject/paramprovider.hpp \ @@ -243,5 +257,6 @@ liblumieraproc_la_LIBADD = \ liblumiprocmobject.la \ liblumiprocmobjectbuilder.la \ liblumiproccontrol.la \ + liblumiprocplay.la \ liblumiprocmobjectsession.la diff --git a/src/proc/play/dummy-image-generator.cpp b/src/proc/play/dummy-image-generator.cpp new file mode 100644 index 000000000..29d58a1ac --- /dev/null +++ b/src/proc/play/dummy-image-generator.cpp @@ -0,0 +1,170 @@ +/* + DummyImageGenerator - creating test output frames for simulated playback + + Copyright (C) Lumiera.org + 2009, Joel Holdsworth , + Hermann Vosseler + + 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/play/dummy-image-generator.hpp" + + +namespace proc { + namespace play { + + + + namespace { // implementation details + + + typedef unsigned char byte; + + inline int + clamp (const int &val, const int &maxval, const int &minval) + { + if(val > maxval) return maxval; + if(val < minval) return minval; + return val; + } + + inline void + rgb_to_yuv (int r, int g, int b, byte &y, byte &u, byte &v) + { + // This code isn't great, but it does the job + y = (byte)clamp((299 * r + 587 * g + 114 * b) / 1000, 235, 16); + v = (byte)clamp((500 * r - 419 * g - 81 * b) / 1000 + 127, 255, 0); + u = (byte)clamp((-169 * r - 331 * g + 500 * b) / 1000 + 127, 255, 0); + } + + + void + rgb_buffer_to_yuy2 (unsigned char *in, unsigned char *out) + { + for (uint i = 0; i < 320*240*2; i+=4) + { + byte y0, u0, v0; + const byte r0 = *(in++); + const byte g0 = *(in++); + const byte b0 = *(in++); + rgb_to_yuv(r0, g0, b0, y0, u0, v0); + + byte y1, u1, v1; + const byte r1 = *(in++); + const byte g1 = *(in++); + const byte b1 = *(in++); + rgb_to_yuv(r1, g1, b1, y1, u1, v1); + + out[i] = y0; + out[i + 1] = u0; + out[i + 2] = y1; + out[i + 3] = v0; + } } + + + } // (End) implementation details + + + + + DummyImageGenerator::DummyImageGenerator(uint fps) + : current_(0) + , frame_(0) + , fps_(fps) + { } + + + void * const + DummyImageGenerator::next() + { + + ++frame_; + if(frame_ > 2 * fps_) + frame_ = 0; + + if(frame_ < 1 * fps_) + { + // create random snow... + for(int i = 0; i < 320*240*3; i+=3) + { + byte value ( rand() ); + buf_[i] = value; + buf_[i+1] = value; + buf_[i+2] = value; + } + } + else + { // create a colour strip pattern + typedef unsigned char Row[320 * 3]; + + unsigned char * row = buf_; + + // create a colour strip pattern in the first row... + for(int x = 0; x < 320; ++x) + { + byte &r = row[x*3]; + byte &g = row[x*3+1]; + byte &b = row[x*3+2]; + + if (x < 1*320/7) r = 0xC0, g = 0xC0, b = 0xC0; + else if(x < 2*320/7) r = 0xC0, g = 0xC0, b = 0x00; + else if(x < 3*320/7) r = 0x00, g = 0xC0, b = 0xC0; + else if(x < 4*320/7) r = 0x00, g = 0xC0, b = 0x00; + else if(x < 5*320/7) r = 0xC0, g = 0x00, b = 0xC0; + else if(x < 6*320/7) r = 0xC0, g = 0x00, b = 0x00; + else r = 0x00, g = 0x00, b = 0xC0; + } + + // fill remaining rows of the frame with the same pattern + for(int y = 1; y < 240; ++y) + memcpy(buf_ + y*sizeof(Row), row, sizeof(Row)); + + } + + // select output buffer to return + unsigned char * outBuff; + + if (!current_) + { + outBuff = outFrame_A_; + current_= 1; + } + else + { + outBuff = outFrame_B_; + current_= 0; + } + + rgb_buffer_to_yuy2(buf_, outBuff); + return outBuff; + + } + + + void * const + DummyImageGenerator::current() + { + if (!current_) return outFrame_A_; + else return outFrame_B_; + } + + + + } // namespace play + +} // namespace proc diff --git a/src/proc/play/dummy-image-generator.hpp b/src/proc/play/dummy-image-generator.hpp new file mode 100644 index 000000000..e24a4592e --- /dev/null +++ b/src/proc/play/dummy-image-generator.hpp @@ -0,0 +1,89 @@ +/* + DUMMY-IMAGE-GENERATOR.hpp - creating test output frames for simulated playback + + Copyright (C) Lumiera.org + 2009, Joel Holdsworth , + Hermann Vosseler + + 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file dummy-image-generator.hpp + ** Generator for test dummy video frames to simulate playback of rendered output. + ** As of 1/2009 the render engine and source reading functions are not ready yet. + ** So, in order to learn how to build up the GUI/Playback interfaces, we use + ** this dummy image generator to create visible output. First implemented + ** by Joel within PlaybackController, then factored out into a separate + ** dummy playback service. + ** + ** @see gui::controller::PlaybackController + ** @see proc::play::DummyPlayerService + ** + */ + + +#ifndef PROC_PLAY_DUMMY_IMAGE_GENERATOR_H +#define PROC_PLAY_DUMMY_IMAGE_GENERATOR_H + + +#include "lib/error.hpp" + + +namespace proc { + namespace play { + + + class DummyImageGenerator + { + + unsigned char buf_[320 * 240 * 3]; ///< working buffer for next frame + + unsigned char outFrame_A_[320 * 240 * 4]; ///< output frame 1 + unsigned char outFrame_B_[320 * 240 * 4]; ///< output frame 2 + + uint current_; + uint frame_; + uint fps_; + + + public: + DummyImageGenerator(uint fps); + + ~DummyImageGenerator() { } + + /** generate the next frame and + * occupy the alternate buffer. + * @return the buffer containing the new frame + */ + void * const next(); + + /** just re-return a pointer to the current frame + * without generating any new image data */ + void * const current(); + + + private: + + }; + + + + + } // namespace play + +} // namespace proc +#endif // PROC_PLAY_DUMMY_IMAGE_GENERATOR_H + diff --git a/src/proc/play/dummy-player-service.cpp b/src/proc/play/dummy-player-service.cpp new file mode 100644 index 000000000..ca2f0dd31 --- /dev/null +++ b/src/proc/play/dummy-player-service.cpp @@ -0,0 +1,325 @@ +/* + DummyPlayerService - access point and service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "proc/play/dummy-player-service.hpp" +#include "proc/play/dummy-image-generator.hpp" +#include "lib/singleton.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + +#include +#include + + +namespace proc { + namespace play{ + + using std::string; + using lumiera::Subsys; + using boost::scoped_ptr; + + + namespace { // hidden local details of the service implementation.... + + /** details of how the DummyPlayer service can be started + * and used as independent "subsystem" within main() */ + class DummyPlayerSubsysDescriptor + : public Subsys + { + operator string () const { return "Dummy-Player"; } + + + bool + shouldStart (lumiera::Option&) + { + return false; // for now the DummyPlayerService only comes "up" as dependency, + } // but doesn't start as a subsystem on it's own. + + bool + start (lumiera::Option&, Subsys::SigTerm terminationHandle) + { + ASSERT (!thePlayer_); + + thePlayer_.reset (new DummyPlayerService (terminationHandle)); + return true; + } + + /** manages the actual (single) instance of the player service impl */ + scoped_ptr thePlayer_; + + + void + triggerShutdown () throw() + { + TODO ("implement waiting for any playback processes to terminate gracefully"); + //..... but this would require us to use a separate thread, so I skip it for now. + // Probably it's better design to manage the processes in a separate thread anyway... + + thePlayer_.reset(0); + } + + bool + checkRunningState () throw() + { + //note: not locking here... + return (thePlayer_); + } + }; + + lumiera::Singleton theDescriptor; + + + + + + /* ================== define an lumieraorg_GuiNotification instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DummyPlayerFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", + const char*, (LumieraInterface ifa), + { (void)ifa; return "DummyPlayer"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; + } + ) + , LUMIERA_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... + + typedef DummyPlayer::Process* ProcP; + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 + ,lumieraorg_DummyPlayerFacade + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", + LumieraPlayProcess, (void), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return 0; + } + + return static_cast (& (_instance->start())); + } + ) + , LUMIERA_INTERFACE_INLINE (pausePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", + void, (LumieraPlayProcess handle, bool doPlay), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + proc->pause(doPlay); + } + ) + , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", + void, (LumieraPlayProcess handle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + UNIMPLEMENTED ("terminate a running playback process"); + } + ) + , LUMIERA_INTERFACE_INLINE (getFrame, "\230\130\101\300\047\065\170\052\226\164\026\112\150\166\074\134", + void *, (LumieraPlayProcess handle), + { + //skipping full checks for performance reasons + REQUIRE (_instance && !lumiera_error_peek()); + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + return const_cast (proc->getFrame()); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) + : error_("") + , notifyTermination_(terminationHandle) + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerFacade)) + { + INFO (operate, "DummyPlayer Facade opened."); + } + + + + + DummyPlayer::Process& + DummyPlayerService::start() + { + // REQUIRE (!theProcess_.isActive()); //////////////TODO: reactivate this check when we have really independent processes which can be stopped! + theProcess_.setRate(25); + + return theProcess_; + } + + + + void + ProcessImpl::setRate (uint fps) + { + // REQUIRE (fps==0 || fps_==0 ); //////////////TODO: reactivate this check when we have really independent processes which can be stopped! + // REQUIRE (fps==0 || !play_ ); //////////////TODO: reactivate this check when we have really independent processes which can be stopped! + + fps_ = fps; + play_ = (fps != 0); + + if (play_) + imageGen_.reset(new DummyImageGenerator(fps)); + } + + + + void + ProcessImpl::pause(bool doPause) + { + REQUIRE (isActive()); + play_ = !doPause; + } + + + + void* const + ProcessImpl::getFrame() + { + REQUIRE (isActive()); + ASSERT (imageGen_); + + if (play_) + return imageGen_->next(); + else + return imageGen_->current(); + } + + + + } // namespace play + + + + /** @internal intended for use by main(). */ + lumiera::Subsys& + DummyPlayer::getDescriptor() + { + return play::theDescriptor(); + } + + // emit the vtable here into this translation unit within liblumieraproc.so ... + DummyPlayer::~DummyPlayer() { } + DummyPlayer::Process::~Process() { } + + + + +} // namespace proc diff --git a/src/proc/play/dummy-player-service.hpp b/src/proc/play/dummy-player-service.hpp new file mode 100644 index 000000000..ae8b3161e --- /dev/null +++ b/src/proc/play/dummy-player-service.hpp @@ -0,0 +1,140 @@ +/* + DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file notification-service.hpp + ** A public service provided by the GUI, implementing the gui::GuiNotification facade interface. + ** The purpose of this service is to push state update and notification of events from the lower + ** layers into the Lumiera GUI. Typically, this happens asynchronously and triggered by events + ** within the lower layers. + ** + ** This service is the implementation of a layer separation facade interface. Clients should use + ** gui::GuiNotification#facade to access this service. This header defines the interface used + ** to \em provide this service, not to access it. + ** + ** @see gui::GuiFacade + ** @see guifacade.cpp starting this service + */ + + +#ifndef PROC_DUMMYPLAYER_SERVICE_H +#define PROC_DUMMYPLAYER_SERVICE_H + + +#include "include/dummy-player-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" + +#include +#include + + +namespace proc { + namespace play { + + using std::string; + using lumiera::Subsys; + + + class DummyImageGenerator; + + + class ProcessImpl + : public DummyPlayer::Process + { + void pause(bool doPause); + void* const getFrame(); + + uint fps_; + bool play_; + + boost::scoped_ptr imageGen_; + + + public: + ProcessImpl() : fps_(0), play_(false), imageGen_(0) {} + + /* Implementation-level API to be used By DummyPlayerService */ + + /** activate a playback process + * with given specification */ + void setRate (uint fps); + + bool isActive () { return fps_ != 0; } + bool isPlaying() { return play_; } + }; + + + /****************************************************** + * Actual implementation of the GuiNotification service + * within the Lumiera GTK GUI. Creating an instance of + * this class automatically registers the interface + * with the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + * + * @todo the ctor of this class should take references + * to any internal service providers within the + * GUI which are needed to implement the service. + */ + class DummyPlayerService + : public DummyPlayer + { + + /* === Implementation of the Facade Interface === */ + + Process& start(); + + + /** for now we use an single inline Process... + * @todo actually implement multiple independent Playback processes! + * @todo I am aware holding this object inline may cause a segfault at shutdown! + */ + ProcessImpl theProcess_; + + string error_; + Subsys::SigTerm notifyTermination_; + + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/tests/40components.tests b/tests/40components.tests index 6ed448349..ba14f909f 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -337,6 +337,10 @@ return: 0 END +PLANNED "Waiting on Thread termination" ThreadWrapperJoin_test < + + 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" + +#include "include/symbol.hpp" +#include "backend/thread-wrapper.hpp" +#include "lib/error.hpp" +#include "lib/sync.hpp" + +#include + +using std::tr1::bind; +using test::Test; + + + +namespace lib { + namespace test { + + /************************************************************************** + * @test use the Lumiera backend to create some new threads, additionally + * passing an condition variable for waiting on thread termination. + * Actually this is implemented as creating and passing a JoinHandle. + * + * @see lib::Thread + * @see threads.h + */ + class ThreadWrapperJoin_test : public Test + { + + virtual void + run (Arg) + { + simpleUse (); + wrongUse (); + } + + + volatile int aValue_; ///< state to be modified by the other thread + + void + theAction (int secretValue) ///< to be run in a new thread... + { + sleep(1); + aValue_ = secretValue+42; + } + + + void + simpleUse () + { + aValue_=0; + int mySecret = (rand() % 1000) - 500; + + JoinHandle waitingHandle; + + Thread("test Thread joining", + bind (&ThreadWrapperJoin_test::theAction, this, mySecret), + waitingHandle); + // note binding and thread wrapper already destroyed + + waitingHandle.join(); // blocks until theAction() is done + + ASSERT (aValue_ == mySecret+42); + } + + + void + wrongUse () + { + JoinHandle waitingHandle; + + Thread("test Thread joining-1", + bind (&ThreadWrapperJoin_test::theAction, this, 111)); + // note we "forget" to pass the JoinHandle + try + { + waitingHandle.join(); // protocol error: handle wasn't passed for starting a Thread; + NOTREACHED; + } + catch (lumiera::error::Logic& logo) + { lumiera_error(); } + + + Thread("test Thread joining-2", + bind (&ThreadWrapperJoin_test::theAction, this, 222), + waitingHandle); // this time we pass it.... + +#ifdef DEBUG + /////////////////////////////////////////////////////////////////////////////////////////////TODO: better way of detecting debug builds + try + { + Thread("test Thread joining-3", + bind (&ThreadWrapperJoin_test::theAction, this, 333), + waitingHandle); // but then pass it again for another thread.... + NOTREACHED; + } + catch (...) + { + ASSERT (lumiera_error() == lumiera::error::LUMIERA_ERROR_ASSERTION); + } +#endif + + // note: the waitingHandle goes out of scope here, + // which unblocks the second thread. The first thread wasn't blocked, + // while the third thread wasn't created at all. + } + + }; + + + + /** Register this test class... */ + LAUNCHER (ThreadWrapperJoin_test, "function common"); + + + + } // namespace test + +} // namespace lib diff --git a/wiki/compatibility.html b/wiki/compatibility.html index 56353ac46..a014734fe 100644 --- a/wiki/compatibility.html +++ b/wiki/compatibility.html @@ -747,7 +747,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS } //}}} -
+
! Programming Languages
 * C
 ** a C99 compatible compiler, some GCC extensions are used, most are optional.
@@ -787,7 +787,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
 ** libcairomm-1.0-dev (>=0.6.0)
 ** libgdl-1-dev (>=0.6.1)
 *** libbonoboui2-dev (>=2.14.0)
-** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16)
+** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16) and gthread-2.0 (>=2.12.4)
 ** libxv-dev ~~(1.0.2 is known to work)~~
 
 
diff --git a/wiki/index.html b/wiki/index.html
index af19d10e4..fc7632387 100644
--- a/wiki/index.html
+++ b/wiki/index.html
@@ -780,7 +780,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
 }
 //}}}
-
+
for __Building__
 * gcc (4.1), glibc6 (2.3), libstdc++6 (4.1)
 * [[build system|BuildSystem]] dependencies: SCons (0.96.90), Python (2.4), pkg-config
@@ -799,7 +799,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
 ** libcairomm-1.0-dev (>=0.6.0)
 ** libgdl-1-dev (>=0.6.1)
 *** libbonoboui2-dev (>=2.14.0)
-** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16)
+** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16) and gthread-2.0 (>=2.12.4)
 ** libxv-dev (>=1.0.2)
 //usually, newer versions are OK//