Writing a Parser Plug-in

This tutorial describes how to write a MUF parser plug-in.

Purpose

The purpose of this document is to show you how to write a MUF parser plug-in and integrate it into the ECom framework.

Required Background

The ID3 Parser Plug-in Overview describes the reference implementation of the ID3 parser plug-in.

The Metadata Utility Library Overview introduces the metadata client utility.

Introduction

The main purpose of parser plug-ins is to read metadata from a media source and fill the metadata buffer. Parser plug-ins typically support one media format (but can support multiple formats). Parser plug-ins are managed using the ECom framework and can be loaded at run-time by MUF.

The following shows the class diagram for the MUF:

Figure 1. MUF Class Diagram

The CMetaDataClientUtility class provides the client interface and enables a client to perform various metadata operations. MUF provides the parser plug-in interface CMetaDataParser. All parser plug-in implementations are derived from CMetaDataParser.

Using Parser Plug-ins

Writing a MUF parser plug-in involves the following tasks:

  • Implementing the CMetaDataParser base class

  • Extending the metadata field enumeration

  • Adding support for parser plug-in extensions

  • Integrating the parser plug-in into the ECom framework

Basic Procedure

The high level steps to write a parser plug-in are shown here:

  1. Implement the CMetaDataParser parser plug-in base class. All parser plug-ins must be derived from this class and implement all the pure virtual methods. When implementing CMetaDataParser, consider the following:

    • Parsing:

      CMetaDataParser provides parsing methods for both synchronous and asynchronous (ParseSyncL() and ParseAsyncL()) extraction of metadata. These methods handle the filtering options specified by the client. MUF allows the client to filter metadata fields based on the options specified in TRequestedFields. The parser plug-in must filter the metadata fields and append the field items in the order requested by the client. The following filtering options are available to the client:

      Filtering Option Description

      EAllFieldsNoOrder

      The parser plug-in extracts metadata for all of the fields but in no order. It simply iterates through a file or buffer and fills the metadata buffer. Field items are added in default order as specified in the file.

      EOnlyRequestedFields

      The parser plug-in extracts metadata only for requested fields specified in const RPointerArray<TMetaDataEntryId>& and in the same order.

      EAllFieldsRequestedListFirst

      The metadata buffer is filled with the requested fields first in order, and then with the remaining fields.

      If the client wants to get a part of metadata or that metadata first in the order, it specifies the wanted fields in an array of TMetaDataEntryId and passes it to the parsing methods. The parser plug-in must extract and arrange the metadata fields in the order specified in the CMetaDataFieldsArray& parameter passed in the parsing methods.

    • Filling the metadata buffer:

      The metadata buffer (CMetaDataFieldsArray) is created by the framework and an array reference is passed to the parser plug-in. When the parser plug-in has extracted the metadata it fills this array with field values and returns them to the client through the framework. If the client has set a maximum size limit for the metadata buffer then the parser plug-in fills the metadata buffer until it reaches the limit. If no maximum size has been set, then the parser plug-in fills the metadata buffer until free space is available in the client thread.

      For more information, see Setting the Maximum Buffer Limit.

    • Handling more metadata:

      If the metadata buffer has reached its set limit and the parser plug-in has more metadata to fill then it should pause parsing and inform the MUF client of this. In synchronous parsing, the aMoreData parameter is set to ETrue to indicate that the parser plug-in has more data. If parsing is being done asynchronous then the parser plug-in uses the MMUFParserObserver::MetaDataBufferFilled() callback to specify the status of the parsing.

      class MMUFParserObserver
          {
      public:
          virtual void MetaDataBufferFilled(TInt aErrorCode, TBool aMoreData) =0;
          };
      

      When the MUF client is informed that it has more metadata to get then it can change from synchronous parsing to asynchronous parsing (or from asynchronous to synchronous) to get the remaining metadata. For example, if the MUF client has initiated synchronous parsing then the MUF calls ParseSyncL() on the parser plug-in. If there is more data then the client can initiate asynchronous parsing and MUF calls MoreDataAsyncL() on the parser plug-in. Note that if the client does not have free space to receive further data then it is responsibility of the client to empty the metadata buffer before calling for more metadata.

      If the client is not interested in getting the remaining data, it calls StopParsing(). In the case of StopParsing(), the parser plug-in does the cleanup activity.

    • Asynchronous error reporting:

      If the parser plug-in gets an error while asynchronously retrieving metadata, it should report the error to MUF using the Complete() method.

  2. Extend the TMetaDataEntryId metadata field enum.

    MUF defines a generic enum (TMetaDataEntryId) of common metadata field items such as "Song Title" and "Artist Name". Parser plug-ins can extend TMetaDataEntryId with their own extra metadata field items.

    Each metadata field item (CMetaDataFieldItem) is identified by the Field ID parameter (aFieldId) which takes an integer. If a parser plug-in wants to add a new metadata field item, it must provide a new Field ID. The new Field ID must follow after the Symbian reserved Field IDs. The parser plug-in implements the ParserFieldExtensions() method to add the new Field ID.

    Note: New Field IDs can only be interpreted between the client and the parser plug-in.

  3. Add support for extending the parser plug-in.

    MUF provides an extension mechanism to enable clients to access non-standard, parser-specific functionality. A custom interface (or extension) can be defined to access extended functionality from a parser plug-in implementation. The parser plug-in can implement or load this custom interface (or extension). MUF accesses this extension by calling the following method on the parser plug-in:

    IMPORT_C virtual TInt GetExtension(TUint
                    aExtensionId, TAny*& aCMInterPtr, TAny* aCMIntPtr); 

    The parser plug-in can be extended by multiple custom interfaces. The desired custom interface is recognized using the Extension ID passed as the aExtensionId parameter in the GetExtension() method.

  4. Integrate the parser plug-in into the ECom framework

    The MUF parser plug-in resolver decides which parser plug-in should be used to provide parsing for a particular format. Parser plug-ins provide information in their ECom resource (.RSS) file, which allows MUF (and ultimately the client application) to determine:

    • The Display Name of the parser plug-in

    • The Supplier of the parser plug-in

    • The MIME types applicable to the parser plug-in

    • The file extensions that identify files which can be handled by the parser

    • 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 parser plug-in.

    For all formats, the information outlined above is provided by the parser plug-in in the opaque_data field of the ECom resource file under IMPLEMENTATION_INFO. The opaque_data field takes an 8-bit string and is limited to 127 characters in length. A tagged data scheme is used to separate the different data types. The scheme only uses opening tags (no end tags) to reduce overhead and complexity. All tags are three characters in length. The available tags are described in the following table:

    Tag Description

    <s>

    The supplier of the plug-in.

    <m>

    A MIME type that describes the format. Multiple entries with this tag can be included.

    <e>

    A file extension supported by this format. Multiple entries with this tag can be included.

    <h>

    A segment of header data that can be matched against the first few bytes of a clip to check whether this format is capable of handling the clip. Multiple entries with this tag can be included. If this tag is omitted, the header data is not recognisable and the clip can be opened by matching the file extension.

