Symbian
Symbian OS Library

SYMBIAN OS V9.3

[Index] [Spacer] [Previous] [Next]



MmfExFormatPlugin: Multimedia Framework format encoder/decoder plug-ins example

Found in: Examples\MultiMedia\MmfExFormatPlugin\

These are the main files contained in the examples. Some extra files may be needed to run the examples, and these will be found in the appropriate examples directory.

// Copyright (c) 2002-2005 Symbian Software Ltd.  All rights reserved.

// MMF framework headers
#include <mmfFile.h>
#include <ecom.h>
#include <mmfFormatImplementationUIDs.hrh>
#include <mmffourcc.h>

#include "mmfrawformat.h"
#include "UIDs.hrh"

const TUint KFormatDefaultFrameSize(0x1000); //Set default frame size to 4 K
const TUint KDefineIOBufferSize(0x0200); //easy to read clip buffer size
const TUint KOneSecondInMicroSeconds(1000000); //1 Second
const TUint KMono(1);
const TUint KStereo(2);
//this defines the valid sample rates for RAW
const TUint KRawSampleRates[] = { 8000, 11025, 22050, 44100 };


//
// CMMFRawFormatRead
//

// Factory function
CMMFFormatDecode* CMMFRawFormatRead::NewL(MDataSource* aSource)
    {
    if ((aSource->DataSourceType()==KUidMmfDescriptorSource)||
            (aSource->DataSourceType()==KUidMmfFileSource))
        {//currently only files and descriptor MDataSources are supported
        CMMFRawFormatRead* self = new(ELeave)CMMFRawFormatRead;
        CleanupStack::PushL(self);
        self->ConstructL(aSource);
        CleanupStack::Pop();
        return self;
        }
    else return NULL;
    }

// Destructor
CMMFRawFormatRead::~CMMFRawFormatRead()
    {
    delete iBuffer;
    }
    
// Second-phase constructor
void CMMFRawFormatRead::ConstructL(MDataSource* aSource)
    {
    // tell clip we're using it
    iClip = aSource;
    User::LeaveIfError(iClip->SourceThreadLogon(*this));
    iClip->SourcePrimeL();
    iFrameSize = KFormatDefaultFrameSize;
    iClipLength = (static_cast<CMMFClip*>(iClip))->Size();
    }

// Implementing MDataSource

// Handle request to fill buffer with data from clip
void CMMFRawFormatRead::FillBufferL(CMMFBuffer* aBuffer, MDataSink* aConsumer, TMediaId aMediaId )
    {   
    // check media id & pass onto the clip
    if (aMediaId.iMediaType != KUidMediaTypeAudio) User::Leave(KErrNotSupported); 
    iDataPath = aConsumer;
    //assumes first frame is frame 1
    TUint position = ((aBuffer->FrameNumber()-1)*iFrameSize)+iStartPosition;
    (static_cast<CMMFClip*>(iClip))->ReadBufferL(aBuffer, position, this);
    // notified of when buffer is full by BufferFilledL
    }

// creates the buffer for the source
// This overload supplies the sink buffer, as optimal source buffer size creation may depend on this
CMMFBuffer* CMMFRawFormatRead::CreateSourceBufferL(TMediaId aMediaId, CMMFBuffer& aSinkBuffer, TBool &aReference)
    {
    if (aMediaId.iMediaType == KUidMediaTypeAudio) 
        {
        NegotiateSourceBufferL(aSinkBuffer); //sets frame size to match sink buffer
        return CreateSourceBufferL(aMediaId, aReference);
        }
    else User::Leave(KErrNotSupported);
    return NULL;
    }

// creates the buffer for the source
CMMFBuffer* CMMFRawFormatRead::CreateSourceBufferL(TMediaId aMediaId, TBool &aReference)
    {
    if (aMediaId.iMediaType == KUidMediaTypeAudio) 
        {
        // assume default frame size if haven't determined a better one
        if (!iFrameSize) iFrameSize = KFormatDefaultFrameSize;
        // sets aReference to false if a new CMMFBuffer is returned
        aReference = EFalse;
        return CreateSourceBufferOfSizeL(iFrameSize);
        }
    else User::Leave(KErrNotSupported);
    return NULL;
    }

// Helper function to create and zero fill a buffer of specified size
CMMFDataBuffer* CMMFRawFormatRead::CreateSourceBufferOfSizeL(TUint aSize)
    {
    //needs to create source buffer
    CMMFDataBuffer* buffer = CMMFDataBuffer::NewL(aSize);
    buffer->Data().FillZ(aSize);
    return buffer;
    }

// Helper function to determine best source buffer size
void CMMFRawFormatRead::NegotiateSourceBufferL(CMMFBuffer& aSinkBuffer)
    {
    // if sink buffer has a fixed size use this to determine source buffer size
    if (aSinkBuffer.Type() == KUidMmfDataBuffer)
        {
        // RAW is linear data, so can set target buffer to sink buffer size
        TUint sinkBufferLength = (static_cast<CMMFDataBuffer&>(aSinkBuffer)).Data().MaxLength();
        if (sinkBufferLength == 0) sinkBufferLength = KFormatDefaultFrameSize;
        iFrameSize = sinkBufferLength; 
        CalculateFrameTimeInterval();
        }
    else 
        User::Leave(KErrNotSupported);
    }

