videoeditorengine/audioeditorengine/src/ProcEncoder.cpp
author Mikael Laine <mikael.laine@ixonos.com>
Fri, 29 Jan 2010 14:08:33 +0200
changeset 0 951a5db380a0
permissions -rw-r--r--
Committing the Video Editor package under the Eclipse Public License

/*
* Copyright (c) 2010 Ixonos Plc.
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - Initial contribution
*
* Contributors:
* Ixonos Plc
*
* Description:  
*
*/



#include "ProcEncoder.h"
#include "audconstants.h"
#include    <MmfDatabuffer.h>
#include    <mmfcontrollerpluginresolver.h>
#include    <mmfutilities.h>
#include    <mmf/plugin/mmfCodecImplementationUIDs.hrh>
#include    <MMFCodec.h>



// MACROS

// Debug print macro
#if defined _DEBUG 
#include <e32svr.h>
#define PRINT(x) RDebug::Print x; 
#else
#define PRINT(x)
#endif

// -----------------------------------------------------------------------------
// TCMRAMREncParams
// Encoding parameters structure for SW AMR codec
// -----------------------------------------------------------------------------
//
class TVedACAMREncParams
    {
public:
    // encoding mode (for AMR-NB: 0=MR475,1=MR515,...,7=MR122, default 7)
    TInt iMode;
    // DTX flag (TRUE or default FALSE)
    TInt iDTX;
    };

CProcEncoder* CProcEncoder::NewL()
                            
    {
    CProcEncoder* self = NewLC();
    CleanupStack::Pop(self);
    return self;
    
    }


CProcEncoder* CProcEncoder::NewLC()
                                
    {

    CProcEncoder* self = new (ELeave) CProcEncoder();
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    
    }

CProcEncoder::~CProcEncoder()
    {
    

    delete iSourceInputBuffer;
    
    delete  iDestInputBuffer;
        
    delete iDestCodec;
    
    
    }
    
    
    
    
TBool CProcEncoder::InitL(TAudType aAudioType, TInt aTargetSamplingRate, TChannelMode aChannelMode,
                          TInt aBitrate)
    {
    PRINT((_L("CProcEncoder::InitL() in")));
    
    if (aAudioType == EAudAMR)
        {
        iOutputFrameDurationMilli = 20;
        }
    else if (aAudioType == EAudAAC_MPEG4)
        {
        iOutputFrameDurationMilli = (1024*1000)/(aTargetSamplingRate);
        }
    else
        {
        // only AMR & AAC encoding supported
        PRINT((_L("CProcEncoder::InitL() out, unsupported audio type")));
        User::Leave(KErrNotSupported);
        }
    
    iToBitRate = aBitrate;
    
    iAudioType = aAudioType;
    
    iNumberOfFramesInOutputBuffer = 0;

    iToSampleRate = aTargetSamplingRate;
       
    iToChannels = 1; 
    
    if (aChannelMode == EAudStereo)
        {
        iToChannels = 2;    
        }

    
    TInt destInputBufferSize = 0;
    TInt sourceInputBufferSize = 0;
   
    // input buffer size:
    // we never get longer than 64 milliseconds input
    // as it is limited by the input formats
    // for 16kHz AAC the frame duration is 64 ms
    //
    const TInt KMaxInputDurationMilli = 64;
   
    // multiplied by 2 as we have a bitdepth of 16
    sourceInputBufferSize = (2*aTargetSamplingRate*KMaxInputDurationMilli)/1000;

    if (aChannelMode == EAudStereo)
        {
        sourceInputBufferSize *= 2;
        }

    if ( aAudioType == EAudAMR )
        {
        
        // from 64 ms input we can have maximum 4 AMR frames
        destInputBufferSize = KAedMaxAMRFrameLength*4;
        
        
        }
    else if (aAudioType == EAudAAC_MPEG4)
        {
 
        if ( aChannelMode == EAudSingleChannel )
            {
            destInputBufferSize = KAedMaxAACFrameLengthPerChannel;
            }
        else
            {
            destInputBufferSize = 2 * KAedMaxAACFrameLengthPerChannel;
        
            }        
        }
    
    if ( iSourceInputBuffer )
        {
        delete iSourceInputBuffer;
        iSourceInputBuffer = NULL;
        }
    
    iSourceInputBuffer = CMMFDataBuffer::NewL(sourceInputBufferSize*5);
    
    
    if ( iDestInputBuffer )
        {
        delete iDestInputBuffer;
        iDestInputBuffer = NULL;
        }

    TInt errC = KErrNone;

    if (aAudioType == EAudAMR)
        {
        
        // shouldn't get more than 6 AMR frames at a time -> 120 ms
        iDestInputBuffer = CMMFDataBuffer::NewL( destInputBufferSize);
        
        }
    else
        {
        
        iDestInputBuffer = CMMFDataBuffer::NewL( destInputBufferSize);
        
        }
    
    TRAP (errC,SetDestCodecL());
    
    if (errC != KErrNone)
        {
        // initialization failed for some reason
        User::Leave(KErrNotSupported);
        
        }

    TInt err = KErrNone;

    if ( iAudioType == EAudAAC_MPEG4 )
        {
        TRAP( err, ConfigureAACEncoderL());
        
        }
    else if (iAudioType == EAudAMR)
        {
        TRAP( err, ConfigureAMREncoderL());
        }
    
    
    if (err != KErrNone || errC != KErrNone)
        {

        // initialization failed for some reason
        User::Leave(KErrNotSupported);
        }

    iReady = ETrue;
    PRINT((_L("CProcEncoder::InitL() out")));

    return ETrue;
    }

