A data sink plugin needs to implement the pure virtual (and where appropriate
override the virtual) base class mixin functions. This section describes how
to implement the MDataSink
base class.
Note that it is possible for a single source/sink plugin to be both a source
and a sink, i.e. derive from both MDataSource
and MDataSink
.
See Creating a Source Plugin for information on source/sink negotiation and data transfer.
Sink Plugin Instantiation
A client application instantiates a sink plugin using the RMMFController::AddDataSink()
method,
passing in the UID of the sink as one of the parameters. The controller framework
instantiates a sink via the MDataSink::NewSinkL()
method
rather than the more conventional NewL()
method. This is
because, in some cases, a plugin can be both a sink and a sink of multimedia
data in which case the instantiation methods for sinks and sinks need to be
distinct. The MDataSink
base class instantiation methods
are described below followed by how to write the instantiation methods in
the derived class.
The derived data sink needs to implement MDataSink::NewSinkL()
and MDataSink::ConstructSinkL()
for
instantiation. The constructor of a data sink plugin needs to specify what
type of data sink it is. This is achieved by passing a type UID into the MDataSink
constructor.
If the sink has additional methods, that are not part of the base MDataSink
class,
then a further layer of instantiation is required. For example, suppose CAcmeDataSink
had
some extra methods that were not part of the base class then an additional
mxin interface for this class is required. For example:
class MAcmeDataSink : public MDataSink { public: inline static MAcmeDataSink* NewAcmeDataSinkL(TUid aImplemetationUid, const TDesC8& aInitData); //This allows dynamic linkage to the Class: }
The NewAcmeDataSinkL
should be implemented as follows:
MAcmeDataSink* retPtr = static_cast<MAcmeDataSink*>(MDataSink::NewSinkL(aImplementationUid, aInitData));
The class should derive from the MAcmeDataSink
rather
than MDataSink
as follows:
class CAcmeDataSink: public CBase, public MAcmeDataSink { public: MDataSink* NewSinkL();
Sink Plugin Buffer Creation
Buffers are required to transfer data between a source and a sink. These buffers may be created by the source and/or sink. The methods below are for the sink buffer creation.
The MDataSink::CanCreateSinkBuffer()
method must be
implemented by the data sink plugin. Most sinks should be capable of creating
their own buffer and so would return ETrue
. Note that just
because a sink can create a buffer, this does not guarantee that the framework
will actually use the buffer created by the sink. Which buffer is used depends
on factors such as whether a null codec is used and whether the source is
the reference buffer, in either of these cases the sink buffer will not be
used.
The MDataSink::CreateSinkBufferL()
method is called
by the framework to create a buffer from the sink. This method should create
a buffer of length 0 bytes and a maximum length of an appropriate size for
the sink. The meaning of 'appropriate' in this context depends on sink specifics,
such as whether the sink data ultimately comes from hardware that supplies
buffers of a certain size. Generally a larger buffer size means a smaller
number data transfers between the sink and sink are required. The returned
buffer must derive from CMMFBuffer
but will be a derived
buffer eg CMMFDataBuffer
or a video frame buffer.
The overloaded MDataSink::CreateSinkBufferL()
method,
which has an additional aSinkBuffer
parameter, is optional.
The default implementation is identical to the standard CreateSinkBufferL
above.
This overloaded version is used where the nature of the sink buffer may impact
the created sink buffer. This method should only be overridden if the size
and/or type of the sink buffer can influence the size and/or type of the created
sink buffer.
Sink State Methods
The MDataSink
mixin provides a number of state-related
functions used to inform a source/sink that the data path (via the controller)
has made a transition to a particular state. These state transition methods
are usually called on the source/sink from the datapath and so will be called
on the data source/sink plugin. These methods are not pure virtual and so
it is not compulsory to implement them.
The MDataSink::SinkThreadLogon()
method indicates to
the sink that it can perform any thread specific initialisation. This is so
that the thread in which the data sink is instantiated is not necessarily
the same thread in which the actual transfer of data between the source and
the sink takes place. Any thread-specific sink initialisation cannot take
place in the NewSinkL()
method and must instead be performed
in the MDataSink::SinkThreadLogon()
method which is always
called in the thread in which the data transfer between sink and sink is to
take place.
The MAsyncEventHandler
must also be passed into the
sink in the same thread in which the sink is to transfer data. If the sink
can generate events during a data transfer, then it must keep the reference
to event handler.
It is only necessary to provide an implementation of MDataSink::SinkThreadLogon()
if
the sink has thread specific initialisation and/or can generate events during
data transfer.
Implementation of the MDataSink::SinkPrimeL()
, MDataSink::SinkPlayL()
, MDataSink::SinkPauseL()
and MDataSink::SinkStopL()
methods is optional. They
are called when a controller performs a transition to the corresponding state.
For example, if the controller starts, or resumes, playing then MDataSink::SinkPlayL()
is
called.
Implementation of the MDataSink::SouceThreadLogoff()
method
is optional. This method is called when the controller has finished with the
data sink and is called in the same thread as the data transfer. This allows
the data sink to perform any thread specific destruction such as the closure
of handles.
Sink Data Types
The MDataSink::SinkDataTypeCode()
method must be implemented
by the data sink. It should return the data type of the sink for the specified
media ID. Some data sinks may need their data type to be explicitly set, via
the SetSinkDataTypeCode
method or via negotiation with a
data sink, in which case this function should either return a default FourCC
code,
or a NULL
code, to indicate that the data type is not yet
known.
Implementation of the MDataSink::SetSinkDataTypeCode()
method
is optional. It should be implemented where the sink can support multiple
data types.
Sink Custom Commands
The MDataSink::SinkCustomCommand()
method facilitates
the use of custom commands. An example implementation is shown below:
void CAcmeDataSink::SinkCustomCommand(TMMFMessage& aMessage) { // First, check we can handle message by checking its interface id if (aMessage.InterfaceId() != KUidAcmeDatasinkCustomCommandInterface) { aMessage.Complete(KErrNotSupported); return; } // Next, dispatch the command to the appropriate method. TInt error = KErrNone; switch (aMessage.Function()) { case EAcmeDatasinkCustomCommandOne: error = HandleCustomCommandOne(aMessage); break; case EAcmeDataSinkCustomCommandTwo: error = HandleCustomCommandTwo(aMessage); break; default: error = KErrNotSupported; break; } aMessage.Complete(error); }
Use of the custom command mechanism is preferable to adding extra methods as it avoids extra casting.
Sink Priority Settings
The MDataSink::SetSinkPrioritySettings()
method is optional.
It is used to provide a mechanism to determine which sink should have priority
in cases where more than one client wishes to use the same physical sink.
An example might be an audio output, although several audio output sinks can
be created, the actual hardware may only have one physical speaker. Therefore,
if one audio output is being used to play music, and another is being used
to play a ring tone due to an incoming call, then the latter needs to take
precedence. TMMFPrioritySettings
contains an iPriority
TInt
data member, where 100 is maximum priority, 0 is normal and
-100 is minimum priority. The TMMFPrioritySettings::TMdaPriorityPreference
and TMMFPrioritySettings::TMMFStateA
data members provide further information which may be used if required.
These specify whether the priority applies to recording or playing.