// returns the codec FourCC code for the mediaId
TFourCC CMMFRawFormatRead::SourceDataTypeCode(TMediaId aMediaId)
    {
    if (aMediaId.iMediaType == KUidMediaTypeAudio) return iFourCC;
    else return TFourCC(); //defaults to 'NULL' fourCC
    }

// sets the codec FourCC code for the mediaId
TInt CMMFRawFormatRead::SetSourceDataTypeCode(TFourCC aSinkFourCC, TMediaId aMediaId)
    {
    if (aMediaId.iMediaType != KUidMediaTypeAudio) return KErrNotSupported;
    else iFourCC = aSinkFourCC;
    
    if ((iFourCC == KMMFFourCCCodePCM16) || 
        (iFourCC == KMMFFourCCCodePCM16B) || 
        (iFourCC == KMMFFourCCCodePCMU16)) 
            iBitsPerSample = 16;
    else if ((iFourCC == KMMFFourCCCodeIMAD) || 
        (iFourCC == KMMFFourCCCodeIMAS)) 
            iBitsPerSample = 4;
    else 
            iBitsPerSample = 8; //default to 8
    return KErrNone;
    }

// Initiate use of the interface 
TInt CMMFRawFormatRead::SourceThreadLogon(MAsyncEventHandler& aEventHandler)
    {//pass through to source clip
    return(iClip->SourceThreadLogon(aEventHandler));
    }

// Prepare clip
void CMMFRawFormatRead::SourcePrimeL()
    {
    iClip->SourcePrimeL();
    CalculateFrameTimeInterval();
    }

// Play clip
void CMMFRawFormatRead::SourcePlayL()
    {
    iClip->SourcePlayL();
    }

// Pause clip
void CMMFRawFormatRead::SourcePauseL()
    {
    iClip->SourcePauseL(); //propagate state change down to clip
    }

// Stop clip
void CMMFRawFormatRead::SourceStopL()
    {
    iClip->SourceStopL();
    }

// End use of the interface
void CMMFRawFormatRead::SourceThreadLogoff()
    {
    iClip->SourceThreadLogoff();
    }

// from MDataSink

// called by MDataSource to pass back full buffer to the sink
void CMMFRawFormatRead::BufferFilledL(CMMFBuffer* aBuffer)
    {
    //set position
    TTimeIntervalMicroSeconds position = //assumes frame numbers begin at frame 1
        TTimeIntervalMicroSeconds(TInt64(aBuffer->FrameNumber()-1)*iFrameTimeInterval.Int64());
    aBuffer->SetTimeToPlay(position);
    iDataPath->BufferFilledL(aBuffer);   
    }


// from CMMFFormatDecode

// returns number of streams
TUint CMMFRawFormatRead::Streams(TUid aMediaType) const
    {
    //need to check aMediaType for audio
    if (aMediaType == KUidMediaTypeAudio) return 1; //raw files can only have 1 audio stream
    else return 0;
    }

// returns the time interval for one frame
TTimeIntervalMicroSeconds CMMFRawFormatRead::FrameTimeInterval(TMediaId aMediaId) const
    {
    if (aMediaId.iMediaType == KUidMediaTypeAudio) return iFrameTimeInterval;
    else return TTimeIntervalMicroSeconds(0);
    }

// returns the duration of the source clip
TTimeIntervalMicroSeconds CMMFRawFormatRead::Duration(TMediaId aMediaId) const
    {
    if ((aMediaId.iMediaType == KUidMediaTypeAudio) && 
        (iClipLength) && (iSampleRate) && (iBitsPerSample) && (iChannels))
        {//we have enough values to calculate the duration
        TInt64 clipLength(iClipLength);
        clipLength*=KOneSecondInMicroSeconds;
        TTimeIntervalMicroSeconds duration = TTimeIntervalMicroSeconds(clipLength/iSampleRate);
        duration = TTimeIntervalMicroSeconds(duration.Int64()/(iBitsPerSample*iChannels));
        duration = TTimeIntervalMicroSeconds(duration.Int64()*8);
        return duration;
        }
    else return TTimeIntervalMicroSeconds(0);
    }

// helper function: calculates time between frames
void CMMFRawFormatRead::CalculateFrameTimeInterval()
    {
    if ((iFrameSize) && (iSampleRate) && (iBitsPerSample) && (iChannels))
        {
        iFrameTimeInterval = TTimeIntervalMicroSeconds((iFrameSize*KOneSecondInMicroSeconds)/iSampleRate);
        iFrameTimeInterval = 
            TTimeIntervalMicroSeconds(iFrameTimeInterval.Int64()/(iBitsPerSample*iChannels));
        iFrameTimeInterval = TTimeIntervalMicroSeconds(iFrameTimeInterval.Int64()*8);
        }
    }

// called by sink to suggest a source buffer size
void CMMFRawFormatRead::SuggestSourceBufferSize(TUint aSuggestedBufferSize)
    {
    iFrameSize = aSuggestedBufferSize; //set source format frame size to buffer size suggested by sink
    CalculateFrameTimeInterval();
    }