Examples

The following examples illustrate the steps involved in writing a parser plug-in:

  1. The following implements the parsing methods:

    void CID3Parser::ParseSyncL(const TRequestedFields aOrder, RPointerArray<TInt>& aWantedFields, TBool& aMoreData, CMetaDataFieldsArray& aFdArray)
        {
        ReInitialiseL();
        iID3Decode = CID3Decode::NewL(aFdArray, aWantedFields, aOrder);
        ParseMetaDataL(aMoreData);
        }
    
    void CID3Parser::ParseSyncL(const TRequestedFields aOrder, TBool& aMoreData, CMetaDataFieldsArray& aFdArray)
        {
        ReInitialiseL();
        iID3Decode = CID3Decode::NewL(aFdArray, aOrder);
        ParseMetaDataL(aMoreData);
        }
    
    void CID3Parser::ParseAsyncL(const TRequestedFields aOrder, RPointerArray<TInt>& aWantedFields, CMetaDataFieldsArray& aFdArray)
        {
        ReInitialiseL();
        iID3Decode = CID3Decode::NewL(aFdArray, aWantedFields, aOrder);
        SetBufferSize();
        SelfComplete(KErrNone);    
        }
    
    void CID3Parser::ParseAsyncL(const TRequestedFields aOrder, CMetaDataFieldsArray& aFdArray)
        {
        ReInitialiseL();
        iID3Decode = CID3Decode::NewL(aFdArray, aOrder);
        SetBufferSize();
        SelfComplete(KErrNone);
        }
  2. Add a new metadata field item:

    const RPointerArray<CFieldExtensionInfo>& CID3Parser::ParserFieldExtensionsL()
        {
        TInt fdId = 1001; //Id should not be there in the generic structure
        TBuf8<KID3FieldNameSize> fdName;
        fdName = KEMetaDataPaymentOptions;
        CFieldExtensionInfo* self = CFieldExtensionInfo::NewL(fdId, fdName);
        CleanupStack::PushL(self);
        iFdExtInfo.AppendL(self);
        CleanupStack::Pop(self);
        return iFdExtInfo;
        }
    
    
  3. Add support for extending the parser plug-in:

    TInt CID3Parser::GetExtension(TUint aExtensionId, TAny*& aCMInterPtr, TAny* aCMIntPtr)
        {
        return CMetaDataParser::GetExtension(aExtensionId, aCMInterPtr, aCMIntPtr);
        }
    
    
  4. Example parser plug-in ECom resource file:

    RESOURCE REGISTRY_INFO theInfo
        {
        dll_uid = KID3ParserDLLUid;
        interfaces =
            {
            INTERFACE_INFO // Parser Plug-in Description
                {
                interface_uid = KUidPluginInterfaceMetaDataParser;
                implementations =
                    {
                    IMPLEMENTATION_INFO
                        {
                        implementation_uid = KID3ParserUid;
                        version_no = 1;
                        display_name = "id3parser";
                        default_data = "?";
                        //Above field gives all the information about the parser
                        // <s> = Supplier of the parser plug-in
                        // <e> = file extension supported by this format
                        // <h> = A segment of header data that can be matched against the first     
                                       //           few bytes of a clip to check whether this format is capable 
                                       //           of handling the clip.
                        // <m> = mime type that describes the format
                    opaque_data="<s>Symbian<i>0x101F5D07<e>.mp3<h>ID3<m>audio/mp3";
                        }
                    };
                }
            };
        }