TBool CProcEncoder::FillEncBufferL(const TDesC8& aRawFrame, HBufC8* aEncBuffer, TInt& aOutputDurationMilli)
    {
    PRINT((_L("CProcEncoder::FillEncBufferL() in")));
    
    aOutputDurationMilli = 0;
    

    if (!iReady)
        {
        User::Leave(KErrNotReady);
        }
    
    iEncBuffer = aEncBuffer;
    
    if (iEncBuffer->Length() == 0)
        {
        iNumberOfFramesInOutputBuffer = 0;
        }
       
    if ( !aRawFrame.Length() )
        {
        return EFalse;
        }
    
    if ( (TInt)(aRawFrame.Length() + iSourceInputBuffer->Position() ) > iSourceInputBuffer->Data().MaxLength() )
        {
        ReAllocBufferL( iSourceInputBuffer, aRawFrame.Length() + iSourceInputBuffer->Position() );
        
        }
    
    
    // copy the input data to MMF buffer
    iSourceInputBuffer->Data().SetLength( 0 );
    iSourceInputBuffer->SetPosition( 0 );

    iSourceInputBuffer->Data().Append( aRawFrame );
    iSourceInputBuffer->Data().SetLength( iSourceInputBuffer->Data().Length() );
    iSourceInputBuffer->SetStatus(EFull);
    
    iDestInputBuffer->Data().SetLength(0);
    iDestInputBuffer->SetPosition(0); 
  
    FeedCodecL( iDestCodec, iSourceInputBuffer, iDestInputBuffer);

    
    
    iDestInputBuffer->Data().SetLength(0);
    iDestInputBuffer->SetPosition(0); 
    
    if (iEncBuffer->Size() > 0)
       {
       
       aOutputDurationMilli = iOutputFrameDurationMilli * iNumberOfFramesInOutputBuffer;
       PRINT((_L("CProcEncoder::FillEncBufferL() out with ETrue (complete)")));
       return ETrue;
       }
       
    PRINT((_L("CProcEncoder::FillEncBufferL() out with EFalse (incomplete)")));
    return EFalse;
    }

TAudType CProcEncoder::DestAudType()
    {
    return iAudioType;
    }

void CProcEncoder::ConstructL()
    {
    
    }

CProcEncoder::CProcEncoder()
    {
    
    }
    
    