// set the number of channels
TInt CMMFRawFormatRead::SetNumChannels(TUint aChannels)
    {
    TInt error = KErrNone;
    if ((aChannels ==  KMono)||(aChannels == KStereo)) iChannels = aChannels;
    else error = KErrNotSupported; //only alow one or two channels
    return error;
    }

// set the sample rate
TInt CMMFRawFormatRead::SetSampleRate(TUint aSampleRate)
    {
    TInt status = KErrNotSupported;
    //we'll iterate through the valid sample table
    TInt i = sizeof(KRawSampleRates) / sizeof(TUint);
        
    while ((i--) && (status != KErrNone))
        {
        if (aSampleRate == KRawSampleRates[i])
            {
            iSampleRate = aSampleRate;
            status = KErrNone;
            }
        }
    return status;
    }

// helper function to read from clip
void CMMFRawFormatRead::DoReadL(TInt aReadPosition)
    {
    STATIC_CAST(CMMFClip*,iClip)->ReadBufferL(iBuffer,aReadPosition);
    }

// get the supported sample rates
void CMMFRawFormatRead::GetSupportedSampleRatesL(RArray<TUint>& aSampleRates)
    {
    aSampleRates.Reset();

    // Iterate through the valid sample table and append each value to aSampleRates
    TInt i = sizeof(KRawSampleRates) / sizeof(TUint);
    
    while (i--)
        {
        User::LeaveIfError(aSampleRates.Append(KRawSampleRates[i]));
        }
    }

// get the supported channel number options
void CMMFRawFormatRead::GetSupportedNumChannelsL(RArray<TUint>& aNumChannels)
    {
    aNumChannels.Reset();
    User::LeaveIfError(aNumChannels.Append(KMono));
    User::LeaveIfError(aNumChannels.Append(KStereo));
    }

// get the supported codecs
void CMMFRawFormatRead::GetSupportedDataTypesL(TMediaId aMediaId, RArray<TFourCC>& aDataTypes)
    {
    if (aMediaId.iMediaType != KUidMediaTypeAudio)
        User::Leave(KErrNotSupported);
    aDataTypes.Reset();
    User::LeaveIfError(aDataTypes.Append(KMMFFourCCCodePCM16));
    User::LeaveIfError(aDataTypes.Append(KMMFFourCCCodePCM16B));
    User::LeaveIfError(aDataTypes.Append(KMMFFourCCCodePCMU16));
    User::LeaveIfError(aDataTypes.Append(KMMFFourCCCodeIMAD));
    User::LeaveIfError(aDataTypes.Append(KMMFFourCCCodeIMAS));
    }


//
// CMMFRawFormatWrite
//

// Factory function
CMMFFormatEncode* CMMFRawFormatWrite::NewL(MDataSink* aSink)
    {
    if ((aSink->DataSinkType()==KUidMmfDescriptorSink)||
            (aSink->DataSinkType()==KUidMmfFileSink))
        {//currently only files and descriptor MDataSources are supported
        CMMFRawFormatWrite* self = new(ELeave)CMMFRawFormatWrite;
        CleanupStack::PushL(self);
        self->ConstructL(aSink);
        CleanupStack::Pop();
        return STATIC_CAST(CMMFFormatEncode*, self);
        }
    else return NULL;
    }

// destructor
CMMFRawFormatWrite::~CMMFRawFormatWrite()
    {
    delete iBuffer;
    delete iConvertBuffer;
    delete iChannelAndSampleRateConverterFactory;
    }
    
// second-phase construction  
void CMMFRawFormatWrite::ConstructL(MDataSink* aSink)
    {
    iClip = aSink;
    //first need to check if sink clip already exists to get settings.
    User::LeaveIfError(iClip->SinkThreadLogon(*this));
    iClip->SinkPrimeL();
    iBuffer = CreateSinkBufferOfSizeL(KDefineIOBufferSize); //512 easiest file size to read
    DoReadL(0);//read from beginning of clip
    if (iBuffer->Data().Size()> 0)
        {
        iClipAlreadyExists = ETrue;
        //There is no header, so data size is the same as the clip size in this case.
        iDataSize = iClipLength = STATIC_CAST(CMMFClip*,iClip)->Size();
        }
    iFrameSize = KFormatDefaultFrameSize;
    }

// from MDataSink

// sink thread attaches
TInt CMMFRawFormatWrite::SinkThreadLogon(MAsyncEventHandler& aEventHandler)
    {//pass through to sink clip
    return(iClip->SinkThreadLogon(aEventHandler));
    }

// helper function: calculates time between frames
void CMMFRawFormatWrite::CalculateFrameTimeInterval()
    {
    if ((iFrameSize) && (iSampleRate) && (iBitsPerSample) && (iChannels))
        {
        iFrameTimeInterval = TTimeIntervalMicroSeconds((iFrameSize*KOneSecondInMicroSeconds)/iSampleRate);
        iFrameTimeInterval = 
            TTimeIntervalMicroSeconds((iFrameTimeInterval.Int64())/(iBitsPerSample*iChannels));
        iFrameTimeInterval = TTimeIntervalMicroSeconds(iFrameTimeInterval.Int64()*8);
        }
    }

