PlayService: basic definition and link to facade

This commit is contained in:
Fischlurch 2011-06-13 20:07:30 +02:00
parent 2099ecbcac
commit 971dea6f6a
12 changed files with 238 additions and 29 deletions

View file

@ -0,0 +1,123 @@
/*
INTERFACE-FACADE-LINK - a switchable link from interface to service implementation
Copyright (C) Lumiera.org
2011, 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 interface-facade-link.hpp
** Opening, accessing and closing the service access through a facade interface.
** Client code is assumed to access an application level service through an facade
** interface, while the actual implementation object remains an opaque internal detail.
** Moreover, services may come up and shut down, so the implementation might change
** during the Lifecycle. The facility defined here in this header provides a basic
** implementation for this access mechanism, but without any adaptation, binding
** or plugin access layer. It works only under the assumption that both the
** interface and the actual service implementation coexist in the same
** executable and are written in C++, so any invocation of an
** interface method boils down to a language-level call.
**
** Usually, client code doesn't need to include this header. Clients are assumed
** to use the facade interface of the service in question. This facade interface
** contains a static member of type \c lumiera::facade::Accessor<I> (where I is
** the type of the facade interface). The Accessor baseclass is defined in
** interfaceproxy.hpp and typically included through the facade header.
**
** @note there is a way more elaborate implementation of the same mechanism
** for use with the Lumiera Interface/Plugin system.
**
** @see interfaceproxy.hpp description of the more general use case
** @see PlayService example for the simple use case
*/
#ifndef LUMIERA_FACADE_INTERFACE_FACADE_LINK_H
#define LUMIERA_FACADE_INTERFACE_FACADE_LINK_H
#include "lib/error.hpp"
#include "lib/test/test-helper.hpp"
#include "include/interfaceproxy.hpp"
#include "lib/symbol.hpp"
#include <boost/noncopyable.hpp>
namespace lumiera {
namespace facade {
using lib::Literal;
/************************************************************************
* simple access-frontend to the implementation of a service (C++ only).
* Usually, an instance of Accessor is placed as static member right into
* the facade interface used to access the service. This implementation
* of the access mechanism handles the simple case that both the facade
* and the service implementation are written in C++ and calls happen
* within the main executable as direct language calls, without an
* binding layer and without involving the Interface/Plugin system.
*
* Typically, the InterfaceFacadeLink becomes a member of the service
* implementation class and is directly tied into the constructor of
* the latter. Being a subclass of lumiera::facade::Accessor, it is
* allowed to "open" the facade access just by setting the static
* protected pointer Accessor::implProxy_
*/
template<class FA>
class InterfaceFacadeLink
: protected Accessor<FA>
, boost::noncopyable
{
Literal displayName_;
void
__checkLifecycle ()
{
if (Accessor<FA>::implProxy_)
throw error::State("Attempt to open an already opened Facade interface."
, error::LUMIERA_ERROR_LIFECYCLE);
}
public:
InterfaceFacadeLink(FA& serviceImpl, Literal interfaceName_for_Log=0)
: displayName_(lib::test::showType<FA>(interfaceName_for_Log))
{
__checkLifecycle();
Accessor<FA>::implProxy_ = &serviceImpl;
INFO (interface, "interface %s opened", displayName_.c());
}
~InterfaceFacadeLink()
{
INFO (interface, "closing interface %s...", displayName_.c());
Accessor<FA>::implProxy_ = 0;
}
};
/** storage for the static access pointer */
template<class FA>
FA* Accessor<FA>::implProxy_;
}} // namespace lumiera::facade
#endif

View file

