| 
                   | 
               
                  
                   | 
            |
A controller plugin is the main type of multimedia framework (MMF)
            plugin. A controller plugin typically supports playing or recording one or more
            multimedia formats, mp3 or avi for example. A
            controller plugin is able to read data from one or more sources, apply any
            required data transformations, then write the data to the appropriate
            sink.
         
Writing a controller plugin involves the following tasks:
The controller plugin API is defined by the controller framework
            element of the MMF. All controller plugin implementations must derive from the
            abstract class CMMFController and implement its pure
            virtual methods.
         
As well as providing the controller API, the
            CMMFController base class also provides functionality such
            as instantiation of controller plugins via the ECom plugin framework, inter-thread message
            decoding and parameter unpacking, an object reference counting mechanism and a
            few utility methods for use by controller plugins.
         
The following sections describe how to implement the controller API.
Sources and sinks are themselves ECom plugins and provide an interface that
            allows the controller to read and write raw data buffers. The type of source or
            sink, along with any source/sink-specific initialisation parameters (for
            example the file name for a file source), is specified by the client. The
            source or sink is then created and owned by the controller framework and passed
            into the controller plugin via a call to
            CMMFController::AddDataSourceL() or
            CMMFController::AddDataSinkL() for a data source or a data
            sink respectively. 
         
Note that ownership of a source or sink always remains with the
            controller framework and not with the controller plugin, and that a controller
            plugin should never delete a source or sink. If the controller plugin leaves
            during the call to CMMFController::AddDataSourceL() or
            CMMFController::AddDataSinkL(), the source or sink will be
            destroyed by the controller framework.
         
There is no limit on the number of sources or sinks that can be added to a particular controller plugin, other than the limit provided by the plugin itself. For example, an audio controller would have one source and one sink. A video playing controller would have one source and two sinks (display and speaker).
Sources and sinks can also be removed from the controller plugin by
            the client. The reference of the particular source or sink being removed is
            passed into either the CMMFController::RemoveDataSourceL()
            or CMMFController::RemoveDataSinkL() method to remove a
            data source or a data sink respectively. The controller plugin should remove
            any reference that it has to the source or sink; the source or sink will then
            be deleted by the controller framework. Note that if the controller plugin
            leaves during these calls (for example if it does not support the removal of
            sources or sinks), the controller framework will not delete the source or
            sink.
         
Controller plugin states, along with the commands to transition the plugin between the states, are:
Open, this is the state in which the controller is
                  in just after it has been created. Once the controller has been configured, it
                  moves to the Stopped state. This transition typically involves
                  adding the source(s) and sink(s) and configuring any utility objects or other
                  plugins owned by the controller.
               
Stopped, in this state the controller should not
                  have any resources or buffers allocated, and the position should be
                  zero.
               
Primed, in moving to the primed state, the
                  controller should allocate any buffers and resources that will be required to
                  perform playback. The playback/recording position should also be stored when in
                  the primed state. Controllers should only be moved into this state just before
                  being told to play, otherwise unnecessary memory and resources will be
                  used.
               
Playing, when in this state, the controller plugin
                  should be transferring data between source(s) and sink(s).
               
Calling CMMFController::ResetL() on the
            controller plugin should cause it to revert back to the Open
            state. This will involve stopping playback, deleting any allocated resources,
            and removing any references to sources and sinks.
         
Position and duration can be set by deriving from the pure virtual
            methods CMMFController::PositionL(),
            CMMFController::SetPositionL() and
            CMMFController::DurationL().
         
Note that position has meaning only in the
            Primed and Playing states. The controller plugin
            should leave with KErrNotReady if an attempt is made to set or get
            the position when the controller is in any other state. However, it should be
            possible to query the duration as soon as the data source or data sink has been
            added.
         
Custom commands allow controller plugin writers to extend the basic controller API. They provide a way of passing raw messages from the client through the controller framework to the controller plugin.
The CMMFController::CustomCommand() method can
            be implemented in the controller plugin to support custom commands. The default
            implementation provided by the base class is to complete the message with
            KErrNotSupported. Note that it is imperative that the message is
            always completed by this method and that this method cannot leave. Any error
            that occurs must be passed back to the client by completing the message with
            the error code.
         
Each TMMFMessage has an associated interface UID
            which should be checked before handling any message to make sure that the
            command is supported.
         