// called if sink setup depends on source
void CMMFRawFormatWrite::NegotiateL(MDataSource& aSource)
    {
    if (aSource.DataSourceType() == KUidMmfAudioInput)
        {
        // could  query the audio caps from DevSound for the settings below
        iSourceSampleRate = 8000; // assume 8KHz for now
        iSourceChannels = 1; //assume mono
        iSourceFourCC.Set(KMMFFourCCCodePCM16); //16 bit PCM
        }
    else if (aSource.DataSourceType() == KUidMmfFormatDecode)
        {//source is a clip so for now set sink settings to match source
        iSourceSampleRate = ((CMMFFormatDecode&)aSource).SampleRate();
        iSourceChannels = ((CMMFFormatDecode&)aSource).NumChannels();
        iSourceFourCC.Set(aSource.SourceDataTypeCode(TMediaId(KUidMediaTypeAudio)));
        iSourceWillSampleConvert = STATIC_CAST(CMMFFormatDecode&, aSource).SourceSampleConvert();
        ((CMMFFormatDecode&)aSource).SuggestSourceBufferSize(iFrameSize); //for now suggest format src takes same buf size as sink??
        //make the start position the end of the clip
        }
    else return;
    //set default sink parameters to be the same as the source
    if (iClipAlreadyExists) iStartPosition = iClipLength;
    if (!iSampleRate) iSampleRate = iSourceSampleRate; //might have already been set by custom command
    if (!iChannels) iChannels = iSourceChannels;
    if (!iBitsPerSample)
        {
        iFourCC.Set(iSourceFourCC);
        if ((iFourCC == KMMFFourCCCodePCM16) ||
            (iFourCC == KMMFFourCCCodePCM16B) ||
            (iFourCC == KMMFFourCCCodePCMU16))
                iBitsPerSample = 16;      
        else if ((iFourCC == KMMFFourCCCodeIMAD) || 
            (iFourCC == KMMFFourCCCodeIMAS))
                iBitsPerSample = 4;   
        else 
                iBitsPerSample = 8; //default to 8
        }
    CalculateFrameTimeInterval();
    }

// Prime the sink to be accessed
void CMMFRawFormatWrite::SinkPrimeL()
    {
    iClip->SinkPrimeL(); //propagate state change down to clip
    CalculateFrameTimeInterval();
    }

// Play the sink
void CMMFRawFormatWrite::SinkPlayL()
    {
    iClip->SinkPlayL(); //propagate state change down to clip
    if ((iChannels != iSourceChannels) || (iSampleRate != iSourceSampleRate) && (!iSourceWillSampleConvert))
        {//the source channels & sample rate don't match the formats - therefore need to do a conversion 
        //currently only pcm16 is supported so return with an error if format not pcm16
        if (iFourCC != KMMFFourCCCodePCM16) User::Leave(KErrNotSupported);
        iChannelAndSampleRateConverterFactory 
            = new(ELeave)CMMFChannelAndSampleRateConverterFactory;
        iChannelAndSampleRateConverter = 
            iChannelAndSampleRateConverterFactory->CreateConverterL( iSourceSampleRate, iSourceChannels, 
                                                                    iSampleRate, iChannels);
        //need to create an intermediate buffer in which to place the converted data
        TUint convertedBufferFrameSize = (iFrameSize*iChannels)/iSourceChannels;
        iConvertBuffer = CreateSinkBufferOfSizeL(convertedBufferFrameSize);
        }
    iFileHasChanged = ETrue; //file will change if we start playing to it
    }

// Pause the sink
void CMMFRawFormatWrite::SinkPauseL()
    {
    iClip->SinkPauseL(); //propagate state change down to clip
    }

// Stop the sink
void CMMFRawFormatWrite::SinkStopL()
    {
    iClip->SinkStopL(); //propagate state change down to clip
    }

// Detach from the sink
void CMMFRawFormatWrite::SinkThreadLogoff()
    {
    iClip->SinkThreadLogoff(); //propagate down to clip
    }

// Called by the CMMFDataPath to add a buffer to a clip
void CMMFRawFormatWrite::EmptyBufferL(CMMFBuffer* aBuffer, MDataSource* aSupplier, TMediaId aMediaId)
    {
    //since raw always contains linear audio the sink buffer size can set the source buffer size

    //check media id &pass onto clip
    if (aMediaId.iMediaType!=KUidMediaTypeAudio) User::Leave(KErrNotSupported); 
    iDataPath = aSupplier;

    // Check we haven't exceeded any set maximum on our clip length
    if (iMaximumClipSize > 0)
        {
        // Find the current clip size
        TInt currentClipLength = STATIC_CAST(CMMFClip*, iClip)->Size();
        TInt bufferSize = aBuffer->BufferSize();
        if ((currentClipLength + bufferSize) >= iMaximumClipSize)
            User::Leave(KErrEof);
        }

    //assumes first frame is frame 1
    iBufferToEmpty = aBuffer; //save this so it can be returned to datapath
    TInt position = ((aBuffer->FrameNumber()-1)*iFrameSize)+iStartPosition;
    if (position < (TInt)iStartPosition) position = iStartPosition; //can't write before start of header
    if ((iChannelAndSampleRateConverter) && (!iSourceWillSampleConvert))
        {//need to perform channel & sample rate conversion before writing to clip
        iFrameSize = iChannelAndSampleRateConverter->Convert(*(CMMFDataBuffer*)aBuffer,*iConvertBuffer);
        STATIC_CAST(CMMFClip*,iClip)->WriteBufferL(iConvertBuffer, position, this);
        }
    else
        {//no need to convert the data
        STATIC_CAST(CMMFClip*,iClip)->WriteBufferL(aBuffer, position, this);
        }
    iPos = position; //save current write position
    }

