Creating a Sink Plugin

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.