void CProcEncoder::ConfigureAMREncoderL()
    {
    PRINT((_L("CProcEncoder::ConfigureAMREncoderL() in")));

    if ( (iToBitRate < KAedMinAMRBitRate) || (iToBitRate > KAedMaxAMRBitRate) )
        {
        User::Leave( KErrArgument );
        }

    TVedACAMREncParams* configData = new (ELeave) TVedACAMREncParams;
    CleanupStack::PushL( configData );

    // the bitrates in the switch & if below are not magic numbers but AMR bitrates in bits per seconds and mode indices from TAmrEncParams

    switch ( iToBitRate )
        {
        case 4750:
            configData->iMode = 0;
            configData->iDTX = EFalse;
            break;
        case 5150:
            configData->iMode = 1;
            configData->iDTX = EFalse;
            break;
        case 5900:
            configData->iMode = 2;
            configData->iDTX = EFalse;
            break;
        case 6700:
            configData->iMode = 3;
            configData->iDTX = EFalse;
            break;
        case 7400:
            configData->iMode = 4;
            configData->iDTX = EFalse;
            break;
        case 7950:
            configData->iMode = 5;
            configData->iDTX = EFalse;
            break;
        case 10200:
            configData->iMode = 6;
            configData->iDTX = EFalse;
            break;
        case 12200:
            configData->iMode = 7;
            configData->iDTX = EFalse;
            break;
        default :
            // Interprets now the bitrate proprietarily: bitrates that are not exactly AMR bitrates 
            // mean that voice activity detection is used and the AMR bitrates is the given bitrate rounded upwards to the next AMR bitrate
            if ( iToBitRate < 4750 )
                {
                configData->iMode = 0;
                configData->iDTX = ETrue;
                }
            else if ( iToBitRate < 5150 )
                {
                configData->iMode = 1;
                configData->iDTX = ETrue;
                }
            else if ( iToBitRate < 5900 )
                {
                configData->iMode = 2;
                configData->iDTX = ETrue;
                }
            else if ( iToBitRate < 6700 )
                {
                configData->iMode = 3;
                configData->iDTX = ETrue;
                }
            else if ( iToBitRate < 7400 )
                {
                configData->iMode = 4;
                configData->iDTX = ETrue;
                }
            else if ( iToBitRate < 7950 )
                {
                configData->iMode = 5;
                configData->iDTX = ETrue;
                }
            else if ( iToBitRate < 10200 )
                {
                configData->iMode = 6;
                configData->iDTX = ETrue;
                }
            else // must be: ( iToBitRate < 12200 ) since checked earlier
                {
                configData->iMode = 7;
                configData->iDTX = ETrue;
                }
        }

    TUid uid ={KUidMmfCodecAudioSettings}; // Use Uid reserved for codec configurations
    iDestCodec->ConfigureL( uid, reinterpret_cast<TDesC8&>(*configData));
    CleanupStack::PopAndDestroy( configData );
    PRINT((_L("CProcEncoder::ConfigureAMREncoderL() out")));
    }

// -----------------------------------------------------------------------------
// CProcEncoder::ConfigureAACEncoderL
// 
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CProcEncoder::ConfigureAACEncoderL()
    {
    PRINT((_L("CProcEncoder::ConfigureAACEncoderL() in")));
    
    TInt i = 0;
    TBool iSet = EFalse;
    for ( i = 0; i < KAedNumSupportedAACSampleRates; i++ )
        {
        if ( iToSampleRate == KAedSupportedAACSampleRates[i] )
            {
            iSet = ETrue;
            }
        }
    if ( !iSet )
        {
        // given samplerate is not supported
        User::Leave( KErrNotSupported );
        }

    // AAC codec interprets the input as array of TInts, not as a class
    RArray<TInt> config;
    config.Append (iToBitRate); //BitRate
    config.Append (iToSampleRate);  //SamplingRate
    config.Append (0);  //iToolFlags
    config.Append (iToChannels);    //iNumChan
    
    // NOTE Ali: for 48kHz stereo we might need to use ADTS as output format
    // as we can get more than one frame in synchronous call!
    config.Append (0);  //iuseFormat 0=RAW; 1=ADTS; 2=ADIF
    config.Append (0);  // 0 = 1 Frame only; 1 = Full Buffer

    TUid uid ={KUidMmfCodecAudioSettings}; // Use Uid reserved for codec configurations
    iDestCodec->ConfigureL( uid,  reinterpret_cast<TDesC8&>(config));
    config.Close();
    PRINT((_L("CProcEncoder::ConfigureAACEncoderL() out")));
    }

    