// helper function to create buffer of specficed size
CMMFDataBuffer* CMMFRawFormatWrite::CreateSinkBufferOfSizeL(TUint aSize)
    {
    //needs to create source buffer
    CMMFDataBuffer* buffer = CMMFDataBuffer::NewL(aSize);
    buffer->Data().FillZ(aSize);
    iBufferCreated = ETrue;
    return buffer;
    }

// create buffer to receive data
CMMFBuffer* CMMFRawFormatWrite::CreateSinkBufferL(TMediaId aMediaId, TBool &aReference)
    {
    if (aMediaId.iMediaType == KUidMediaTypeAudio) 
        {
        if (!iFrameSize) iFrameSize = KFormatDefaultFrameSize;
        aReference = EFalse;
        return CreateSinkBufferOfSizeL(iFrameSize);
        }
    else User::Leave(KErrNotSupported);
    return NULL;
    }

// gets the codec type
TFourCC CMMFRawFormatWrite::SinkDataTypeCode(TMediaId aMediaId)
    {
    if (aMediaId.iMediaType == KUidMediaTypeAudio) return iFourCC;
    else return TFourCC(); //defaults to 'NULL' fourCC
    }

// sets the codec type
TInt CMMFRawFormatWrite::SetSinkDataTypeCode(TFourCC aSinkFourCC, TMediaId aMediaId)
    {
    if (aMediaId.iMediaType != KUidMediaTypeAudio) return KErrNotSupported;
    else iFourCC = aSinkFourCC;
    
    if ((iFourCC == KMMFFourCCCodePCM16) || 
        (iFourCC == KMMFFourCCCodePCM16B) ||
        (iFourCC == KMMFFourCCCodePCMU16)) 
            iBitsPerSample = 16;
    else if ((iFourCC == KMMFFourCCCodeIMAD) ||
        (iFourCC == KMMFFourCCCodeIMAS)) 
            iBitsPerSample = 4;
    else 
            iBitsPerSample = 8; //default to 8

    return KErrNone;
    }

// helper function to read data from clip
void CMMFRawFormatWrite::DoReadL(TInt aReadPosition)
    {
    STATIC_CAST(CMMFClip*,iClip)->ReadBufferL(iBuffer,aReadPosition);
    }

// helper function to write data to clip
void CMMFRawFormatWrite::DoWriteL(TInt aWritePosition)
    {
    STATIC_CAST(CMMFClip*,iClip)->WriteBufferL(iBuffer,aWritePosition);
    }


// from MDataSource

// called by MDataSink to pass back emptied buffer to the source
void CMMFRawFormatWrite::BufferEmptiedL(CMMFBuffer* aBuffer)
    {
    iDataSize+=aBuffer->BufferSize(); //total bytes written
    iPos += aBuffer->BufferSize(); //total bytes written so far - iPos is not always = iDataSize due to repositions
    if (iMaxPos < iPos) iMaxPos = iPos; //need iMaxPos incase we write data then repos to an earlier pos in the clip
    if (iBufferToEmpty != aBuffer) iDataPath->BufferEmptiedL(iBufferToEmpty); //need to return same buffer
    else iDataPath->BufferEmptiedL(aBuffer);
    }


// from CMMFFormatEncode

// set the number of channels 
TInt CMMFRawFormatWrite::SetNumChannels(TUint aChannels)
    {
    TInt error = KErrNone;
    if ((aChannels ==  KMono)||(aChannels == KStereo)) iChannels = aChannels;
    else error = KErrNotSupported; //only alow one or two channels
    return error;
    }

// set the sample rate
TInt CMMFRawFormatWrite::SetSampleRate(TUint aSampleRate)
    {
    TInt status = KErrNotSupported;
    //we'll iterate through the valid sample table
    TInt i = sizeof(KRawSampleRates) / sizeof(TUint);
        
    while ((i--) && (status != KErrNone))
        {
        if (aSampleRate == KRawSampleRates[i])
            {
            iSampleRate = aSampleRate;
            status = KErrNone;
            }
        }
    return status;
    }

// get the frame interval
TTimeIntervalMicroSeconds CMMFRawFormatWrite::FrameTimeInterval(TMediaId aMediaId) const
    {
    if (aMediaId.iMediaType == KUidMediaTypeAudio) return iFrameTimeInterval;
    else return TTimeIntervalMicroSeconds(0);
    }

