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