void CProcEncoder::ReAllocBufferL( CMMFDataBuffer* aBuffer, TInt aNewMaxSize )
    {
    if ( aBuffer->Data().Length() )
        {
        TInt position = aBuffer->Position();
        TInt length = aBuffer->Data().Length();
        HBufC8* oldData = aBuffer->Data().AllocL();
        CleanupStack::PushL( oldData );
        ((CMMFDescriptorBuffer*)aBuffer)->ReAllocBufferL( aNewMaxSize );
        aBuffer->Data().Copy( *oldData );
        CleanupStack::PopAndDestroy( oldData );
        aBuffer->Data().SetLength( length );
        aBuffer->SetPosition( position );
        }
    else
        {
        ((CMMFDescriptorBuffer*)aBuffer)->ReAllocBufferL( aNewMaxSize );
        }
    }
    
void CProcEncoder::FeedCodecL( CMMFCodec* aCodec, CMMFDataBuffer* aSourceBuffer, CMMFDataBuffer* aDestBuffer )
    {
    PRINT((_L("CProcEncoder::FeedCodecL() in")));
    TBool completed = EFalse;
    TCodecProcessResult result;
    TInt aSrcUsed = 0;

    while ( !completed )
        {

        // encode and check the result
        result = EncodeL(aCodec, aSourceBuffer, aDestBuffer);
             
        switch ( result.iStatus )
            {
            case TCodecProcessResult::EProcessIncomplete:
                // Not all data from input was consumed (EncodeL updated buffer members), but output was generated
                
                iEncBuffer->Des().Append(aDestBuffer->Data());
                iNumberOfFramesInOutputBuffer++;
                         
                aDestBuffer->Data().SetLength( 0 );
                aDestBuffer->SetPosition( 0 );
                
                break;

            case TCodecProcessResult::EProcessComplete:
                // all data from input was used and output was generated
      
                iEncBuffer->Des().Append(aDestBuffer->Data());
                
                iNumberOfFramesInOutputBuffer++;
                    
                aDestBuffer->Data().SetLength( 0 );
                aDestBuffer->SetPosition( 0 );
         
                //completed = ETrue;
                
                break;

            case TCodecProcessResult::EDstNotFilled:
                // need more input data, can't fill the output yet; put it back to the empty queue
                //completed = ETrue;
          
                break;

            default:
                // EEndOfData, EProcessError, EProcessIncompleteRepositionRequest, EProcessCompleteRepositionRequest
                User::Leave( KErrUnknown );
            }
        
        aSrcUsed += result.iSrcBytesProcessed;
        if (aSrcUsed >= (STATIC_CAST(CMMFDataBuffer*, aSourceBuffer)->Data().Length()))
			{
            PRINT((_L("CProcEncoder::FeedCodecL() ProcessL is completed aSrcUsed[%d]"), aSrcUsed));
			completed = ETrue;
			}
            

        }
    
    PRINT((_L("CProcEncoder::FeedCodecL() out")));
    }
    

TBool CProcEncoder::GetIsSupportedDestCodec()
    {
    TFourCC fourCC; 
    TUid euid;
 
    if (iAudioType == EAudAMR)
        {
        fourCC = TFourCC(KMMFFourCCCodeAMR);
        euid = KAedAMRNBEncSWCodecUid;
        }
    else if (iAudioType == EAudAAC_MPEG4 )
        {
        fourCC = TFourCC(KMMFFourCCCodeAAC);
        euid = KAedAACEncSWCodecUid;
        }
    
    _LIT8(emptyFourCCString, "    ,    ");
    TBufC8<9> fourCCString(emptyFourCCString);
    TPtr8 fourCCPtr = fourCCString.Des();
    TPtr8 fourCCPtr1(&fourCCPtr[0], 4);
    TPtr8 fourCCPtr2(&fourCCPtr[5], 4 );

    TFourCC srcFourCC(' ','P','1','6');
    srcFourCC.FourCC(&fourCCPtr1);
    fourCC.FourCC(&fourCCPtr2);

    TBool found = EFalse;
    TRAPD( err, found = CheckIfCodecAvailableL( fourCCPtr, euid ));
    
    if (err == KErrNone)
        {
        return found;    
        }
    else
        {
        return EFalse;
        }
    
    }

    
void CProcEncoder::SetDestCodecL()
    {
    PRINT((_L("CProcEncoder::SetDestCodecL() in")));

    if ( !GetIsSupportedDestCodec() )
        {
        PRINT((_L("CProcEncoder::SetDestCodecL() error, unsupported codec")));
        User::Leave( KErrNotSupported );
        }
    
    if ( iDestCodec )
        {
        delete iDestCodec;
        iDestCodec = NULL;
        }

    TUid euid = TUid::Null();
 
    if (iAudioType == EAudAMR)
        {
        euid = KAedAMRNBEncSWCodecUid;
        }
    else if (iAudioType == EAudAAC_MPEG4 )
        {
        euid = KAedAACEncSWCodecUid;
        }
    
    
    iDestCodec = CMMFCodec::NewL (euid);
    iReady = EFalse;

    PRINT((_L("CProcEncoder::SetDestCodecL() out")));
    }   
   
    