// returns the duration of the source clip
TTimeIntervalMicroSeconds CMMFRawFormatWrite::Duration(TMediaId aMediaId) const
    {
    if ((aMediaId.iMediaType == KUidMediaTypeAudio) 
        && (iDataSize) && (iSampleRate) && (iBitsPerSample) && (iChannels))
        {
        TInt64 clipLength(iDataSize);
        clipLength*=KOneSecondInMicroSeconds;
        TTimeIntervalMicroSeconds duration = TTimeIntervalMicroSeconds(clipLength/iSampleRate);
        duration = 
            TTimeIntervalMicroSeconds(duration.Int64()/(iBitsPerSample*iChannels));
        duration = TTimeIntervalMicroSeconds(duration.Int64()*8);
        return duration;
        }
    else return TTimeIntervalMicroSeconds(0);
    }

// Calculate and return the number of bytes used for on second of audio.
TInt64 CMMFRawFormatWrite::BytesPerSecond() 
    {
    TInt64 bitsPerSecond = iSampleRate * iBitsPerSample * iChannels ;
    TInt64 bytesPerSecond = bitsPerSecond/8;
    return bytesPerSecond ;
    }

// Shortens the clip from the position specified to the end specified.
void CMMFRawFormatWrite::CropL(TTimeIntervalMicroSeconds aPosition, TBool aToEnd )
    {
    // Does clip have any size to crop
    if (!(STATIC_CAST(CMMFClip*,iClip)->Size())) User::Leave(KErrNotFound); //no clip to crop or clip is 0 bytes.


    // Is aPosition between the start and the end?
    if ( ( aPosition < TTimeIntervalMicroSeconds(0) ) || ( aPosition >= Duration( KUidMediaTypeAudio) ) ) 
        User::Leave( KErrArgument ) ;

    // Convert aPostion to cropPosition in bytes

    TInt64 cropPosition64 = 
        TInt64( ( aPosition.Int64() * iSampleRate * (iBitsPerSample/8) * iChannels ) /KOneSecondInMicroSeconds);
    TUint cropPosition = I64INT(cropPosition64);

    // Does cropPosition need adjustment to retain integrity?  (assume not)

    TUint dataSize ;  // This will be the size of the data left after cropping.

    if ( !aToEnd )
        {
        // Shift the data physically
        // move the data in blocks
        // Create a CMMFDataBuffer and use CMMFClip to shift the data
        dataSize = iMaxPos - cropPosition ;
        if (( dataSize > 0 ) && (aPosition != TTimeIntervalMicroSeconds(0)))
            {
            TUint bufSize = ( dataSize < KDefineIOBufferSize ? dataSize : KDefineIOBufferSize ) ; //max bufSize 512
            CMMFDataBuffer* buffer = CMMFDataBuffer::NewL(bufSize) ;
            CleanupStack::PushL( buffer ) ;

            TUint rPos = cropPosition ; // read position
            TUint wPos = 0;
            TInt dataToShift = ETrue ;
            while ( dataToShift )
                {
                STATIC_CAST( CMMFClip*, iClip )->ReadBufferL( buffer, rPos ) ;  // synchronous calls
                STATIC_CAST( CMMFClip*, iClip )->WriteBufferL( buffer, wPos ) ;
                if ( rPos > iMaxPos ) 
                    dataToShift = EFalse ;  // past the end:  Done
                else
                    { // shift the pointers
                    rPos += bufSize ;
                    wPos += bufSize ;
                    }
                }// while data to shift
            CleanupStack::PopAndDestroy( ) ; // buffer
            }// if data to shift
        }// crop to start
    else // crop to end
        dataSize = cropPosition ;

    iDataSize = dataSize ;
    iMaxPos = dataSize ;

    // Do the physical chop
    if ( iClip->DataSinkType() == KUidMmfFileSink )
        {
        STATIC_CAST( CMMFFile*, iClip )->FileL().SetSize( iMaxPos ) ;
        iClipLength = iMaxPos; 
        }
    }

// get the supported sample rates
void CMMFRawFormatWrite::GetSupportedSampleRatesL(RArray<TUint>& aSampleRates)
    {
    aSampleRates.Reset();

    // Iterate through the valid sample table and append each value to aSampleRates
    TInt i = sizeof(KRawSampleRates) / sizeof(TUint);
    
    while (i--)
        {
        User::LeaveIfError(aSampleRates.Append(KRawSampleRates[i]));
        }
    }

// get the supported channel number options
void CMMFRawFormatWrite::GetSupportedNumChannelsL(RArray<TUint>& aNumChannels)
    {
    aNumChannels.Reset();
    User::LeaveIfError(aNumChannels.Append(KMono));
    User::LeaveIfError(aNumChannels.Append(KStereo));
    }

// set maximum clip size
void CMMFRawFormatWrite::SetMaximumClipSizeL(TInt aBytes)
    {
    iMaximumClipSize = aBytes;
    }

// get the supported codecs
void CMMFRawFormatWrite::GetSupportedDataTypesL(TMediaId aMediaId, RArray<TFourCC>& aDataTypes)
    {
    if (aMediaId.iMediaType != KUidMediaTypeAudio)
        User::Leave(KErrNotSupported);
    aDataTypes.Reset();
    User::LeaveIfError(aDataTypes.Append(KMMFFourCCCodePCM16));
    User::LeaveIfError(aDataTypes.Append(KMMFFourCCCodePCM16B));
    User::LeaveIfError(aDataTypes.Append(KMMFFourCCCodePCMU16));
    User::LeaveIfError(aDataTypes.Append(KMMFFourCCCodeIMAD));
    User::LeaveIfError(aDataTypes.Append(KMMFFourCCCodeIMAS));
    }