@ -32,7 +32,7 @@ namespace lumiera{
namespace facade {
LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible");
LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade is closed; service currently not accessible");
/**

View file

@ -48,17 +48,18 @@
** Any sort of dependency management is outside the scope of the InstanceHandle (for the core
** services, it is handled by the dependency of subsystems, while the plugin loader cares
** for dependency issues regarding loadable modules, thereby building on the deployment
** descriptors.
** descriptors.)
**
** For the Layer separation interfaces, the process of loading and opening is abstracted as
** an InstanceHandle object. When creating such an InstanceHandle using the appropriate
** template and ctor parameters, in addition to the registration with the Interface/Plugin
** system, the corresponding facade::Proxy factory is addressed and "opened" by creating
** the right proxy object instance. Similarly, when the InstanceHandle object goes out
** of scope, prior to detaching from the Interface/Proxy system, the corresponding
** lumiera::facade::Accessor factory is "closed", which additionally means destroying
** the proxy object instance and switching any further access to throwing and exception.
**
** system, the corresponding facade::Proxy factory is addressed and the interface instance
** is "opened" by creating the appropriate proxy object instance. Similarly, when the
** InstanceHandle object goes out of scope, prior to detaching from the Interface/Proxy
** system, the corresponding lumiera::facade::Accessor frontend is "closed", which
** additionally means destroying the proxy object instance and switching any
** further access to throwing and exception.
**
** While client code just includes the interface header (including interfaceproxy.hpp
** in turn), there needs to be an actual implementation of each proxy object located in
** some translation unit. The usual place is interfaceproxy.cpp, which gets linked into
@ -89,7 +90,29 @@ namespace facade {
/*********************************************************************
* access-frontend to the implementation of a service.
* Usually, an instance of Accessor is placed as static member
* right into the facade interface used to access the service.
* This allows clients to get the current actual implementation
* of that service, just by invoking the function operator on
* that member, e.g. \c lumiera::Play::facade()
*
* The reason for this rather indirect access technique is Lifecycle:
* Service implementations may come up and go down; moreover, a service
* might be implemented through a plugin component and thus the actual
* invocation needs to be passed through a binding layer. In this case,
* clients rather access a proxy object, which then passes on any call
* through that binding layer to the actual implementation located
* "somewhere".
*
* @note the pointer to the actual implementation is a static variable.
* This has two consequences. For one, we're dealing with kind of
* singleton service here. And, secondly, the implementation or
* proxy accessor can inherit from Accessor<FA> where FA is the
* facade interface. Being a subclass, allows the implementation
* to set that pointer when the service comes up, and to clear
* it when the service goes down and the access needs to
* be closed.
*/
template<class FA>
class Accessor
@ -105,7 +128,8 @@ namespace facade {
if (implProxy_)
return *implProxy_;
else
throw error::State("Facade interface currently closed.");
throw error::State("Facade interface currently closed."
, LUMIERA_ERROR_FACADE_LIFECYCLE);
}
};

View file

@ -115,15 +115,16 @@ namespace lumiera {
};
typedef lib::IterSource<mobject::ModelPort> ModelPorts;
typedef lib::IterSource<mobject::OutputDesignation> Pipes;
typedef lib::IterSource<mobject::ModelPort>& ModelPorts;
typedef lib::IterSource<mobject::OutputDesignation>& Pipes;
typedef proc::play::POutputManager Output;
typedef mobject::session::PClipMO Clip;
typedef mobject::PTrack Track;
typedef mobject::PTrack Track;
typedef asset::PTimeline Timeline;
typedef asset::PViewer Viewer;
/** create a new playback process outputting to the given viewer/display */
/** core operation: create a new playback process
* outputting to the given viewer/display */
virtual Controller connect(ModelPorts, Output) =0;

View file

@ -122,6 +122,7 @@ namespace lumiera {
LUMIERA_ERROR_DECLARE (ASSERTION); ///< assertion failure
/* generic error situations */
LUMIERA_ERROR_DECLARE (LIFECYCLE); ///< Lifecycle assumptions violated
LUMIERA_ERROR_DECLARE (WRONG_TYPE); ///< runtime type mismatch
LUMIERA_ERROR_DECLARE (ITER_EXHAUST); ///< end of sequence reached
LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value

View file

@ -79,6 +79,7 @@ namespace lumiera {
LUMIERA_ERROR_DEFINE (ASSERTION, "assertion failure");
/* some further generic error situations */
LUMIERA_ERROR_DEFINE (LIFECYCLE, "Lifecycle assumptions violated");
LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch");
LUMIERA_ERROR_DEFINE (ITER_EXHAUST, "end of sequence reached");
LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value");

View file

@ -60,7 +60,7 @@ namespace lib {
* Generic opaque reference counting handle, for accessing a service
* and managing its lifecycle. Usually such a handle is created by
* an service interface and \link #activate activated \endlink by
* setting up the link to some internal implementation object.
* setting up the link to a suitable hidden implementation object.
* This setup can only be done by a friend or derived class, //////////////////////////TODO: that was the intention. Why didn't this work out as expected?
* while client code is free to copy and store handle objects.
* Finally, any handle can be closed, thereby decrementing

View file

@ -57,14 +57,15 @@ namespace proc {
/** provide a descriptor for lumiera::AppState,
* wired accordingly to allow main to load and
* save an existing session. */
* wired accordingly to allow main to bring up
* a editing session, possibly by loading an
* existing session from storage. */
static lumiera::Subsys& getSessionDescriptor();
/** provide a descriptor for lumiera::AppState,
* wired accordingly to allow main to bring up
* the render, playback coordination and
* the render / playback coordination and
* output management subsystem. */
static lumiera::Subsys& getPlayOutDescriptor();

View file

@ -72,7 +72,7 @@ namespace proc {
/********************************************************************
* Actual implementation of a single (dummy) playback process.
* The DummyPlayerService (see below) maintains a collection of such
* actively running playback processes, while the client code gets
* actively running playback processes, while the client code gets
* DummyPlayer::Process handles to track any ongoing use. Users of
* the plain C interface get a direct bare pointer to the respective
* ProcessImpl instance and have to manage the lifecycle manually.

View file

@ -32,6 +32,16 @@
namespace lumiera {
Play::~Play() { } // emit VTables here...
}//(End) namespace lumiera
namespace proc {
namespace play {
@ -55,9 +65,37 @@ namespace play {
} // (End) hidden service impl details
using lumiera::Play;
/** */
/** bring up the global render- and playback service.
* This service allows to create individual PlayProcess instances
* to \em perform a timeline or similar model object, creating
* rendered data for output. Client code is assumed to access
* this service through the lumiera::Play facade.
*/
PlayService::PlayService() /////TODO Subsys::SigTerm terminationHandle);
: facadeAccess_(*this, "Player")
{ }
/**
* @note this is the core operation of the play and render service
*
* Invoking this function investigates the given exit nodes of the
* render nodes network and retrieves actual output destinations
* through the given OutputManager. The goal is to configure an
* PlayProcess, based on the renderengine and the collection of
* OutputSlot instances retrieved for each of the given exit nodes.
* Running this PlayProcess will activate the render engine to deliver
* calculated media data to the outputs.
*/
Play::Controller
PlayService::connect(ModelPorts dataGenerators, Output outputDestinations)
{
UNIMPLEMENTED ("build a PlayProcess");
}
}} // namespace proc::play

View file

@ -34,6 +34,7 @@
#include "lib/error.hpp"
#include "include/play-facade.h"
#include "common/interface-facade-link.hpp"
#include <boost/noncopyable.hpp>
//#include <boost/scoped_ptr.hpp>
@ -44,6 +45,7 @@ namespace proc {
namespace play {
using std::string;
using lumiera::facade::InterfaceFacadeLink;
//using lumiera::Subsys;
//using lumiera::Display;
//using lumiera::DummyPlayer;
@ -58,8 +60,15 @@ namespace play {
* Interface: Player subsystem.
*/
class PlayService
: boost::noncopyable
: public lumiera::Play
, boost::noncopyable
{
InterfaceFacadeLink<lumiera::Play> facadeAccess_;
/** Implementation: build a PlayProcess */
virtual Controller connect(ModelPorts, Output);
public:
PlayService(); /////TODO Subsys::SigTerm terminationHandle);

View file

@ -1632,7 +1632,7 @@ The main tool used to implement this separation is the [[Builder Pattern|http://
Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any &quot;internal-multi&quot; effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the Session in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways.
</pre>
</div>
<div title="DesignPlayerSubsystem" modifier="Ichthyostega" modified="201105230122" created="201105220216" tags="Player design draft" changecount="12">
<div title="DesignPlayerSubsystem" modifier="Ichthyostega" modified="201106122319" created="201105220216" tags="Player design draft" changecount="13">
<pre>//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
!Starting point
@ -1658,7 +1658,7 @@ Reworking the dummy player into the [[Player subsystem|PlayService]] is a larger
* then, of course, the PlayService interface needs to be created
* as a temporary soltion, a ''~DummyPlayConnection'' will be created hard-wired, within the ~DummyPlayer service. This dumy container is a test setup, explicitly creating a OutputManager implementation, a GeneratorMO, a GeneratorNode (thus omitting the not-yet-implemented Builder) and the corresponding ModelPort. This way, the ~DummyPlayer service can be changed to use the real PlayService for creating the dummy generated output data. It will then pass back the resulting PlayController to the existing GUI setup.
The Method of pushing frames to the Viewer widget is changed fundamentally during that transition: while previously, in the PlayerDummy design study, the GUI opened a Display-façade interface -- now rather the GUI has to register an OutputSlot implementation with the global OutputManager. Such a registration requires an explicit deregistration on shutdown, and this can be made to block, until no further data will be delivered. Using this approach tries to circumvent the problems with occasional crashes on shutdown, due to dispatching frame output signals in the event thread while already in shutdown.
The Method of pushing frames to the Viewer widget is changed fundamentally during that transition: while previously, in the PlayerDummy design study, the GUI opened a Display-façade interface -- now rather the GUI has to register an OutputSlot implementation with the global OutputManager. Such a registration requires an explicit deregistration on shutdown, and this can be made to block, until no further data will be delivered. Using this approach we hope to circumvent problems with occasional crashes on closing the application, due to dispatching frame output signals in the event thread while shutdown is already in progress.
</pre>
</div>
@ -3219,14 +3219,15 @@ While actually data frames are //pulled,// on a conceptual level data is assumed
As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
</pre>
</div>
<div title="OutputManagement" modifier="Ichthyostega" modified="201105220022" created="201007090155" tags="Model Rendering Player spec draft" changecount="19">
<div title="OutputManagement" modifier="Ichthyostega" modified="201106130008" created="201007090155" tags="Model Rendering Player spec draft" changecount="21">
<pre>//writing down some thoughts//
* ruled out the system outputs as OutputDesignation.
* thus, any designation is a [[Pipe]]-ID.
* consequently, it is not obviously clear if such an designation is the final exit point
* please note the [[Engine interface proposal|http://lumiera.org/Lumiera/DesignProcess/EngineInterfaceOverview.html]]
* please note the [[Engine interface proposal|http://lumiera.org/documentation/devel/rfc_pending/EngineInterfaceOverview.html]]
* this introduces the notion of a ModelPort: //a point in the (high level) model where output can be produced//
* thus obviously we need an OutputManager element to track the association of OutputDesignation to OutputSlot
Do we get a single [[Fixture]] &amp;mdash; guess yes
@ -3242,10 +3243,20 @@ We should note that in both cases this mapping operation is controlled and drive
!Connection to external outputs
External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &amp;mdash; any slot implementation provides a way to handle timeouts gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &amp;mdash; any slot implementation provides a way to handle time-outs gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
&amp;rarr; see also the PlayerDummy
!the global output manager
While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager''. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities.
</pre>
</div>
<div title="OutputManager" modifier="Ichthyostega" modified="201106130000" created="201106122359" tags="Player Model def" changecount="2">
<pre>The term &amp;raquo;''Output Manager''&amp;laquo; might deonte two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is &quot;the&quot; global OutputManager, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
&amp;rarr; see [[output management overview|OutputManagement]]
&amp;rarr; see OutputSlot
&amp;rarr; see ViewerPlayConnection</pre>
</div>
<div title="OutputMapping" modifier="Ichthyostega" modified="201105221814" created="201011080238" tags="Model spec draft" changecount="25">
<pre>An output mapping serves to //resolve//&amp;nbsp; [[output designations|OutputDesignation]].
@ -7285,14 +7296,14 @@ These Viewer (or Monitor) elements play an enabling role for any output generati
When the GUI is outfitted, based on the current Session or HighLevelModel, it is expected to retrieve the viewer assets and for each of them, after installing the necessary widgetes, registers an OutputSlot with the global OutputManager.</pre>
</div>
<div title="ViewerPlayConnection" modifier="Ichthyostega" modified="201105221838" created="201007110305" tags="Model Player spec draft" changecount="8">
<div title="ViewerPlayConnection" modifier="Ichthyostega" modified="201106122343" created="201007110305" tags="Model Player spec draft" changecount="10">
<pre>for showing output, three entities are involved
* the [[Timeline]] holds the relevant part of the model, which gets rendered for playback
* by connecting to a viewer component, an actual output target is established
* by connecting to a viewer component (&amp;rarr; ViewConnection), an actual output target is established
* the playback process itself is coordinated by a PlayController, which in turn creates a PlayProcess
!the viewer connection
A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. Connecting a timeline to a viewer creates a ViewConnection, which is a special [[binding|BindingMO]]. The number and kind of pipes provided is a configurable property of the viewer element &amp;mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&amp;rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputManager slot &amp;mdash; again an output mapping step.
A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. Connecting a timeline to a viewer creates a ViewConnection, which is a special [[binding|BindingMO]]. The number and kind of pipes provided is a configurable property of the viewer element &amp;mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&amp;rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputSlot registered with the OutputManager &amp;mdash; again an output mapping step.
</pre>
</div>
<div title="VirtualClip" modifier="Ichthyostega" modified="201011220152" created="200804110321" tags="def Model" changecount="17">