The following code illustrates a typical custom command implementation.
void CMyController::CustomCommand(TMMFMessage& aMessage)
    {
    // First, check we can handle message by checking its interface id
    if (aMessage.InterfaceId() != KUidMyControllerCustomCommandInterface)
       {
        aMessage.Complete(KErrNotSupported);
        return;
       }
    // Next, dispatch the command to the appropriate method.
    TInt error = KErrNone;
    switch (aMessage.Function())
        {
    case EMyCustomCommandOne:
        error = HandleCustomCommandOne(aMessage);
        break;
    case EMyCustomCommandTwo:
        error = HandleCustomCommandTwo(aMessage);
        break;
    default:
        error = KErrNotSupported;
break;
        }
    aMessage.Complete(error);
    }
The above example shows synchronous command handling. If the plugin
            needs to do something which may take a long time, the aMessage
            parameter should be copied, stored and completed later when processing has
            finished.
         
The methods HandleCustomCommandOne and
            HandleCustomCommandTwo above, copy any data to and from the
            client. This can be done using the appropriate TMMFMessage
            methods.
         
The CMMFController::DoSendEventToClient()
            utility method allows a controller plugin to send an event to its
            client.
         
The multimedia client utilities listen for specific event types and error codes. If the controller plugin is accessed by clients that are using the multimedia client utility APIs (which will usually be the case) then those event types and error codes should be used. The event types and error codes for which the multimedia client utilities listen are listed below:
                  
  | 
            
The controller framework contains an object referencing mechanism
            that allows a client to send messages to arbitrary objects in the controller
            thread without having to go via the controller plugin itself. In order to
            achieve this, the arbitrary object must be derived from
            CMMFObject and added to the object container. The object
            container can be accessed via the
            CMMFController::MMFObjectContainerL() method.
         
Sources and sinks have a CMMFObject wrapper
            placed around them by the controller framework, and can receive messages from
            the client. This mechanism is also used to reference source(s) and sink(s), so
            the client can specify exactly which source/sink when calling the
            CMMFController::RemoveDataSourceL() or
            CMMFController::RemoveDataSinkL() methods.
         
Note that objects added to the
            CMMFObjectContainer are owned by the
            CMMFObjectContainer.
         
Each object added to the CMMFObjectContainer is assigned
            a handle. This handle must be passed back to the client in order for the client
            to be able to send messages directly to the object. The client should use this
            handle to set the handle of the TMMFMessageDestination
            parameter in the RMMFController::CustomCommandAsync() or
            RMMFController::CustomCommandSync() method for
            asynchronous or synchronous operation respectively. The custom command will
            then be routed directly to the CMMFObject by the
            controller framework.
         
The core controller plugin API provides only basic support for
            controlling the flow of data. The application-level multimedia utility APIs
            (CMdaAudioPlayerUtility for example) contain much richer
            functionality. The application-level multimedia utility APIs provide clients
            with a concrete API to access extended controller functionality, and to give
            controller plugins a concrete mixin API to implement. 
         
Several sets of standard custom command APIs have been defined. The following table shows which of these classes must be implemented to allow the controller plugin to be used properly from each of the application-level utility APIs.
                  
  | 
            
In order to implement the required custom command APIs, the controller
            plugin should derive from the mixins shown in the table above, then use the
            CMMFController::AddCustomCommandParserL() method to
            register itself as being able to handle that API.
         
The CMMFCustomCommandParserBase derived object
            decodes the custom command on behalf of the controller plugin and calls the
            concrete API via the mixin interface. The following table shows which
            CMMFCustomCommandParserBase object should be used with
            each mixin class.
         
                  
  | 
            
The following example code shows how the controller should register itself with the controller framework to receive standard custom commands.
class CMyControllerPlugin : public    CMMFController,
                                      MMMFAudioControllerCustomCommandImplementor,
                                      MMMFAudioPlayDeviceCustomCommandImplementor
    {
...
private:
    void ConstructL();
    };
void CMyControllerPlugin::ConstructL()
    {
...
    // Construct custom command parsers
    CMMFAudioControllerCustomCommandParser* audConParser =
                                     CMMFAudioControllerCustomCommandParser::NewL(*this);
    CleanupStack::PushL(audParser);
    AddCustomCommandParserL(*audConParser); //parser now owned by controller framework
    CleanupStack::Pop(audConParser);
    CMMFAudioPlayDeviceCustomCommandParser* audPlayDevParser = 
                                     CMMFAudioPlayDeviceCustomCommandParser::NewL(*this);
    CleanupStack::PushL(audPlayDevParser);
    AddCustomCommandParserL(*audPlayDevParser); //parser now owned by controller framework
    CleanupStack::Pop();//audPlayDevParser
    }