// __________________________________________________________________________
// Exported proxy for instantiation method resolution
// Define the interface UIDs

const TImplementationProxy ImplementationTable[] = 
    {
        IMPLEMENTATION_PROXY_ENTRY(KRawDecoder, CMMFRawFormatRead::NewL),
        IMPLEMENTATION_PROXY_ENTRY(KRawEncoder, CMMFRawFormatWrite::NewL)
    };

EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
    {
    aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);

    return ImplementationTable;
    }

#ifndef __MMF_RAW_FORMAT_H__
#define __MMF_RAW_FORMAT_H__

// MMF framework headers
#include <mmfformat.h>
#include <mmfdatabuffer.h>
#include <mmfclip.h>
#include <ImplementationProxy.h>
#include <mmfutilities.h>

/* Audio decoder plug-in to read .RAW audio.
 It implements MDataSource to pass data to the controller, and MDataSink to read data from 
 the source clip.
 Also implements MAsyncEventHandler to send an event to the client */
class CMMFRawFormatRead : public CMMFFormatDecode, public MAsyncEventHandler
    {
public:
    // Factory function
    static CMMFFormatDecode* NewL(MDataSource* aSource);
    ~CMMFRawFormatRead();

    //from MDataSource
    void FillBufferL(CMMFBuffer* aBuffer, MDataSink* aConsumer, TMediaId aMediaId);
    CMMFBuffer* CreateSourceBufferL(TMediaId aMediaId, TBool &aReference); 
    CMMFBuffer* CreateSourceBufferL(TMediaId aMediaId, CMMFBuffer& aSinkBuffer, TBool &aReference);
    TFourCC SourceDataTypeCode(TMediaId aMediaId);
    TInt SetSourceDataTypeCode(TFourCC aSourceFourCC, TMediaId aMediaId); 
    TInt SourceThreadLogon(MAsyncEventHandler& aEventHandler);
    void SourceThreadLogoff();
    void SourcePrimeL();
    void SourcePlayL();
    void SourcePauseL();
    void SourceStopL();

    //from MDataSink
    void BufferFilledL(CMMFBuffer* aBuffer);

    // from CMMFFormatDecode
    TUint Streams(TUid aMediaType) const;
    TTimeIntervalMicroSeconds FrameTimeInterval(TMediaId aMediaType) const;
    TTimeIntervalMicroSeconds Duration(TMediaId aMediaType) const;
    TUint NumChannels() {return iChannels;};
    TUint SampleRate() {return iSampleRate;};
    TUint BitRate() {return iSampleRate * iBitsPerSample;};
    TInt SetNumChannels(TUint aChannels);
    TInt SetSampleRate(TUint aSampleRate);
    void GetSupportedSampleRatesL(RArray<TUint>& aSampleRates);
    void GetSupportedNumChannelsL(RArray<TUint>& aNumChannels);
    void GetSupportedDataTypesL(TMediaId aMediaId, RArray<TFourCC>& aDataTypes);
    void SuggestSourceBufferSize(TUint aSuggestedBufferSize); 

    //from MAsyncEventHandler
    TInt SendEventToClient(const TMMFEvent& /*aEvent*/) {return KErrNone;}

private:
    // Construction
    void ConstructL(MDataSource* aSource);
    // Helper functions
    CMMFDataBuffer* CreateSourceBufferOfSizeL(TUint aSize);
    void DoReadL(TInt aReadPosition);
    void NegotiateSourceBufferL(CMMFBuffer& aBuffer);
    void CalculateFrameTimeInterval();

protected:
    MDataSource* iClip; //for decode format MDatasource; for encode format MDataSink
    MDataSink* iDataPath; //for decode format MDataSink; for encode format MDataSource
    TFourCC iFourCC;   

private:
    CMMFDataBuffer* iBuffer;
    TUint iStartPosition;
    TUint iPos;
    TUint iChannels;
    TUint iSampleRate;
    TUint iBitsPerSample;
    TTimeIntervalMicroSeconds iFrameTimeInterval;
    TUint iFrameSize;
    TUint iClipLength;
    };


/* Audio encoder plug-in to write .RAW audio.
 It implements MDataSink to get data from the controller, and MDataSource to write data to 
 the target clip.
 Also implements MAsyncEventHandler to send an event to the client */
