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.
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();
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.
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.
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.
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.
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.