lumiera_documentation/src/proc/play/dummy-player-service.cpp

385 lines
17 KiB
C++

/*
DummyPlayerService - access point and service implementing a dummy test player
Copyright (C) Lumiera.org
2009, Hermann Vosseler <Ichthyostega@web.de>
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-player-service.cpp
** Implementation of a service for mock render output to support testing.
*/
#include "proc/play/dummy-player-service.hpp"
#include "proc/engine/worker/dummy-image-generator.hpp"
#include "proc/engine/worker/tick-service.hpp"
#include "lib/depend.hpp"
extern "C" {
#include "common/interface-descriptor.h"
}
#include <functional>
#include <memory>
#include <string>
namespace proc {
namespace play{
using std::string;
using lumiera::Subsys;
using std::unique_ptr;
using std::bind;
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&) override
{
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) override
{
ASSERT (!thePlayer_);
thePlayer_.reset (new DummyPlayerService (terminationHandle));
return true;
}
/** manages the actual (single) instance of the player service impl */
std::unique_ptr<DummyPlayerService> thePlayer_;
void
triggerShutdown () noexcept override
{
thePlayer_.reset(0);
// note: shutdown of the DummyPlayerService instance may block
// for a short period, until termination of all tick services
}
bool
checkRunningState () noexcept override
{
return bool(thePlayer_);
}
};
lib::Depend<DummyPlayerSubsysDescriptor> theDummyPlayerDescriptor;
/* ================== define an lumieraorg_DummyPlayer instance ======================= */
LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0
,lumieraorg_DummyPlayerFacade_descriptor
, NULL, NULL, NULL
, LUMIERA_INTERFACE_INLINE (name,
const char*, (LumieraInterface ifa),
{ (void)ifa; return "DummyPlayer"; }
)
, LUMIERA_INTERFACE_INLINE (brief,
const char*, (LumieraInterface ifa),
{ (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; }
)
, LUMIERA_INTERFACE_INLINE (homepage,
const char*, (LumieraInterface ifa),
{ (void)ifa; return "http://www.lumiera.org/develompent.html" ;}
)
, LUMIERA_INTERFACE_INLINE (version,
const char*, (LumieraInterface ifa),
{ (void)ifa; return "0.1~pre"; }
)
, LUMIERA_INTERFACE_INLINE (author,
const char*, (LumieraInterface ifa),
{ (void)ifa; return "Hermann Vosseler"; }
)
, LUMIERA_INTERFACE_INLINE (email,
const char*, (LumieraInterface ifa),
{ (void)ifa; return "Ichthyostega@web.de"; }
)
, LUMIERA_INTERFACE_INLINE (copyright,
const char*, (LumieraInterface ifa),
{
(void)ifa;
return
"Copyright (C) Lumiera.org\n"
" 2009 Hermann Vosseler <Ichthyostega@web.de>";
}
)
, LUMIERA_INTERFACE_INLINE (license,
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,
int, (LumieraInterface ifa),
{(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; }
)
, LUMIERA_INTERFACE_INLINE (versioncmp,
int, (const char* a, const char* b),
{(void)a;(void)b; return 0;} ////////////////////////////////////////////TODO define version ordering
)
);
using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE;
typedef lib::SingletonRef<DummyPlayerService>::Accessor InstanceRef;
InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation...
typedef ProcessImpl* ProcP;
LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0
,lumieraorg_DummyPlayerService
, LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor)
, NULL /* on open */
, NULL /* on close */
, LUMIERA_INTERFACE_INLINE (startPlay,
LumieraPlayProcess, (LumieraDisplaySlot viewerHandle),
{
if (!_instance)
{
lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0);
return 0;
}
return static_cast<LumieraPlayProcess> (_instance->start(viewerHandle));
}
)
, LUMIERA_INTERFACE_INLINE (togglePlay,
void, (LumieraPlayProcess handle, bool doPlay),
{
if (!_instance)
{
lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0);
return;
}
REQUIRE (handle);
ProcP proc = static_cast<ProcP> (handle);
proc->doPlay(doPlay);
}
)
, LUMIERA_INTERFACE_INLINE (terminate,
void, (LumieraPlayProcess handle),
{
if (!_instance)
{
lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0);
return;
}
REQUIRE (handle);
ProcP proc = static_cast<ProcP> (handle);
ProcessImpl::terminate (proc);
}
)
);
} // (End) hidden service impl details
DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle)
: error_("")
, notifyTermination_(terminationHandle)
, implInstance_(this,_instance)
, serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService))
{
INFO (progress, "DummyPlayer Facade opened.");
}
/** @par implementation note
* A new process (implementation) is created, configured
* and started here. This may include spawning a thread or
* allocating a timer. The newly created process is self-contained
* and will be just handed out, without caring for its lifecycle.
* If client code accesses this function via the plain C interface,
* the client is responsible for terminating this process, whereas
* when using the C++ interface, you'll get a Handle object which
* manages the lifecycle automatically.
*/
ProcessImpl*
DummyPlayerService::start (LumieraDisplaySlot viewerHandle)
{
unique_ptr<ProcessImpl> newProcess (new ProcessImpl (viewerHandle));
REQUIRE (!newProcess->isActive());
newProcess->setRate(25);
return newProcess.release();
}
/* === Process Implementation === */
ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle)
: fps_{0}
, play_{false}
, display_{Display::facade().getHandle (viewerHandle)}
, imageGen_{}
, tick_{new TickService (bind (&ProcessImpl::doFrame, this))}
{ }
ProcessImpl::~ProcessImpl()
{
INFO (proc_dbg, "Playback process halted...");
}
/** deleter function for lib::Handle */
void
ProcessImpl::terminate (DummyPlayer::ProcessImplementationLink* process)
{
if (process)
delete process;
}
/**
* activate a forwarding smart-Handle
* to be used by client code for communication with the play process implementation.
* The handle manages the lifecycle of the interface / play process connection.
* This virtual function is used by the interface proxy to connect the client side
* and the actual play process, after creating the latter through the interface system.
*/
DummyPlayer::Process
ProcessImpl::createHandle()
{
DummyPlayer::Process handle;
DummyPlayer::ProcessImplementationLink *const implementationLink = this;
handle.activate(implementationLink, &terminate);
return handle; // note the deleter function...
}
void
ProcessImpl::setRate (uint fps)
{
REQUIRE (fps==0 || fps_==0 );
REQUIRE (fps==0 || !play_ );
REQUIRE (tick_);
fps_ = fps;
play_ = (fps != 0);
if (play_)
imageGen_.reset(new DummyImageGenerator(fps));
// callbacks with given frequency, starting now
tick_->activate(fps);
}
void
ProcessImpl::doPlay(bool yes)
{
REQUIRE (isActive());
tick_->activate (yes? fps_:0);
play_ = yes;
}
void
ProcessImpl::doFrame()
{
REQUIRE (isActive());
ASSERT (imageGen_);
if (play_)
display_(imageGen_->next());
else
display_(imageGen_->current());
}
} // namespace play
} // namespace proc
namespace lumiera {
/** @internal intended for use by main(). */
lumiera::Subsys&
DummyPlayer::getDescriptor()
{
return proc::play::theDummyPlayerDescriptor();
}
} // namespace lumiera