class CMMFRawFormatWrite : public CMMFFormatEncode, public MAsyncEventHandler
    {
public:
    // Factory function
    static CMMFFormatEncode* NewL(MDataSink* aSink);
    ~CMMFRawFormatWrite();

    //from MDataSink
    CMMFBuffer* CreateSinkBufferL(TMediaId aMediaId, TBool &aReference); 
    TFourCC SinkDataTypeCode(TMediaId aMediaId); //returns FourCC code for the mediaId
    TInt SetSinkDataTypeCode(TFourCC aSinkFourCC, TMediaId aMediaId); 
    TInt SinkThreadLogon(MAsyncEventHandler& aEventHandler);
    void SinkThreadLogoff();
    void NegotiateL(MDataSource& aSource);
    void SinkPrimeL();
    void SinkPlayL();
    void SinkPauseL();
    void SinkStopL();
    void EmptyBufferL(CMMFBuffer* aBuffer, MDataSource* aSupplier, TMediaId aMediaId);

    //from MDataSource
    void BufferEmptiedL(CMMFBuffer* aBuffer);

    // from CMMFFormatEncode
    TTimeIntervalMicroSeconds FrameTimeInterval(TMediaId aMediaType) const;
    TTimeIntervalMicroSeconds Duration(TMediaId aMediaType) const;
    TInt SetNumChannels(TUint aChannels);
    TInt SetSampleRate(TUint aSampleRate);
    TUint NumChannels() {return iChannels;};
    TUint SampleRate() {return iSampleRate;};
    TUint BitRate() {return iSampleRate * iBitsPerSample;};
    TInt64 BytesPerSecond()  ;
    void GetSupportedSampleRatesL(RArray<TUint>& aSampleRates);
    void GetSupportedNumChannelsL(RArray<TUint>& aNumChannels);
    void GetSupportedDataTypesL(TMediaId aMediaId, RArray<TFourCC>& aDataTypes);
    void SetMaximumClipSizeL(TInt aBytes);
    void CropL(TTimeIntervalMicroSeconds aPosition, TBool aToEnd ) ;

    //from MAsyncEventHandler
    TInt SendEventToClient(const TMMFEvent& /*aEvent*/) {return KErrNone;}

private:
    // construction
    void ConstructL(MDataSink* aSink);
    // helper functions
    CMMFDataBuffer* CreateSinkBufferOfSizeL(TUint aSize);
    void CalculateFrameTimeInterval();
    void DoReadL(TInt aReadPosition);
    void DoWriteL(TInt aWritePosition);

private:
    MDataSink* iClip; //for decode f

#ifndef _MmfRAWFormatUIDS_H_
#define _MmfRAWFormatUIDS_H_

#define KPluginDLLUID 0x101F81CF
#define KRawDecoder 0x101F81D0
#define KRawEncoder 0x101F81D1

#endif

PRJ_MMPFILES
MmfRAWFormat.mmp

// MmfExFormatPlugin.mmp
//
// Copyright (c) 2002-2005 Symbian Software Ltd.  All rights reserved.
//

TARGET MmfExFormatPlugin.dll
TARGETTYPE PLUGIN

// ECom Dll recognition UID followed by the unique UID for this dll
UID 0x10009D8D 0x101F81CF
VENDORID 0x70000001
CAPABILITY ALL -TCB

SOURCEPATH  .
SOURCE      MmfRawFormat.cpp 

USERINCLUDE .
SYSTEMINCLUDE  \epoc32\include \epoc32\include\ecom 
SYSTEMINCLUDE  \epoc32\include\mmf\common \epoc32\include\mmf\server

START RESOURCE 101F81CF.RSS
TARGETPATH     \resource\Plugins
HEADER
END

LIBRARY euser.lib
LIBRARY mmfcontrollerframework.lib
LIBRARY mmfserverbaseclasses.lib
LIBRARY efsrv.lib
LIBRARY ecom.lib

// 101F81CF.RSS
//
// Copyright (c) 2002 Symbian Ltd.  All rights reserved.
//

#include "RegistryInfo.rh"
#include <mmfPluginInterfaceUIDs.hrh>
#include "UIDs.hrh"

RESOURCE REGISTRY_INFO theInfo
    {
    dll_uid = KPluginDLLUID;
    interfaces = 
        {
        INTERFACE_INFO
            {
            interface_uid = KMmfUidPluginInterfaceFormatDecode;   
            implementations = 
                {
                IMPLEMENTATION_INFO
                    {
                    implementation_uid = KRawDecoder;  // CMMFRawFormatRead
                    version_no = 1;
                    display_name = "MMF read format example";
                    default_data = "0x101f5022";//Preferred Controller
                    opaque_data = "<s>Symbian<i>0x101f5d07<e>.raw";
                    }
                };
            },
        INTERFACE_INFO
            {
            interface_uid = KMmfUidPluginInterfaceFormatEncode;    
            implementations = 
                {
                IMPLEMENTATION_INFO
                    {
                    implementation_uid = KRawEncoder;  // CMMFRawFormatWrite
                    version_no = 1;
                    display_name = "MMF writer format example";
                    default_data = "0x101f5022";//Preferred Controller
                    opaque_data = "<s>Symbian<i>0x101f5d07<e>.raw";
                    }
                };
            }
        };
    }


Description

MmfExFormatPlugin demonstrates how to implement format encoder and decoder plug-ins for the Multimedia Framework. Multimedia data is usually packaged in a particular format, which specify the properties of the data (e.g. duration, frame intervals), and how the buffers for holding the data are organised. A format decoder plug-in is responsible for reading a particular format; a format encoder plug-in for writing a particular format.

The example handles the RAW audio format (PCM data with no headers). The decoder plug-in CMMFRawFormatRead reads RAW audio; it implements the ECom interface CMMFFormatDecode. The encoder plug-in CMMFRawFormatWrite writes RAW audio: it implements the ECom interface CMMFFormatEncode.