A data source plugin needs to implement the pure virtual, and where appropriate
override the virtual, MDataSource
base class mixin functions.
This section describes how to implement the MDataSource
base
class.
Note that a single plugin can be both a source and a sink, i.e. derive
from both MDataSource
and MDataSink
.
Source Plugin Instantiation
A client application instantiates a source plugin using the RMMFController::AddDataSource()
method,
passing in the UID of the source as one of the method's parameters. The controller
framework instantiates a source via the MDataSource::NewSourceL()
method,
rather than the more conventional NewL()
method. This is
because a plugin can be both a source and a sink of multimedia data, in which
case the instantiation methods for sources and sinks need to be distinct.
The MDataSource
base class instantiation methods are described
below, followed by information on how to write the instantiation methods in
the derived class.
The derived data source needs to implement MDataSource::NewSourceL()
and MDataSource::ConstructSourceL()
for
instantiation. The constructor of a data source plugin needs to specify what
type of data source it is. This is achieved by passing a type UID into the MDataSource
constructor.
If the source has additional methods, that are not part of the base MDataSource
class,
then a further layer of instantiation is required. For example, suppose CAcmeDataSource
has
some extra methods that are not part of the base class, then an additional
mxin interface for this class is required. For example:
class MAcmeDataSource : public MDataSource { public: inline static MAcmeDataSource* NewAcmeDataSourceL(TUid aImplemetationUid, const TDesC8& aInitData); //This allows dynamic linkage to the Class: }
The NewAcmeDataSourceL
should be implemented as follows:
MAcmeDataSource* retPtr = static_cast<MAcmeDataSource*>(MDataSource::NewSourceL(aImplementationUid, aInitData));
The class should derive from the MAcmeDataSource
rather
than MDataSource
as follows:
class CAcmeDataSource: public CBase, public MAcmeDataSource { public: MDataSource* NewSourceL();
Source Plugin Buffer Creation
Buffers are required to transfer data between a data source and a data sink. These buffers can be created by the source and/or sink. The methods below are for source buffer creation.
The MDataSource::CanCreateSourceBuffer()
method must
be implemented by the data source plugin. Most sources should be capable of
creating their own buffer and so would return ETrue
. Note
that just because a source can create a source buffer, this does not guarantee
that the framework will use the buffer created by the source. The buffer that
is used depends on factors such as whether a null codec is used and whether
the sink is the reference buffer, in either of these cases the source buffer
will not be used.
The MDataSource::CreateSourceBufferL()
method is called
by the framework to create a buffer from the source. This method should create
a buffer of length zero bytes and a maximum length of an appropriate size
for the source. The appropriate size is determined by source specifics, such
as whether the source 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 source and sink are required. The returned buffer must
derive from CMMFBuffer
but will be a derived buffer, for
example CMMFDataBuffer
or a video frame buffer.
The overloaded MDataSource::CreateSourceBufferL()
method,
which has an additional aSinkBuffer
parameter, is optional.
The default implementation is identical to the standard CreateSourceBufferL
above.
This version is used where the nature of the sink buffer may impact the created
source 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 source
buffer.
Data Transfer
Data transfer methods are used by a datapath to perform the transfer of data between the source and the sink.
The MDataSource::FillBufferL()
method is used to obtain
data from the data source. It is a passive method in that an external component
such as a datapath must ask the source to fill the buffer with data from the
source. The MDataSource::FillBufferL()
method may operate
either synchronous and asynchronously:
A synchronous source
is one in which the mechanism for filling the source buffer is synchronous.
That is, the source fills the buffer and calls the BufferFilledL()
method
on the consumer of the dat, which is itself derived from MDataSink
to
indicate that the buffer has now been filled.
An asynchronous source
is one in which the mechanism for filling the source buffer operates asynchronously.
The MDataSource::FillBufferL()
method will typically make
an asynchronous request, for example via an active object, such that when
the FillBufferL
method has returned, the buffer has not yet
been filled. The BufferFilledL()
call back will occur some
time later when the asynchronous request has been processed.
The MDataSink::BufferFilledL()
method is the callback
on a sink when the source has filled a buffer with source data. The source
normally operates in a passive mode in that a sink of data will ask the source
to fill a buffer via a call to MDataSource::FillBufferL()
above.
However, the sink that makes the FillBufferL()
call on the
source needs this callback to know when the buffer has been filled. This is
applicable to a datapath which is both a sink and source of data. The actual
sink, as seen by the controller, would normally operate passively and return KErrNotSupported
for
this procedure.
The MDataSink::EmptyBufferL()
method is used to transfer
data to the data sink. This is a passive method in that an external component,
such as a datapath, must send the buffer with data to the sink. This method
may operate either synchronously or asynchronously:
A synchronous sink is
one in which the emptying of the sink buffer is synchronous. That is, the
sink finishes processing the buffer and then calls the BufferEmptiedL()
method
on the data supplier.
An asynchronous sink
is one in which the mechanism for emptying the sink buffer operates asynchronously.
The MDataSink::EmptyBufferL()
method will typically make
an asynchronous request, for example via an active object, such that when
the EmptyBufferL()
method has returned, the buffer is not
yet available for reuse. The BufferEmptiedL()
call back should
occur some time later when the asynchronous request has been processed.
Note that the 'Empty' in the method name does not imply that the sink really
has to empty the buffer. The returned buffer does not have to contain no data
and have a length of 0. The buffer passed back to the supplier by the BufferEmptiedL()
could
be the same buffer that was passed in with the EmptyBufferL()
method,
although it does not have to be. The buffer passed back is intended to be
used as the next buffer passed into the subsequent call to EmptyBufferL()
.
The MDataSource::BufferEmptiedL()
method is the callback
on the source when the source asks a sink to empty a buffer that originated
from the sink. The sink normally operates in a passive mode (will not ask
the source to fill a buffer with data) in that a source of data will ask the
sink to empty a buffer via a call to EmptyBufferL()
above,
but a source can ask a sink to empty a buffer by calling EmptyBufferL()
on
the sink. In which case the sink informs the source that it has finished with
the buffer by calling the source's BufferEmptiedL()
method.
Sources which only support the passive mode of operation should return KErrNotSupported
.
Source State Methods
The MDataSource
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 MDataSource::SourceThreadLogon()
method indicates
to the source that it can perform any thread specific initialisation. This
is so that the thread in which the data source is instantiated is not necessarily
the same thread in which the actual transfer of data between the source and
the sink takes place. It is only necessary to provide an implementation of MDataSource::SourceThreadLogon()
if
the source has thread specific initialisation and/or can generate events during
data transfer.
The MAsyncEventHandler
must also be passed into the
source in the same thread in which the source is to transfer data. If the
source can generate events during a data transfer, then it must keep the reference
to event handler.
Implementation of the MDataSource::SourcePrimeL()
, MDataSource::SourcePlayL()
, MDataSource::SourcePauseL()
and MDataSource::SourceStopL()
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 MDataSource::SourcePlayL()
is
called.
Implementation of the MDataSource::SouceThreadLogoff()
method
is optional This method is called when the controller has finished with the
data source and is called in the same thread as the data transfer. This allows
the data source to perform any thread specific destruction such as the closure
of handles.
Source Data Types
The MDataSource::SourceDataTypeCode()
method must be
implemented by the data source. It should return the data type of the source
for the specified media ID. Some data sources may need their data type to
be explicitly set, via the SetSourceDataTypeCode
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 MDataSource::SetSourceDataTypeCode()
method
is optional. It should be implemented where the source can support multiple
data types.
Source Sink Negotiation
In many cases a data source will need to adjust its settings and data type according to the settings of the data sink. The opposite is also true in that a data sink may need to adjust its settings and data type according to the data source. This process is known as negotiation. An example of negotiation is where the source is an audio input recording to a clip of a certain data type. The source audio input (such as a microphone) attempts to match its settings to that required by the clip. For example, if the audio input supports the same data type as that required by the clip to be recorded to, then the negotiation should set the source audio input to the same data type and settings as the clip sink.
Implementation of the MDataSource::NegotiateSourceL()
method
is optional. It needs to be implemented by a data source only if the source
needs to configure itself in accordance with the sink.
Implementation of the MDataSink::NegotiateL()
method
is optional. It only needs to be implemented by a data sink if the data sink
needs to configure itself in accordance with the source.
Note that it is not always necessary to call both the source and sink negotiate functions. It is up to the controller to determine whether one, or both, are the most appropriate. The controller is also responsible for determining the sequence of the negotiate functions. For example, if an audio input data source is negotiating with a format sink such that the audio input needs to adjust it's settings to that of the sink, then there is need to perform this negotiation until the sink has been configured.
Source Custom Commands
The MDataSource::SourceCustomCommand()
method facilitates
the use of custom commands. An example implementation is shown below:
void CAcmeDataSource::SourceCustomCommand(TMMFMessage& aMessage) { // First, check we can handle message by checking its interface id if (aMessage.InterfaceId() != KUidAcmeDatasourceCustomCommandInterface) { aMessage.Complete(KErrNotSupported); return; } // Next, dispatch the command to the appropriate method. TInt error = KErrNone; switch (aMessage.Function()) { case EAcmeDatasourceCustomCommandOne: error = HandleCustomCommandOne(aMessage); break; case EAcmeDataSourceCustomCommandTwo: 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 as described is required for source/sink instantiation.
Source Priority Settings
The MDataSource::SetSourcePrioritySettings()
method
is optional. It is used to provide a mechanism to determine which source should
have priority in cases where more than one client wishes to use the same physical
source. 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.