It is also possible for controller plugins to define their own standard
            custom command classes. This might be useful if a group of plugins have the
            same API (for example a group of MIDI controller plugins). Clients
            would then be able to access equivalent functionality in each plugin using the
            same API. 
         
The MMF provides a set of utility classes and other types of plugins to
            aid with writing controller plugins. All utility classes are provided in the
            library MMFServerBaseClasses.DLL. A brief description of each of
            the classes follows. Note that the use of data sources, data sinks and buffers
            is mandatory. The use of the other classes is optional.
         
Data Sources and Sinks
Buffers
Datapath
Codecs
Formats
Data sources and sinks are ECom
            plugins, and are derived from the base class MDataSource
            or MDataSink respectively. The currently available data
            sources and sinks are CMMFFile,
            CMMFDescriptor, CMMFAudioInput and
            CMMFAudioOutput. They are created by the controller
            framework and passed into the controller plugin by reference using the
            CMMFController::AddDataSourceL() or
            CMMFController::AddDataSinkL() method.
         
Some sources and sinks have extended APIs that allow the controller
            plugin to perform actions, such as setting the volume. The type of the source
            or sink can be checked by the controller plugin using the methods
            MDataSource::DataSourceType() and
            MDataSink::DataSinkType(). These methods return a UID
            which can be checked against known UIDs to identify the extended API of the
            source or sink. For example, the following code would be used to set the volume
            of a speaker sink.
         
MMMFAudioOutput* audioOutput = STATIC_CAST(MMMFAudioOutput*, iDataSink);
audioOutput->SoundDevice().SetVolume(14);
It is possible dynamically to add new data sources and sinks to the system, see How to write a Source/Sink plugin for information.
Buffers are used to contain data as it is being transferred from a source
            to a sink. There are several buffer types all derived from a common base class
            CMMFBuffer: CMMFDataBuffer,
            CMMFDescriptorBuffer and
            CMMFTransferBuffer.
         
The datapath is a utility class that transfers data from a single source to a single sink, via a codec plugin if the source and sink data types differ. The API of the datapath is very similar to that of the basic controller plugin API, and much of the complexity of controller plugins can be avoided by using the datapath.
The datapath can be used either through:
CMMFDataPath, in which case the data will be
                  transferred from the source to the sink in the main thread of the controller
                  plugin.
               
RMMFDataPath, in which case a new thread will be
                  launched to transfer the data from the source to the sink. This is useful if a
                  controller has multiple sources and sinks (a video controller for example). The
                  multiple datapaths can each be executed in their own thread to improve
                  performance.
               
If the source and sink data types are the same then no codec is required
            and the datapath will use a null codec and the buffers will be
            transferred straight from the source to the sink without being copied in
            between.
         
Codec plugins are derived from CMMFCodec and can be
            used by controller plugins to convert data from one data type to another. Codec
            plugins are designed to be used by different controller plugins, for example
            both an audio controller and a video controller might want to make use of a
            PCM8 to PCM16 codec.
         
See How to write a codec plugin for details of how to implement codec plugins.
Controller plugin writers may wish to implement their format support by writing format plugins. While format plugins can only be used by one controller plugin, this does make it much easier to dynamically extend the formats supported by the controller plugin without providing a whole new controller plugin. See How to write a format plugin for details of how to implement format plugins.
This section describes how to write the ECom plugin registry file. See ECom for generic instructions on how to write an ECom plugin.
The controller plugin resolver is responsible for deciding which
            controller plugin should be used to provide support for a particular format.
            Controller plugins provide information in their ECom resource file
            which allows the controller framework (and ultimately the client application)
            to determine:
         
The display name and supplier of the controller plugin.
The media types supported by the controller plugin (e.g. audio or video).
The formats the controller plugin can play and record.
For each format supported, for both playing and recording, the following information is provided:
Display Name of the format
Supplier of the format (in case support for this format was provided by a different party to the controller plugin)
The Media Types supported by the format
The MIME types applicable to the format
The file extensions that identify files that can be handled by the format
Any header data segments that could be matched to the first few bytes of multimedia data to identify that the data could be handled using this format
Most of the information outlined above is provided by the plugin in the
            opaque_data field of the ECom resource file. This field takes an
            8-bit string and is limited to 127 characters in length. A tagged data scheme
            is used to separate the different types of data. The tags are all three
            characters in length, and the scheme only uses opening tags (i.e. no end tags)
            to reduce overhead and complexity. The tags available
            are:
         
                  
  | 
            