TBool CProcEncoder::CheckIfCodecAvailableL(
    const TDesC8& aCodecFourCCString, const TUid& aCodecUId )
    {
    PRINT((_L("CProcEncoder::CheckIfCodecAvailableL() in")));
    TBool found = EFalse;

    // Create a TEcomResolverParams structure.
    TEComResolverParams resolverParams ;
    resolverParams.SetDataType( aCodecFourCCString ) ;
    resolverParams.SetWildcardMatch( EFalse ) ;

    RImplInfoPtrArray plugInArray ; // Array to return matching decoders in (place on cleanupstack _after_ ListImplementationsL() )

    TUid UidMmfPluginInterfaceCodec = {KMmfUidPluginInterfaceCodec};

    // ListImplementationsL leaves if it cannot find anything so trap the error and ignore it.
    TRAPD( err, REComSession::ListImplementationsL(UidMmfPluginInterfaceCodec, resolverParams, plugInArray ) ) ;
    CleanupResetAndDestroyPushL(plugInArray);

    if (err == KErrNone)
        {
        found = EFalse;
        for ( TInt i = 0; i < plugInArray.Count(); i++)
            {
            // there is a match, but 1st we need to ensure it is the one we have tested with, and that have compatible implementation of ConfigureL
            PRINT((_L("CProcEncoder::CheckIfCodecAvailable() plugin found with Uid 0x%x"), plugInArray[i]->ImplementationUid().iUid ));
            if ( plugInArray[i]->ImplementationUid() == aCodecUId )
                {
			    //match accepted
                PRINT((_L("CProcEncoder::CheckIfCodecAvailable() plugin accepted")));
			    found = ETrue;
                }
            }
        }
    else
        {
        PRINT((_L("CProcEncoder::CheckIfCodecAvailable() Error in ListImp.: %d"), err));
        //no match
        found = EFalse;
        }


    CleanupStack::PopAndDestroy();  //plugInArray
    PRINT((_L("CProcEncoder::CheckIfCodecAvailableL() out")));
    return found;
    }

TCodecProcessResult CProcEncoder::EncodeL( CMMFCodec* aCodec, CMMFDataBuffer* aInBuffer, CMMFDataBuffer* aOutBuffer)
    {
    PRINT((_L("CProcEncoder::EncodeL() in, input pos: %d, length: %d"), aInBuffer->Position(), aInBuffer->Data().Length() ));
    TCodecProcessResult result;

    result = aCodec->ProcessL (*aInBuffer, *aOutBuffer);

    switch (result.iStatus)
        {
        case TCodecProcessResult::EProcessComplete:
            // finished processing source data, all data in sink buffer
            PRINT((_L("CProcEncoder::FeedCodecL() EProcessComplete")));
            aInBuffer->SetPosition( 0 );
            aInBuffer->Data().SetLength(0);
            break;

        case TCodecProcessResult::EDstNotFilled:
            // the destination is not full, we need more data. Handled in caller
            PRINT((_L("CProcEncoder::FeedCodecL() EDstNotFilled")));
            aInBuffer->SetPosition( 0 );
            aInBuffer->Data().SetLength(0);
            break;

        case TCodecProcessResult::EProcessIncomplete:
            // the sink was filled before all the source was processed
            PRINT((_L("CProcEncoder::FeedCodecL() EProcessIncomplete")));
            aOutBuffer->SetPosition( 0 );
            aInBuffer->SetPosition( aInBuffer->Position() + result.iSrcBytesProcessed );
            break;

        default:
            break;
        }

 

    PRINT((_L("CProcEncoder::EncodeL() out, %d -> %d"),result.iSrcBytesProcessed, result.iDstBytesAdded));
    PRINT((_L("CProcEncoder::EncodeL() out, input pos: %d, length: %d"), aInBuffer->Position(), aInBuffer->Data().Length() ));
    return result;
    }