Formats can be supported by controller plugins either:
Internally. In this case the controller is able to read or write the
                  format by itself. Controller plugins can specify the formats they support
                  internally with extra entries in their plugin resource file. They define two
                  new ECom plugin interface uids (one for
                  play formats, the other for record formats) in their opaque_data
                  field using the tags <p> and <r>. The
                  play formats they support are then listed as ECom plugin
                  implementations of the play format interface UID, and likewise with the record
                  formats. These interface UIDs and implementations do not correspond to any real
                  plugins. They are simply a way of letting the controller framework know exactly
                  which formats the controller supports in a scalable manner. The implementation
                  UIDs of each format should be known to the controller so that a client can
                  specify the format that a controller should use by using this UID.
               
By using format plugins. The MMFServerBaseClasses
                  component defines base classes for both encoding and decoding format plugins.
                  By using format plugins, the formats supported by a controller plugin can be
                  extended dynamically without having to change the controller plugin itself. The
                  ECom plugin resource file of each format
                  plugin contains the UID of the controller plugin that it extends, allowing the
                  controller framework to build up an accurate picture of the formats supported
                  by each controller.
               
The following is an example of a resource file for an audio controller
            plugin that supports playing WAV and AU, and
            recording AU.
         
RESOURCE REGISTRY_INFO theInfo
    {
    dll_uid = 0x101F1234;
    interfaces = 
        {
        INTERFACE_INFO // Controller Plugin Description
           {
           interface_uid = KMmfUidPluginInterfaceController ;
           implementations = 
               {
               IMPLEMENTATION_INFO
                   {
                   implementation_uid = 0x101F1235 ;
                   version_no = 1;
                   display_name = "Symbian Audio controller";
                   default_data = "?";
                   opaque_data = 
                          “<s>Symbian<i>0x101F5D07<p>0x101F0001<r>0x101F0002";
                           // SUPPLIER = Symbian
                           // MEDIA ID = uid for audio media type
                           // PLAY FORMATS = look at interface uid 0x101f0001
                           // RECORD FORMATS = look at interface uid 0x101f0002
                   }
               };
            },
        INTERFACE_INFO // Play Formats Description
            {
            interface_uid = 0x101F0001 ;
            implementations = 
               {
               IMPLEMENTATION_INFO
                   {
                   implementation_uid = 0x101F1236 ;
                   version_no = 1;
                   display_name = "WAV Play Format";
                   default_data = "?";
                   opaque_data = 
                   “<s>Symbian<i>0x101f5d07<e>.wav<h>RIFF????WAVE<m>Audio/Wave";
                   // SUPPLIER = Symbian
                   // MEDIA ID = uid for audio media type
                   // FILE EXTENSION = .wav
                   // HEADER DATA = look for RIFF????WAVE in header data. The’?’s
                   // indicate a single character wildcard.
                   // MIME TYPE = Audio/Wave
                   },
               IMPLEMENTATION_INFO
                   {
                   implementation_uid = 0x101F1237 ;
                   version_no = 1;
                   display_name = "AU Play Format";
                   default_data = "?";
                   opaque_data = 
                   “<s>Symbian<i>0x101f5d07<e>.au<h>.snd";
                   // SUPPLIER = Symbian
                   // MEDIA ID = uid for audio media type
                   // FILE EXTENSION = .au
                   // HEADER DATA = look for .snd in header data.
                   // MIME TYPE = No mime type
                   }
               };
            },
        INTERFACE_INFO // Record Formats Description
            {
            interface_uid = 0x101F0002 ;
            implementations = 
               {
               IMPLEMENTATION_INFO
                   {
                   implementation_uid = 0x101F1238 ;
                   version_no = 1;
                   display_name = "WAV Record Format";
                   default_data = "?";
                   opaque_data = 
                   “<s>Symbian<i>0x101f5d07<e>.wav<h>RIFF????WAVE<m>Audio/Wave";
                   // SUPPLIER = Symbian
                   // MEDIA ID = uid for audio media type
                   // FILE EXTENSION = .wav
                   // HEADER DATA = look for RIFF????WAVE in header data. The’?’s
                   // indicate a single character wildcard.
                   // MIME TYPE = Audio/Wave
                   }
                };
            }
        };
    }
Note the default_data field is not used by the controller
            framework. A UTF8 to unicode conversion is performed
            on the Supplier. All other data is left in ascii.