--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/camcordermmfplugin/mediarecorder/Src/CCMRMDFVideoRecorder.cpp Thu Dec 17 08:51:24 2009 +0200
@@ -0,0 +1,4238 @@
+/*
+* Copyright (c) 2002-2008 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "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:
+*
+* Description: Implementation for video recorder
+*
+*/
+
+
+// INCLUDE FILES
+#include "mmf/common/mmfbase.h"
+#include "CCMRRecorderBase.h"
+#include "CCMRVideoSettings.h" // default video settings
+#include "CCMRSupportedCodecs.h"
+#include "CCMRVideoHWParams.h"
+#include "CCMRVideoRecorder.h"
+#include "CCMRMediaRecorder.h" // for bitrate control mode
+#include "CCMRVideoThreadProxy.h"
+#include "CCMRFifo.h"
+#include "CCMRActiveOutput.h"
+#include "CCMRVideoCodecDataH263.h"
+#include "CCMRVideoCodecDataMPEG4.h"
+#include "CCMRVideoCodecDataH264AVC.h"
+#include <mmf/common/mmfvideo.h>
+
+/*
+#include "OstTraceDefinitions.h"
+#ifdef OST_TRACE_COMPILER_IN_USE
+#include "CCMRMDFVideoRecorderTraces.h"
+#endif
+*/
+
+// MACROS
+// Assertion macro wrapper for code cleanup
+#define VRASSERT(x) __ASSERT_DEBUG(x, User::Panic(_L("CCMRMDFVIDEORECORDER"), EInternalAssertionFailure))
+
+// Debug print macro
+#ifdef _DEBUG
+#include <e32svr.h>
+#define PRINT(x) RDebug::Print x;
+#else
+#define PRINT(x)
+#endif
+
+// ============================= LOCAL FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// convertFrameRateToInterval Converts framerate to frame time interval
+// (time between 2 consecutive frames).
+// Returns: TInt time between 2 consecutive frames
+// -----------------------------------------------------------------------------
+//
+static TInt convertFrameRateToInterval(TReal32 aFrameRate)
+ {
+ return (TInt(1E6/aFrameRate + 0.5));
+ }
+
+
+// -----------------------------------------------------------------------------
+// convertFrameRateToInterval Converts framerate to frame time interval
+// (time between 2 consecutive frames).
+// Returns: TInt time between 2 consecutive frames
+// -----------------------------------------------------------------------------
+//
+static TInt TLinearOrderFuncVideoSizeRate(const TPictureRateAndSize& aPictureRateAndSize1,
+ const TPictureRateAndSize& aPictureRateAndSize2 )
+ {
+ if ( (aPictureRateAndSize1.iPictureSize.iWidth == aPictureRateAndSize2.iPictureSize.iWidth) &&
+ (aPictureRateAndSize1.iPictureSize.iHeight == aPictureRateAndSize2.iPictureSize.iHeight) &&
+ (aPictureRateAndSize1.iPictureRate == aPictureRateAndSize2.iPictureRate) )
+ {
+ return 0;
+ }
+
+ if ( (aPictureRateAndSize1.iPictureSize.iWidth < aPictureRateAndSize2.iPictureSize.iWidth) ||
+ (aPictureRateAndSize1.iPictureSize.iHeight < aPictureRateAndSize2.iPictureSize.iHeight) ||
+ (aPictureRateAndSize1.iPictureRate < aPictureRateAndSize2.iPictureRate))
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+
+// ================= MEMBER FUNCTIONS =======================
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::NewL
+// Two-phased constructor.
+// ---------------------------------------------------------
+//
+CCMRVideoRecorder* CCMRVideoRecorder::NewL(MAsyncEventHandler& aEventHandler, CCMRConfigManager* aConfig )
+ {
+ PRINT((_L("CCMRVideoRecorder::NewL(), In: MDF")))
+
+ CCMRVideoRecorder* self = new (ELeave) CCMRVideoRecorder(aEventHandler);
+ CleanupStack::PushL( self );
+ self->ConstructL(aConfig);
+ CleanupStack::Pop();
+
+ PRINT((_L("CCMRVideoRecorder::NewL(), Out: MDF")))
+ return self;
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::ConstructL()
+// Symbian 2nd phase constructor can leave.
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::ConstructL(CCMRConfigManager* aConfig)
+ {
+ PRINT((_L("CCMRVideoRecorder::ConstructL(), In")))
+
+ SetState(EStateNone);
+
+ iConfig = aConfig;
+ iNumCameraBuffers = iConfig->PluginSettings().iCMRNumCameraBuffers;
+ // Create input fifo
+ iSourceFifo = CCMRFifo::NewL(iNumCameraBuffers);
+ iCodingFifo = CCMRFifo::NewL(iNumCameraBuffers);
+
+ iOutputSinkBuffer = new (ELeave) CCMRMediaBuffer;
+
+ iSizeIndex = -1;
+ iSizeIndex420 = -1;
+ iSizeIndex422 = -1;
+ iSizeIndexDCEncoder = -1;
+ iRateIndex = -1;
+ iRateIndex420 = -1;
+ iRateIndex422 = -1;
+ iRateIndexDCEncoder = -1;
+ iDevVideoRec = NULL;
+ iEncoderHWDeviceId = 0;
+ iPreProcessorHWDeviceId = 0;
+ iOutputVideoBuffer = NULL;
+ iVideoBufferType = CCMRMediaBuffer::EVideoH263;
+ iClockSource = NULL;
+ iDecSpecInfoLength = 0;
+ iPreferredEncoderUID = KNullUid;
+ iPreferredEncapsulationSet = EFalse;
+ iFrameSize.SetSize( KCMRFrameWidth, KCMRFrameHeight );
+ iSourceFrameRate = KCMRFrameRate;
+ iSourceFrameInterval = convertFrameRateToInterval(iSourceFrameRate);
+ iEncodingFrameRate = KCMRFrameRate;
+ iMaxFrameRate4GivenSize = KCMRFrameRate;
+ iRequestedFrameRate = KCMRFrameRate;
+ CCMRRecorderBase::SetTargetBitRateL( KCMRTargetBitRate );
+
+ iMinRandomAccessPeriodInSeconds = KCMRMinRandomAccessPeriod;
+
+ iMimeType = KCMRMimeTypeH263BaselineProfile; //copy
+ iMimeType += _L8( "; level=10" ); // append
+ iVideoCodecData = new (ELeave) CCMRVideoCodecDataH263(10); // default is H.263 level 10
+
+ iBitRateMode = EBitRateConstant;
+
+ iTimeWhenPaused = 0;
+ iTotalPausedTime = 0;
+
+ iErrorCode = KErrNone;
+
+ iEncoderInputQueueLength = 0;
+
+ iNumberOfCapturedFrames = 0;
+ iNumberOfEncodedFrames = 0;
+
+ iMutexObj.CreateLocal();
+ iMutexCreated = ETrue;
+ iBufferReturnAO = CCMRReturnAO::NewL(this);
+
+ iFatalError = EFalse;
+ iInputEnd = EFalse;
+ iStreamEnd = EFalse;
+
+ // Direct capture
+ iDirectCapture = EFalse;
+ iCameraHandle = 0;
+ iSkipBuffers = EFalse;
+ iDriftFrameSkipCount = 0;
+ iAddedFrameDurationCount = 0;
+ iPreviousCameraFrameIndex = 0;
+
+#ifdef _DEBUG
+ iLastCapture = 0;
+ iCumulativeEncodingTime = iCumulativeCaptureTime = 0;
+ iAverageEncodingTime = iAverageCaptureTime = 0;
+#endif
+ iDevVideoRec = CMMFDevVideoRecord::NewL( *this );
+ iAvailableVideoEncoders.Reset();
+ iAvailableVideoFrameSizesRates.Reset();
+
+ PRINT((_L("CCMRVideoRecorder::ConstructL() looking for defaul video codec encoder(s)")));
+ iDevVideoRec->FindEncodersL(iMimeType, 0 /* aPreProc */, iAvailableVideoEncoders, EFalse );
+ PRINT((_L("CCMRVideoRecorder::ConstructL() search found %d encoder(s)"), iAvailableVideoEncoders.Count() ));
+
+ if ( iConfig )
+ {
+ iConfig->SetVideoCodec(iMimeType);
+ iConfig->SetVideoPixelAspectRatio(KCMRAspectRatioNum, KCMRAspectRatioDenom);
+
+ // fill out defaults for Rate Control from ICM.
+ iRateControlOptions.iPictureQuality = iConfig->PluginSettings().iCMRPictureQuality;
+ iRateControlOptions.iLatencyQualityTradeoff = iConfig->PluginSettings().iCMRLatencyQualityTradeoff; // latency vs. quality
+ iRateControlOptions.iQualityTemporalTradeoff = iConfig->PluginSettings().iCMRQualityTemporalTradeoff; // spatial vs. temporal quality
+ }
+ UpdateSupportedVideoFrameSizesRates();
+ PRINT((_L("CCMRVideoRecorder::ConstructL(), Out")))
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::~CCMRVideoRecorder()
+// Destructor
+// ---------------------------------------------------------
+//
+CCMRVideoRecorder::~CCMRVideoRecorder()
+ {
+ PRINT((_L("CCMRVideoRecorder::~CCMRVideoRecorder(), In")));
+ // This is the counterpart to NewL & OpenL, e.g. Close & Delete
+ // free all memory allocated and uninitalize & delete objects created, e.g. DevVideoRecord
+
+#if defined VIDEO_FILE_OUTPUT || defined VIDEO_BS_FILE_OUTPUT
+ iOutputFile.Close();
+ iFs.Close();
+#endif
+
+ // to make PC Lint happy
+ iOutputVideoBuffer = NULL;
+ iOutput = NULL;
+ iClockSource = NULL;
+
+ // delete DevVideoRecord instance
+ delete iDevVideoRec;
+ PRINT((_L("CCMRVideoRecorder::~CCMRVideoRecorder() devvideorec deleted")));
+
+ iAvailableVideoFrameSizesRates.Close();
+ iAvailableVideoEncoders.Close();
+
+ delete iOutputSinkBuffer;
+ iOutputSinkBuffer = NULL;
+
+ delete iVideoCodecData;
+ iVideoCodecData = NULL;
+
+ // empty the source fifo, just in case once more; this should be done a) by encoder and b) by MdvroStreamEnd()
+ MFrameBuffer* cbuffer;
+ if ( iSourceFifo )
+ {
+ while ( !iSourceFifo->IsEmpty() )
+ {
+ cbuffer = reinterpret_cast<MFrameBuffer*>(iSourceFifo->Get());
+ // Release camera API buffer
+ cbuffer->Release();
+ }
+ delete iSourceFifo;
+ iSourceFifo = NULL;
+ }
+ TVideoPicture* picture;
+ if ( iCodingFifo )
+ {
+ // delete the empty devvr picture-holders stored in the fifo
+ while ( !iCodingFifo->IsEmpty() )
+ {
+ picture = reinterpret_cast<TVideoPicture*>(iCodingFifo->Get());
+ PRINT((_L("CCMRVideoRecorder::~CCMRVideoRecorder() deleting %x"),picture));
+ delete picture;
+ }
+ delete iCodingFifo;
+ iCodingFifo = NULL;
+ }
+ PRINT((_L("CCMRVideoRecorder::~CCMRVideoRecorder() fifos deleted")));
+
+ if ( iDecSpecInfo )
+ {
+ // We still have MPEG-4 decoder configuration info stored
+ delete iDecSpecInfo;
+ iDecSpecInfo = NULL;
+ }
+
+ // delete camera
+ delete iSource;
+ iSource = NULL;
+ PRINT((_L("CCMRVideoRecorder::~CCMRVideoRecorder() camera deleted")));
+
+ if ( iBufferReturnAO )
+ {
+ iBufferReturnAO->Cancel();
+ delete iBufferReturnAO;
+ }
+
+ if ( iThreadHandleOpened )
+ {
+ iOutputThreadHandle.Close();
+ iThreadHandleOpened = EFalse;
+ }
+
+ if ( iMutexCreated )
+ {
+ iMutexObj.Close();
+ }
+
+ SetState(EStateNone);
+
+ PRINT((_L("CCMRVideoRecorder::~CCMRVideoRecorder(), Out")));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetOutputL
+// Sets output active object
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetOutputL(CCMRActiveOutput* aOutput)
+ {
+ iOutput = aOutput;
+ iOutput->RegisterSourceL( this );
+ if ( iSource && iThreadHandleOpened )
+ {
+ SetState( EStateOpen );
+ }
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetClockSourceL
+// Set clock source
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetClockSource(MMMFClockSource* aClockSource)
+ {
+ iClockSource = aClockSource;
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetOutputThreadIdL
+// Sets id of the output thread
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetOutputThreadIdL(TUint aThreadId)
+ {
+ User::LeaveIfError( iOutputThreadHandle.Open(aThreadId) );
+
+ iThreadHandleOpened = ETrue;
+ if ( iOutput && iSource )
+ {
+ SetState( EStateOpen );
+ }
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetCameraHandleL
+// Sets camera handle and creates camera instance
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetCameraHandleL(TInt aCameraHandle)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetCameraHandleL(), In")))
+
+ // Create video source
+#ifdef VIDEO_FILE_INPUT
+ iSource = CCMRVideoFileSource::NewL(this);
+#else
+ iSource = CCMRVideoCameraSource::NewL(this, aCameraHandle);
+#endif
+
+ // Store a camera handle for future use
+ iCameraHandle = aCameraHandle;
+
+ // get camera info
+ iSource->CameraInfo(iCameraInfo);
+
+#if defined VIDEO_FILE_OUTPUT || defined VIDEO_BS_FILE_OUTPUT
+ User::LeaveIfError(iFs.Connect());
+ User::LeaveIfError(iOutputFile.Replace(iFs, _L("videorec_out.bin"), EFileWrite | EFileShareExclusive));
+#endif
+
+ if ( iOutput && iThreadHandleOpened )
+ {
+ SetState( EStateOpen );
+ }
+
+ PRINT((_L("CCMRVideoRecorder::SetCameraHandleL(), Out")))
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetupEncoderL
+// Private helper method to select & setup the encoder
+// plugin devvr must use
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetupEncoderL()
+ {
+#ifdef _DEBUG
+ TBuf<256> mime;
+ mime.Copy(iMimeType);
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() in, video mime-type: %S "), &mime ));
+#endif
+
+ if ( iPreferredEncoderUID != KNullUid )
+ {// We have preferred encoder UID from client - override encoder search and use it instead.
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() skipping encoder search. Using API user encoder: %d "), iPreferredEncoderUID.iUid));
+ iAvailableVideoEncoders.Reset();
+ iAvailableVideoEncoders.AppendL(iPreferredEncoderUID);
+ }
+ else if ( iConfig &&
+ ( iConfig->IsICMConfigDataAvailable() ) &&
+ ( iConfig->VideoQualitySettings().iVideoEncoderUID != KNullUid ) )
+ {// Video quality set has set UID value - override encoder search and use it instead.
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() skipping encoder search. Using ICM config encoder: %d "), iPreferredEncoderUID.iUid));
+ iAvailableVideoEncoders.Reset();
+ iAvailableVideoEncoders.AppendL(iPreferredEncoderUID);
+ }
+
+ // uncompressed format structures for YUV420 planar
+ TBool cameraSupports420 = ETrue;
+
+ if ( (iSizeIndex420 < 0) || (iRateIndex420 < 0) )
+ {
+ cameraSupports420 = EFalse;
+ }
+ else
+ {
+ cameraSupports420 = ETrue;
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() camera supports YUV420 planar") ));
+ }
+
+ // uncompressed format structures for YUV422
+ TBool cameraSupports422 = ETrue;
+ if ( (iSizeIndex422 < 0) || (iRateIndex422 < 0) )
+ {
+ cameraSupports422 = EFalse;
+ }
+ else
+ {
+ cameraSupports422 = ETrue;
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() camera supports YUV422 interleaved") ));
+ }
+
+ // Output format
+ CCompressedVideoFormat *comprFormat = CCompressedVideoFormat::NewL( iMimeType );
+ CleanupStack::PushL( comprFormat );
+
+ // index for encoder candidate. Have to go through the list to find an accerated encoder
+ TInt encoderIndex = -1;
+ TUncompressedVideoFormat encoderUncompFormat;
+
+ // encoder info for retrieving capabilities
+ CVideoEncoderInfo* encoderInfo = NULL;
+
+ // this is needed if init of HW accelerated codec failed and we retry => forces to select ARM codec
+ TBool alreadyFailedWithHWAccelerated = iVideoCodecHWAccelerated;
+ iVideoCodecHWAccelerated = EFalse;
+
+ TInt infoError = KErrNone;
+ TBool encoderSupports422 = EFalse;
+ TBool encoderSupports420 = EFalse;
+
+ TYuvCoefficients preproInputYuvCoefficient;
+ TYuvCoefficients preproOutputYuvCoefficient;
+ TYuvCoefficients encoderInputYuvCoefficient;
+ TUint aspectRatioNum = 0;
+ TUint aspectRatioDenom = 0;
+ TVideoDataUnitEncapsulation outputFormatEncapsulation = EDuElementaryStream;
+
+ if( KCMRMimeTypeH263() == iMimeType.Left( KCMRMimeTypeH263().Length() ))
+ {// H.263
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - setting H.263 YUVs") ));
+ preproInputYuvCoefficient = EYuvBt601Range1;
+ preproOutputYuvCoefficient = EYuvBt601Range0;
+ encoderInputYuvCoefficient = EYuvBt601Range0;
+ iVideoBufferType = CCMRMediaBuffer::EVideoH263;
+ // pixel aspect ratios
+ if ( iConfig && iConfig->IsICMConfigDataAvailable() )
+ {
+ aspectRatioNum = iConfig->VideoQualitySettings().iVideoPixelAspectRatioNum;
+ aspectRatioDenom = iConfig->VideoQualitySettings().iVideoPixelAspectRatioDenom;
+ }
+ else
+ {
+ aspectRatioNum = KCMRAspectRatioNum;
+ aspectRatioDenom = KCMRAspectRatioDenom;
+ }
+ }
+ else if ( KCMRMimeTypeMPEG4V() == iMimeType.Left( KCMRMimeTypeMPEG4V().Length() ))
+ {// MPEG-4
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - setting MPEG4 YUVs") ));
+ preproInputYuvCoefficient = EYuvBt709Range1;
+ preproOutputYuvCoefficient = EYuvBt709Range1;
+ encoderInputYuvCoefficient = EYuvBt709Range1;
+ // pixel aspect ratios
+ if ( iConfig && iConfig->IsICMConfigDataAvailable() )
+ {
+ aspectRatioNum = iConfig->VideoQualitySettings().iVideoPixelAspectRatioNum;
+ aspectRatioDenom = iConfig->VideoQualitySettings().iVideoPixelAspectRatioDenom;
+ }
+ else
+ {
+ aspectRatioNum = KCMRMPEG4AspectRatioNum;
+ aspectRatioDenom = KCMRMPEG4AspectRatioDenom;
+ }
+ iVideoBufferType = CCMRMediaBuffer::EVideoMPEG4;
+ }
+ else
+ {// H.264 AVC
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - setting H.264 AVC YUVs") ));
+ preproInputYuvCoefficient = EYuvBt709Range1;
+ preproOutputYuvCoefficient = EYuvBt709Range1;
+ encoderInputYuvCoefficient = EYuvBt709Range1;
+ // pixel aspect ratios 1:1
+ if ( iConfig && iConfig->IsICMConfigDataAvailable() )
+ {
+ aspectRatioNum = iConfig->VideoQualitySettings().iVideoPixelAspectRatioNum;
+ aspectRatioDenom = iConfig->VideoQualitySettings().iVideoPixelAspectRatioDenom;
+ }
+ else
+ {
+ aspectRatioNum = KCMRMPEG4AspectRatioNum;
+ aspectRatioDenom = KCMRMPEG4AspectRatioDenom;
+ }
+ // output format encapsulation
+ iVideoBufferType = CCMRMediaBuffer::EVideoH264NAL;
+ outputFormatEncapsulation = EDuGenericPayload;
+ }
+
+ TBool directCaptureEncoder = EFalse;
+ TInt supportedEncoderInputsCount = 0;
+ // find an encoder with matching capabilities
+ for ( TInt i = 0 ; i < iAvailableVideoEncoders.Count(); i++ )
+ {
+ encoderInfo = NULL;
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - getting info from a plugin index[%d] with Uid 0x%x"), i, iAvailableVideoEncoders[i].iUid ));
+ TRAPD(error, (encoderInfo = ReadEncoderInfoL(iAvailableVideoEncoders[i])) );
+
+ if ( encoderInfo == NULL )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - getting info from a plugin failed, skipping") ));
+ infoError = error;
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - got an info from a plugin, checking...") ));
+
+ // item was popped in CheckEncoderInfoL, push it back to stack
+ CleanupStack::PushL( encoderInfo );
+
+ // check if compressed video format matches
+ if ( encoderInfo->SupportsOutputFormat(*comprFormat) ) // max picture size & bit-rate was checked in the level of comprFormat
+ {
+ // check input format of encoder.
+ // a candidate encoder supporting the required compressed format was found
+ encoderSupports422 = EFalse;
+ encoderSupports420 = EFalse;
+
+ RArray<TUncompressedVideoFormat> supportedEncoderInputs = encoderInfo->SupportedInputFormats();
+ supportedEncoderInputsCount = supportedEncoderInputs.Count();
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Encoder at index[%d] supports %d input format(s), checking for support"), i, supportedEncoderInputs.Count() ));
+
+ // Go through supported input format and look for match to important parameters.
+ for(TInt j = 0; j < supportedEncoderInputsCount; j++ )
+ {
+ if ( !encoderSupports422 &&
+ cameraSupports422 &&
+ supportedEncoderInputs[j].iDataFormat == EYuvRawData &&
+ supportedEncoderInputs[j].iYuvFormat.iCoefficients == encoderInputYuvCoefficient &&
+ ( supportedEncoderInputs[j].iYuvFormat.iDataLayout == EYuvDataInterleavedLE ||
+ supportedEncoderInputs[j].iYuvFormat.iDataLayout == EYuvDataInterleavedBE ) &&
+ supportedEncoderInputs[j].iYuvFormat.iYuv2RgbMatrix == NULL &&
+ supportedEncoderInputs[j].iYuvFormat.iRgb2YuvMatrix == NULL &&
+ supportedEncoderInputs[j].iYuvFormat.iAspectRatioNum == aspectRatioNum &&
+ supportedEncoderInputs[j].iYuvFormat.iAspectRatioDenom == aspectRatioDenom &&
+ ( supportedEncoderInputs[j].iYuvFormat.iPattern == EYuv422Chroma1 ||
+ supportedEncoderInputs[j].iYuvFormat.iPattern == EYuv422Chroma2 ) )
+ {
+ // YUV 422 input format can be used with this encoder
+ encoderSupports422 = ETrue;
+ encoderUncompFormat = supportedEncoderInputs[j];
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Encoder at index[%d] supports YUV422 interleaved"), i ));
+ break;
+ }
+ if ( !encoderSupports420 &&
+ !encoderSupports422 &&
+ cameraSupports420 &&
+ supportedEncoderInputs[j].iDataFormat == EYuvRawData &&
+ supportedEncoderInputs[j].iYuvFormat.iCoefficients == encoderInputYuvCoefficient &&
+ supportedEncoderInputs[j].iYuvFormat.iDataLayout == EYuvDataPlanar &&
+ supportedEncoderInputs[j].iYuvFormat.iYuv2RgbMatrix == NULL &&
+ supportedEncoderInputs[j].iYuvFormat.iRgb2YuvMatrix == NULL &&
+ supportedEncoderInputs[j].iYuvFormat.iAspectRatioNum == aspectRatioNum &&
+ supportedEncoderInputs[j].iYuvFormat.iAspectRatioDenom == aspectRatioDenom &&
+ ( supportedEncoderInputs[j].iYuvFormat.iPattern == EYuv420Chroma1 ||
+ supportedEncoderInputs[j].iYuvFormat.iPattern == EYuv420Chroma2 ) )
+ {
+ // YUV 420 planar input format can be used with this encoder
+ encoderSupports420 = ETrue;
+ encoderUncompFormat = supportedEncoderInputs[j];
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Encoder at index[%d] supports YUV420 planar"), i ));
+ if( !cameraSupports422 )
+ {
+ break;
+ }
+ }
+ }
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Encoder at index[%d] supports: 420[%d], 422[%d]"), i, encoderSupports420, encoderSupports422 ));
+
+ // Check directCapture support for current codec
+ directCaptureEncoder = encoderInfo->SupportsDirectCapture();
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Encoder at index[%d] supports DirectCapture:[%d]."), i, directCaptureEncoder));
+
+ // determine H.264 AVC encapsulation
+ if ( (iVideoBufferType == CCMRMediaBuffer::EVideoH264NAL) ||
+ (iVideoBufferType == CCMRMediaBuffer::EVideoH264Bytestream) )
+ {
+ if (( iPreferredEncapsulationSet ) &&
+ ( encoderInfo->SupportedDataUnitEncapsulations() & iPreferredEncapsulation ))
+ {
+ outputFormatEncapsulation = iPreferredEncapsulation;
+ }
+ else
+ {
+ if (encoderInfo->SupportedDataUnitEncapsulations() & EDuGenericPayload)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() H.264 Encoder at index[%d] set to EDuGenericPayload encapsulation."), i));
+ outputFormatEncapsulation = EDuGenericPayload;
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() H.264 Encoder at index[%d] set to EDuElementaryStream encapsulation."), i));
+ outputFormatEncapsulation = EDuElementaryStream;
+ }
+ }
+
+ if ( outputFormatEncapsulation == EDuGenericPayload )
+ {
+ iVideoBufferType = CCMRMediaBuffer::EVideoH264NAL;
+ }
+ else
+ {
+ iVideoBufferType = CCMRMediaBuffer::EVideoH264Bytestream;
+ }
+ }
+
+ if (directCaptureEncoder && (supportedEncoderInputsCount != 0))
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Warning - Encoder at index[%d] publish input formats even though it supports directcapture - ERROR!."), i));
+ }
+
+ if ( encoderSupports422 || encoderSupports420 || (directCaptureEncoder && (supportedEncoderInputsCount == 0)) )
+ {
+ // encoder's input format matches with camera's output
+ if ( encoderInfo->Accelerated() && (alreadyFailedWithHWAccelerated == EFalse) )
+ // assume there is only 1 HW accelerated codec, and if it was selected previously we come here only if the init failed
+ // => must use non-hw-accelerated codec
+ {
+ // no need to search for more, accelerated encoder is preferred
+ encoderIndex = i;
+ iVideoCodecHWAccelerated = ETrue;
+ iDirectCapture = directCaptureEncoder;
+ // HW accelerated is preferred regardless of the input format, but if both formats are supported, then 422 is preferred
+ // need to set the camera variables accordingly
+ if ( !(directCaptureEncoder && (supportedEncoderInputsCount == 0)) )
+ {
+ if ( encoderSupports422 )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() YUV422 interleaved selected as input format") ));
+ iSizeIndex = iSizeIndex422;
+ iRateIndex = iRateIndex422;
+ iVideoFormat = CCamera::EFormatYUV422;
+ }
+ else if (encoderSupports420)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() YUV420 planar selected as input format") ));
+ iSizeIndex = iSizeIndex420;
+ iRateIndex = iRateIndex420;
+ iVideoFormat = CCamera::EFormatYUV420Planar;
+ }
+ }
+ // check max framerate for given picture size
+ RArray<TPictureRateAndSize> rateAndSize = encoderInfo->MaxPictureRates();
+ TUint rates = rateAndSize.Count();
+ for ( TUint j = 0; j < rates; j++ )
+ {
+ if ( rateAndSize[j].iPictureSize == iFrameSize )
+ {
+ iMaxFrameRate4GivenSize = rateAndSize[j].iPictureRate;
+ break;
+ }
+ }
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - there is a suitable HW accelerated plugin, don't check other plugins") ));
+ CleanupStack::PopAndDestroy( encoderInfo );
+ break;
+ }
+ else if ( encoderIndex < 0 )
+ {
+ // accept also non-accelerated if no other is available
+ encoderIndex = i;
+ // encoder is selected regardless of the input format, but if both formats are supported, then 422 is preferred
+ // need to set the camera variables accordingly
+ if ( !(directCaptureEncoder && (supportedEncoderInputsCount == 0)) )
+ {
+ if ( encoderSupports422 )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() YUV422 interleaved selected as input format") ));
+ iSizeIndex = iSizeIndex422;
+ iRateIndex = iRateIndex422;
+ iVideoFormat = CCamera::EFormatYUV422;
+ }
+ else if (encoderSupports420)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() YUV420 planar selected as input format") ));
+ iSizeIndex = iSizeIndex420;
+ iRateIndex = iRateIndex420;
+ iVideoFormat = CCamera::EFormatYUV420Planar;
+ }
+ }
+
+ // check max framerate for given picture size
+ RArray<TPictureRateAndSize> rateAndSize = encoderInfo->MaxPictureRates();
+ TUint rates = rateAndSize.Count();
+
+ for ( TUint j = 0; j < rates; j++ )
+ {
+ if ( rateAndSize[j].iPictureSize == iFrameSize )
+ {
+ iMaxFrameRate4GivenSize = rateAndSize[j].iPictureRate;
+ break;
+ }
+ }
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - there is a suitable SW plugin.") ));
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - there is already similar encoder - preferring first.") ));
+ // this kind of encoder already found
+ }
+
+ }
+ else
+ {
+ // skip this encoder, done in the following lines
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() encoder doesn't support YUV420/YUV422 input format or isn't directcapture encoder") ));
+ }
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() encoder at index[%d] doesn't support given compressed output format"), i ));
+ }
+ CleanupStack::PopAndDestroy( encoderInfo );
+ }
+ }
+
+ TUncompressedVideoFormat preproInputFormat = encoderUncompFormat;
+ TUncompressedVideoFormat preproOutputFormat = encoderUncompFormat;
+ preproInputFormat.iYuvFormat.iCoefficients = preproInputYuvCoefficient;
+ preproOutputFormat.iYuvFormat.iCoefficients = preproOutputYuvCoefficient;
+
+ if ( encoderIndex >= 0 )
+ {
+ if ( iVideoCodecHWAccelerated )
+ {
+ iNumCameraBuffers = iConfig->PluginSettings().iCMRNumCameraBuffers;
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - found a suitable HW accelerated video encoder")));
+ }
+ else
+ {
+ iNumCameraBuffers = iConfig->PluginSettings().iCMRNumCameraBuffersARM;
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() - found a suitable ARM video encoder")));
+ }
+
+ // if encoder doesn´t support directcapture try to find preprocessor that does.
+ if ( !iDirectCapture && iVideoCodecHWAccelerated )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Searching preprocessor.")));
+ RArray<TUid> preprocessors;
+ CleanupClosePushL( preprocessors );
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Search starting")));
+
+ TInt preproSearchErr = KErrNone;
+ TRAP(preproSearchErr, iDevVideoRec->FindPreProcessorsL(EPpYuvToYuv, preprocessors));
+ if (preproSearchErr == KErrNone)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Search found %d preprocessor(s)"), preprocessors.Count() ));
+ CPreProcessorInfo* preprocessorInfo = NULL;
+ // find a preprocessor with matching capabilities to encoder
+ for ( TInt k = 0; k < preprocessors.Count(); k++ )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Checking preprocessor at index[%d] with Uid 0x%x"), k, preprocessors[k].iUid ));
+ preprocessorInfo = iDevVideoRec->PreProcessorInfoLC(preprocessors[k]);
+
+ if ( preprocessorInfo->SupportsInputFormat(preproInputFormat) &&
+ preprocessorInfo->SupportsOutputFormat(preproOutputFormat) &&
+ preprocessorInfo->SupportsDirectCapture() )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() preprocessor[%d] is suitable, setting it as preprocessor"), k ));
+ iDirectCapture = ETrue;
+ iPreProcessorHWDeviceId = iDevVideoRec->SelectPreProcessorL(preprocessors[k]);
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() preprocessor selected")));
+ CleanupStack::PopAndDestroy(preprocessorInfo);
+ break;
+ }
+ CleanupStack::PopAndDestroy(preprocessorInfo);
+ }
+ }
+ else if (preproSearchErr == KErrNotFound)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() didn't find any preprocessors")));
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() preprocessors search error: %d"), preproSearchErr));
+ CleanupStack::PopAndDestroy(); //preprocessors
+ User::Leave(preproSearchErr);
+ }
+ CleanupStack::PopAndDestroy(); //preprocessors
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Searching preprocessor done.")));
+ }
+ }
+ else
+ {
+ if ( infoError != KErrNone )
+ {
+ // there is an encoder but it can't be used
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() there is an encoder but it can't be used - info error=%d"), infoError ));
+ User::Leave( infoError );
+ }
+ else
+ {
+ // No suitable encoder found. This should not be possible if the codec was properly set beforehand
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() No suitable encoder found.")));
+ User::Leave( KErrNotSupported );
+ }
+ }
+
+ iEncoderHWDeviceId = iDevVideoRec->SelectEncoderL(iAvailableVideoEncoders[encoderIndex]);
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() encoder selected")));
+
+ // Set input format only for non-directcapture encoders
+ if ( (directCaptureEncoder && (supportedEncoderInputsCount == 0)) )
+ {
+ // Direct capture encoders should ignore
+ TUncompressedVideoFormat ignoredFormat;
+ iDevVideoRec->SetInputFormatL(iEncoderHWDeviceId, ignoredFormat, iFrameSize);
+ }
+ else
+ {
+ iDevVideoRec->SetInputFormatL(iEncoderHWDeviceId, encoderUncompFormat, iFrameSize);
+ }
+
+ if ( iPreProcessorHWDeviceId )
+ {
+ iDevVideoRec->SetInputFormatL(iPreProcessorHWDeviceId, preproInputFormat, iFrameSize);
+ }
+
+ // Buffer options
+ TEncoderBufferOptions bufferOptions;
+ bufferOptions.iHrdVbvParams.Set(NULL, 0);
+ bufferOptions.iHrdVbvSpec = EHrdVbvNone;
+ bufferOptions.iMaxPreEncoderBufferPictures = iConfig->PluginSettings().iCMRNumCameraBuffers;
+ bufferOptions.iMinNumOutputBuffers = iConfig->PluginSettings().iCMRMinNumOutputBuffers;
+
+ bufferOptions.iMaxCodedSegmentSize =
+ bufferOptions.iMaxOutputBufferSize =
+ bufferOptions.iMaxCodedPictureSize = iVideoCodecData->MaxBufferLength(iFrameSize);
+
+ iDevVideoRec->SetBufferOptionsL(bufferOptions);
+
+ // Set output format
+ iDevVideoRec->SetOutputFormatL(iEncoderHWDeviceId, *comprFormat, EDuCodedPicture,
+ outputFormatEncapsulation, EFalse /* aSegmentationAllowed */);
+
+ if( iPreProcessorHWDeviceId)
+ {
+ iDevVideoRec->SetOutputFormatL(iPreProcessorHWDeviceId, preproOutputFormat);
+ }
+
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() MinRandomAccess= %d"), iMinRandomAccessPeriodInSeconds ));
+ if ( iConfig && iConfig->IsICMConfigDataAvailable() )
+ {
+ iMinRandomAccessPeriodInSeconds = iConfig->VideoQualitySettings().iRandomAccessRate;
+ }
+ else
+ {
+ if ( (iFrameSize.iWidth >= KCMRCIFWidth) && (iFrameSize.iHeight >= KCMRCIFHeight) )
+ {
+ iMinRandomAccessPeriodInSeconds = KCMRMinRandomAccessPeriodHighRes;
+ }
+ }
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Set to MinRandomAccess= %d"), iMinRandomAccessPeriodInSeconds ));
+
+ // Set random access in fps
+ if ( iMinRandomAccessPeriodInSeconds > 0 )
+ {
+ iDevVideoRec->SetMinRandomAccessRate( TReal(1) / TReal(iMinRandomAccessPeriodInSeconds) );
+ }
+ else
+ {
+ // there is no concept of disabling random access in MSL, hence use the default
+ iDevVideoRec->SetMinRandomAccessRate( TReal(1) / TReal(KCMRMinRandomAccessPeriod) );
+ }
+
+ CleanupStack::PopAndDestroy( comprFormat );
+
+ // set codec specific settings
+ iVideoCodecData->SetPreInitParamsL(iDevVideoRec);
+
+ if (iDirectCapture)
+ {
+ TInt status = KErrNone;
+ // Set the data source to a camera
+ TRAP( status, iDevVideoRec->SetSourceCameraL( iCameraHandle, TReal(iSourceFrameRate)) );
+
+ if ( status != KErrNone )
+ {
+ // Probably it's not supported, although directCapture was declared in the hwdevice info.
+ // Leave or to give one more chance to encoder, to use src memory as a source ?
+ // Yes, otherwise User::Leave(status);
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() Setting camerasource to MDF failed.")));
+ }
+ else
+ {
+ if ( iClockSource )
+ {
+ iDevVideoRec->SetClockSource( iClockSource );
+ }
+ // Ok, use direct capture
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() out")));
+ return;
+ }
+ }
+
+ // Set source memory is allocated & released by camera API
+ iDevVideoRec->SetSourceMemoryL( TReal(iSourceFrameRate), ETrue, KCMREncodingRealTime);
+ // Set the source to memory buffers
+ iDirectCapture = EFalse;
+ PRINT((_L("CCMRVideoRecorder::SetupEncoderL() out")));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::ReadEncoderInfoL
+// Read encoder information object. The object is popped from
+// stack and MUST be handled properly by the caller
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+CVideoEncoderInfo* CCMRVideoRecorder::ReadEncoderInfoL(TUid aUid)
+ {
+ PRINT((_L("CCMRVideoRecorder::ReadEncoderInfoL(), In")))
+ CVideoEncoderInfo* info = iDevVideoRec->VideoEncoderInfoLC( aUid );
+ CleanupStack::Pop(info);
+
+ PRINT((_L("CCMRVideoRecorder::ReadEncoderInfoL(), Out")))
+ return info;
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::PrepareL
+// Prepares the recorder for recording
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::PrepareL()
+ {
+ PRINT((_L("CCMRVideoRecorder::PrepareL(), In")));
+
+ if ( (State() != EStateOpen) && (State() != EStateReadyToRecord) )
+ {
+ PRINT((_L("CCMRVideoRecorder::PrepareL() wrong state")));
+ User::Leave( KErrNotReady );
+ }
+
+ if ( State() == EStateReadyToRecord )
+ {
+ // No prepare needed... already ready.
+ // everything ok, inform observer that we are prepared.
+ PRINT((_L("CCMRVideoRecorder::PrepareL(), Already prepared. Return right away")));
+ DoSendEventToClient( KCMRPrepareComplete, KErrNone );
+ return;
+ }
+
+ if (iDevVideoRec)
+ {
+ delete iDevVideoRec;
+ iDevVideoRec = NULL;
+ }
+
+ iDevVideoRec = CMMFDevVideoRecord::NewL( *this );
+
+ // reset error code
+ iErrorCode = KErrNone;
+
+ // If the user of the recorder API has not set valid settings,
+ // use default ones
+
+ if ( ((iSizeIndex420 < 0) && (iSizeIndex422 < 0) && (iSizeIndexDCEncoder < 0))
+ || ((iSizeIndex420 >= iCameraInfo.iNumVideoFrameSizesSupported) && (iSizeIndex422 >= iCameraInfo.iNumVideoFrameSizesSupported)) )
+ {
+ // set default frame size
+ SetFrameSizeL( TSize( KCMRFrameWidth, KCMRFrameHeight ) );
+ }
+
+ if ( ((iRateIndex420 < 0) && (iRateIndex422 < 0) && (iRateIndexDCEncoder < 0))
+ || ((iRateIndex420 >= iCameraInfo.iNumVideoFrameRatesSupported) && (iRateIndex422 >= iCameraInfo.iNumVideoFrameRatesSupported)) )
+ {
+ // set default frame rate
+ SetFrameRateL( KCMRFrameRate );
+ }
+
+ // if during previous run a new frame rate was requested; when requested it affected only on encoding rate
+ if ( iRequestedFrameRate != iSourceFrameRate )
+ {
+ SetState(EStateOpen);//change state to make the SetFrameRate work correctly
+ SetFrameRateL( iRequestedFrameRate );
+ }
+
+ // reset init completion flags
+ iEncoderInitComplete = EFalse;
+ iSourceInitComplete = EFalse;
+ iVideoCodecHWAccelerated = EFalse;
+ // Select & set parameters to video encoder
+ TRAPD(err, SetupEncoderL());
+ if ( err == KErrHardwareNotAvailable && iVideoCodecHWAccelerated )
+ {
+ // HW codec exists but can't be used, try again with ARM codec
+ SetupEncoderL();
+ }
+ else if ( err != KErrNone )
+ {
+ // some other error
+ PRINT((_L("CCMRVideoRecorder::PrepareL(), error [%d]"), err));
+ User::Leave( err );
+ }
+
+ SetState(EStatePreparing);
+
+ // Reserve camera for use
+ // Possibly asynchronous call (if not already reserved), completion is anyway informed using this->ReserveComplete()
+ // iSource has to be reserved only if we don't set hwdevice to use DirectCapture.
+ if ( !iDirectCapture )
+ {
+ iSource->Reserve();
+ }
+
+ // Initialize DevVideoRec
+ // asynchronous call, completion informed using this->MdvroInitializeComplete()
+ iDevVideoRec->Initialize();
+
+ PRINT((_L("CCMRVideoRecorder::PrepareL(), Out")));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::RecordL
+// Starts recording
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::RecordL()
+ {
+ PRINT((_L("CCMRVideoRecorder::RecordL() - START - iRequestStatus: 0x%08x"), iRequestStatus ));
+
+ if ( (State() == EStateRecording) || (State() == EStatePaused) )
+ {
+ // ignore, already recording
+ PRINT((_L("CCMRVideoRecorder::RecordL() ignore")));
+ return;
+ }
+
+ if ( State() != EStateReadyToRecord )
+ {
+ PRINT((_L("CCMRVideoRecorder::RecordL() not ready")));
+ User::Leave(KErrNotReady);
+ }
+
+ // check exposure setting from camera => decide framerates
+ CheckExposure();
+
+#ifdef _DEBUG
+ iRecordStartTime.UniversalTime();
+ iLastCapture.UniversalTime();
+#endif
+ iNumberOfCapturedFrames = 0;
+ iNumberOfEncodedFrames = 0;
+ iEncoderInputQueueLength = 0;
+ iDriftFrameSkipCount = 0;
+ iAddedFrameDurationCount = 0;
+ iPreviousCameraFrameIndex = 0;
+ iDecSpecInfoLength = 0;
+
+ // reset pause time adjustments variables
+ iTimeWhenPaused = 0;
+ iTotalPausedTime = 0;
+ iSystemClockDelta = 0;
+ iLatestUsedTimeStamp = -iSourceFrameInterval;
+ iLatestAbsoluteTimeStamp = 0;
+ iNumberOfCapturedFrames = 0;
+ iNumberOfVideoOutputBuffers = 0;
+
+ if ( iVideoCodecData->DecoderConfigInfoUsed() )
+ {
+ // Read decoder specific information from the encoder
+ PRINT((_L("CCMRVideoRecorder::RecordL() asking CodingStandardSpecificInitOutputLC from the encoder.")));
+ iDecSpecInfo = iDevVideoRec->CodingStandardSpecificInitOutputLC();
+ if ( iDecSpecInfo )
+ {
+ PRINT((_L("CCMRVideoRecorder::RecordL() CodingStandardSpecificInitOutputLC received.")));
+ // it was placed to cleanupstack, pop it now since it takes some time before
+ // this ptr can be destructed, and if destructed from the stack, there may
+ // be newer objects in the stack at that time. The ptr is also now stored
+ // to member variable which must not be in stack
+ CleanupStack::Pop(iDecSpecInfo);
+
+ // Inform the output that we have new data; it will read it in callback
+ if ( iRequestStatus )
+ {
+ PRINT((_L("CCMRVideoRecorder::RecordL() - completing output request..." )));
+ iOutputThreadHandle.RequestComplete( iRequestStatus, KErrNone );
+ }
+ }
+ else
+ {
+ // This codec should have decoder specific info field in metadata, hence leave with error
+ PRINT((_L("CCMRVideoRecorder::RecordL() no decoder specific info available from the encoder!?!")));
+ User::Leave( KErrNotSupported );
+ }
+ }
+
+ // Start encoding
+ iDevVideoRec->Start();
+ iSkipBuffers = EFalse;
+
+ iInputEnd = EFalse;
+ iStreamEnd = EFalse;
+
+ // Start capturing
+ // StartCapture in case, if we don't use direct capture mode
+ if ( !iDirectCapture )
+ {
+ iSource->StartCapture();
+ }
+
+ SetState(EStateRecording);
+
+ PRINT((_L("CCMRVideoRecorder::RecordL() - END - iRequestStatus: 0x%08x"), iRequestStatus ));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::StopL
+// Stops recording
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::StopL()
+ {
+ PRINT((_L("CCMRVideoRecorder::StopL(), In")))
+
+ if ( (State() == EStateRecording) || (State() == EStatePaused) )
+ {
+ PRINT((_L("CCMRVideoRecorder::StopL(), Stop capture")));
+
+ // Stop capturing
+ // Stop capture only incase, if we don't use direct capture mode
+ if ( !iDirectCapture )
+ {
+ iSource->StopCapture();
+ PRINT((_L("CCMRVideoRecorder::StopL() camera stopped")));
+ }
+
+
+ // Stopping is async => state can't be "recording"
+ SetState(EStateStopping);
+
+ // Stop encoding
+ if ( !iFatalError )
+ {
+ iInputEnd = ETrue;
+ PRINT((_L("CCMRVideoRecorder::StopL(), calling DevVideo::InputEnd().")));
+ iDevVideoRec->InputEnd();
+ // try to free buffers for video adaptation to complete video bitstream / EOS marker.
+ PRINT((_L("CCMRVideoRecorder::StopL(), flushing written frames back to DevVideo.")));
+ iBufferReturnAO->Flush();
+
+ // get all available buffers from devvideo
+ PRINT((_L("CCMRVideoRecorder::StopL(), getting all encoded frames from DevVideo.")));
+ TVideoOutputBuffer* buffer = NULL;
+ for (;;)
+ {
+ TRAPD( err, ( buffer = iDevVideoRec->NextBufferL()));
+ if ( err != KErrNone || buffer == NULL)
+ {
+ break;
+ }
+ // enter restricted area
+ iMutexObj.Wait();
+ // store
+ TInt timestamp = I64INT(buffer->iCaptureTimestamp.Int64());
+ PRINT((_L("CCMRVideoRecorder::StopL(), storing buffer: 0x%x, timestamp:%d"), buffer, timestamp ));
+ iVideoOutputBufferInputQue.AddLast(*buffer);
+ iNumberOfVideoOutputBuffers++;
+
+ // leave restricted area
+ iMutexObj.Signal();
+ }
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::StopL(), Fatal error was reported by devVideo, next step is to delete it")));
+
+ // set state & inform MR => it can return from stop
+ SetState( EStateReadyToRecord );
+
+ // everything ok, inform observer that we are ready for a new recording (iStopping does it too, though).
+ DoSendEventToClient( KCMRRecordingComplete, KErrNone );
+ }
+
+ PRINT((_L("CCMRVideoRecorder::StopL() out, must wait for state change before stop completed")));
+ }
+ else
+ {
+ DoSendEventToClient( KCMRRecordingComplete, KErrNone );
+ PRINT((_L("CCMRVideoRecorder::StopL() out, already stopped")));
+ }
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::RequestBuffersAndWaitEOSL
+// While stopping keep requesting buffers from adaptation and waiting for EOS marker encodc signal
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+
+void CCMRVideoRecorder::RequestBuffersAndWaitEOSL(TInt& aVideoEOSReached)
+ {
+ PRINT((_L("CCMRVideoRecorder::RequestBuffersAndWaitEOSL(), in")))
+ PRINT((_L("CCMRVideoRecorder::RequestBuffersAndWaitEOSL(), flushing written frames back to DevVideo.")));
+ iBufferReturnAO->Flush();
+
+ // get all available buffers from devvideo
+ PRINT((_L("CCMRVideoRecorder::RequestBuffersAndWaitEOSL(), getting all encoded frames from DevVideo.")));
+ TVideoOutputBuffer* buffer = NULL;
+ for (;;)
+ {
+ TRAPD( err, ( buffer = iDevVideoRec->NextBufferL()));
+ if ( err != KErrNone || buffer == NULL)
+ {
+ break;
+ }
+ // enter restricted area
+ iMutexObj.Wait();
+ // store
+ TInt timestamp = I64INT(buffer->iCaptureTimestamp.Int64());
+ PRINT((_L("CCMRVideoRecorder::RequestBuffersAndWaitEOSL(), storing buffer: 0x%x, timestamp:%d"), buffer, timestamp));
+ iVideoOutputBufferInputQue.AddLast(*buffer);
+ iNumberOfVideoOutputBuffers++;
+
+ // leave restricted area
+ iMutexObj.Signal();
+ }
+
+ aVideoEOSReached = iStreamEnd;
+ PRINT((_L("CCMRVideoRecorder::RequestBuffersAndWaitEOSL() out, aVideoEOSReached=%d"), aVideoEOSReached));
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::PauseL
+// Pauses recording
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::PauseL()
+ {
+ PRINT((_L("CCMRVideoRecorder::PauseL(), In")))
+
+ if ( State() != EStateRecording )
+ {
+ return;
+ }
+
+ SetState( EStatePaused );
+
+ // Take the time stamp and later subtract it from real stamps
+ iTimeWhenPaused.UniversalTime();
+ PRINT((_L("CCMRVideoRecorder::PauseL() at %i:%i"),I64LOW(iTimeWhenPaused.Int64()), I64HIGH(iTimeWhenPaused.Int64()) ));
+
+ // Adjust in the clock time changes, so we don't add it in twice during resume
+ iTimeWhenPaused = iTimeWhenPaused + iSystemClockDelta;
+ PRINT((_L("CCMRVideoRecorder::PauseL() at %i:%i"),I64LOW(iTimeWhenPaused.Int64()), I64HIGH(iTimeWhenPaused.Int64()) ));
+
+ // Pause encoding
+ iDevVideoRec->Pause();
+ PRINT((_L("CCMRVideoRecorder::PauseL(), In")))
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::ResumeL
+// Resumes recording
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::ResumeL()
+ {
+ PRINT((_L("CCMRVideoRecorder::ResumeL(), In")))
+
+ if ( State() != EStatePaused )
+ {
+ return;
+ }
+
+ // Resume encoding
+ iDevVideoRec->Resume();
+
+ // measure the time we were paused; remember that this could have been Nth pause
+ TTime current;
+ current.UniversalTime();
+ iTotalPausedTime = iTotalPausedTime.Int64() + current.MicroSecondsFrom(iTimeWhenPaused-iSystemClockDelta).Int64();
+ PRINT((_L("CCMRVideoRecorder::ResumeL() at %i:%i, iTotalPausedTime now %d, iSystemClockDelta now %d"),I64LOW(current.Int64()),
+ I64HIGH(current.Int64()),
+ I64INT(iTotalPausedTime.Int64()),
+ I64INT(iSystemClockDelta.Int64()) ));
+
+ SetState( EStateRecording );
+ PRINT((_L("CCMRVideoRecorder::ResumeL(), Out")))
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::SetVideoCodecL
+// Set video codec.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::SetVideoCodecL(const TDesC8& aMimeType)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() in")));
+ if ( ( State() != EStateOpen ) && ( State() != EStateReadyToRecord ) )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() wrong state")));
+ User::Leave(KErrNotReady);
+ }
+
+ if ( iMimeType == aMimeType )
+ {
+ // the same codec was already selected, no need to change anything
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() out, no change needed. Requested codec already in use.")));
+ return;
+ }
+
+ TInt numerator = KCMRAspectRatioNum;
+ TInt denominator = KCMRAspectRatioDenom;
+ TInt videoCodecLevel = 10; // default: H.263 p0 level 10
+ TBuf8<256> newMimeType;
+ if ( aMimeType == KNullDesC8 )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() Requested NULL video codec, default H.263 Baseline profile, level 10 in use.")));
+ // use default (H.263 p0 level 10); theoretically this could be useful if codec is first set to smth else and then reset to default
+ newMimeType = KCMRMimeTypeH263BaselineProfile; //copy the contents
+ newMimeType += _L8( "; level=10" ); //append level
+ videoCodecLevel = 10;
+ }
+ else {
+ // check the given type first
+ TBuf8<256> string;
+ string = KCMRMimeTypeH263;
+ string += _L8( "*" );
+
+ // the client-class checked the availability of the codec in the system already
+
+ if ( aMimeType.MatchF( (const TDesC8& )string ) != KErrNotFound )
+ {
+ // H.263
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263 requested")));
+ newMimeType = KCMRMimeTypeH263; //copy the contents
+
+ if ( aMimeType.MatchF( _L8("*profile*") ) != KErrNotFound )
+ {
+ // profile given, check if we support it
+ if ( aMimeType.MatchF( _L8("*profile=0*")) != KErrNotFound )
+ {
+ // profile 0 requested
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263, profile 0 requested")));
+ newMimeType += _L8( "; profile=0" ); //append
+ }
+ else if ( aMimeType.MatchF( _L8("*profile=3*")) != KErrNotFound )
+ {
+ // profile 3 requested
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263, profile 3 requested")));
+ newMimeType += _L8( "; profile=3" ); //append
+ }
+ else
+ {
+ // no other profiles supported
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() unsupported profile")));
+ User::Leave(KErrNotSupported);
+ }
+ }
+ else
+ {
+ // no profile is given => assume 0
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263, no profile requested - default to 0.")));
+ newMimeType += _L8( "; profile=0" ); //append
+ }
+
+ if ( aMimeType.MatchF( _L8("*level=10*") ) != KErrNotFound )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263, level 10 requested.")));
+ videoCodecLevel = 10;
+ newMimeType += _L8( "; level=10" ); //append
+ }
+ else if ( aMimeType.MatchF( _L8("*level=20*") ) != KErrNotFound )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263, level 20 requested.")));
+ videoCodecLevel = 20;
+ newMimeType += _L8( "; level=20" ); //append
+ }
+ else if ( aMimeType.MatchF( _L8("*level=30*") ) != KErrNotFound )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263, level 30 requested.")));
+ videoCodecLevel = 30;
+ newMimeType += _L8( "; level=30" ); //append
+ }
+ else if ( aMimeType.MatchF( _L8("*level=40*") ) != KErrNotFound )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263, level 40 requested.")));
+ videoCodecLevel = 40;
+ newMimeType += _L8( "; level=40" ); //append
+ }
+ else if ( aMimeType.MatchF( _L8("*level=45*") ) != KErrNotFound )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263, level 45 requested.")));
+ videoCodecLevel = 45;
+ newMimeType += _L8( "; level=45" ); //append
+ }
+ else if ( aMimeType.MatchF( _L8("*level=50*") ) != KErrNotFound )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263, level 50 requested.")));
+ videoCodecLevel = 50;
+ newMimeType += _L8( "; level=50" ); //append
+ }
+ else if ( aMimeType.MatchF( _L8("*level*") ) != KErrNotFound )
+ {
+ // no other levels supported
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() unsupported level requested.")));
+ User::Leave(KErrNotSupported);
+ }
+ else
+ {
+ // if no level is given assume 10
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263, no level requested - default to 10.")));
+ newMimeType += _L8( "; level=10" ); //append
+ videoCodecLevel = 10;
+ }
+
+ // recreate the codec data object
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.263 recreating codecdata.")));
+ delete iVideoCodecData;
+ iVideoCodecData = NULL;
+ iVideoCodecData = new (ELeave) CCMRVideoCodecDataH263(videoCodecLevel);
+ }
+ else
+ {
+ numerator = KCMRMPEG4AspectRatioNum;
+ denominator = KCMRMPEG4AspectRatioDenom;
+
+ string = KCMRMimeTypeMPEG4V;
+ string += _L8( "*" );
+
+ if ( aMimeType.MatchF( string ) != KErrNotFound )
+ {
+ // MPEG-4 Visual
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4 Visual requested")));
+ newMimeType = KCMRMimeTypeMPEG4VSP; //copy the contents
+ if ( aMimeType.MatchF( _L8("*profile-level-id=8*") ) != KErrNotFound )
+ {
+ // simple profile level 0
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4, simple profile level 0 requested.")));
+ videoCodecLevel = 0;
+ newMimeType += _L8("8");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=9*") ) != KErrNotFound )
+ {
+ // simple profile level 0b
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4, simple profile level 0b requested.")));
+ videoCodecLevel = KCMRMPEG4SPLevel0B;
+ newMimeType += _L8("9");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=1*") ) != KErrNotFound )
+ {
+ // simple profile level 1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4, simple profile level 1 requested.")));
+ videoCodecLevel = 1;
+ newMimeType += _L8("1");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=2*") ) != KErrNotFound )
+ {
+ // simple profile level 2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4, simple profile level 2 requested.")));
+ videoCodecLevel = 2;
+ newMimeType += _L8("2");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=3*") ) != KErrNotFound )
+ {
+ // simple profile level 3
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4, simple profile level 3 requested.")));
+ videoCodecLevel = 3;
+ newMimeType += _L8("3");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4*") ) != KErrNotFound )
+ {
+ // simple profile level 4
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4, simple profile level 4a requested.")));
+ videoCodecLevel = 4;
+ newMimeType += _L8("4");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=5*") ) != KErrNotFound )
+ {
+ // simple profile level 5
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4, simple profile level 5 requested.")));
+ videoCodecLevel = 5;
+ newMimeType += _L8("5");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=6*") ) != KErrNotFound )
+ {
+ // simple profile level 6
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4, simple profile level 6 requested.")));
+ videoCodecLevel = 6;
+ newMimeType += _L8("6");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=*") ) != KErrNotFound )
+ {
+ // no other profile-level ids supported
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() unsupported profile-level requested.")));
+ User::Leave(KErrNotSupported);
+ }
+ else
+ {
+ // Default is level 1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4, no profile requested - defaulting to 1.")));
+ videoCodecLevel = 1;
+ newMimeType += _L8("1");
+ }
+
+ // recreate the codec data object
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() MPEG-4 recreating codecdata.")));
+ delete iVideoCodecData;
+ iVideoCodecData = NULL;
+ iVideoCodecData = new (ELeave) CCMRVideoCodecDataMPEG4(videoCodecLevel);
+ }
+ else
+ {
+ numerator = KCMRMPEG4AspectRatioNum;
+ denominator = KCMRMPEG4AspectRatioDenom;
+
+ string = KCMRMimeTypeH264AVC;
+ string += _L8( "*" );
+
+ if ( aMimeType.MatchF( string ) != KErrNotFound )
+ {
+ // H.264/AVC
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC requested")));
+ newMimeType = KCMRMimeTypeH264AVCProfileId; //copy the contents
+ if ( aMimeType.MatchF( _L8("*profile-level-id=42800A*") ) != KErrNotFound )
+ {
+ // baseline profile level 1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel10;
+ newMimeType += _L8("42800A");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=42900B*") ) != KErrNotFound )
+ {
+ // baseline profile level 1b
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 1b requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel10b;
+ newMimeType += _L8("42900B");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=42800B*") ) != KErrNotFound )
+ {
+ // baseline profile level 1.1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 1.1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel11;
+ newMimeType += _L8("42800B");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=42800C*") ) != KErrNotFound )
+ {
+ // baseline profile level 1.2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 1.2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel12;
+ newMimeType += _L8("42800C");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=42800D*") ) != KErrNotFound )
+ {
+ // baseline profile level 1.3
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 1.3 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel13;
+ newMimeType += _L8("42800D");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=428014*") ) != KErrNotFound )
+ {
+ // baseline profile level 2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel20;
+ newMimeType += _L8("428014");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=428015*") ) != KErrNotFound )
+ {
+ // baseline profile level 2.1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 2.1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel21;
+ newMimeType += _L8("428015");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=428016*") ) != KErrNotFound )
+ {
+ // baseline profile level 2.2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 2.2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel22;
+ newMimeType += _L8("428016");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=42801E*") ) != KErrNotFound )
+ {
+ // baseline profile level 3
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 3 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel30;
+ newMimeType += _L8("42801E");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=42801F*") ) != KErrNotFound )
+ {
+ // baseline profile level 3.1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 3.1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel31;
+ newMimeType += _L8("42801F");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=428020*") ) != KErrNotFound )
+ {
+ // baseline profile level 3.2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 3.2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel32;
+ newMimeType += _L8("428020");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=428028*") ) != KErrNotFound )
+ {
+ // baseline profile level 4
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, baseline profile level 4 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel40;
+ newMimeType += _L8("428028");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D400A*") ) != KErrNotFound )
+ {
+ // main profile level 1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel10;
+ newMimeType += _L8("4D400A");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D500B*") ) != KErrNotFound )
+ {
+ // main profile level 1b
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 1b requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel10b;
+ newMimeType += _L8("4D500B");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D400B*") ) != KErrNotFound )
+ {
+ // main profile level 1.1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 1.1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel11;
+ newMimeType += _L8("4D400B");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D400C*") ) != KErrNotFound )
+ {
+ // main profile level 1.2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 1.2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel12;
+ newMimeType += _L8("4D400C");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D400D*") ) != KErrNotFound )
+ {
+ // main profile level 1.3
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 1.3 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel13;
+ newMimeType += _L8("4D400D");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D4014*") ) != KErrNotFound )
+ {
+ // main profile level 2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel20;
+ newMimeType += _L8("4D4014");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D4015*") ) != KErrNotFound )
+ {
+ // main profile level 2.1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 2.1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel21;
+ newMimeType += _L8("4D4015");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D4016*") ) != KErrNotFound )
+ {
+ // main profile level 2.2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 2.2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel22;
+ newMimeType += _L8("4D4016");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D401E*") ) != KErrNotFound )
+ {
+ // main profile level 3
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 3 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel30;
+ newMimeType += _L8("4D401E");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D401F*") ) != KErrNotFound )
+ {
+ // main profile level 3.1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 3.1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel31;
+ newMimeType += _L8("4D401F");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D4020*") ) != KErrNotFound )
+ {
+ // main profile level 3.2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 3.2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel32;
+ newMimeType += _L8("4D4020");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=4D4028*") ) != KErrNotFound )
+ {
+ // main profile level 4
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, main profile level 4 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel40;
+ newMimeType += _L8("4D4028");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=64400A*") ) != KErrNotFound )
+ {
+ // high profile level 1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel10;
+ newMimeType += _L8("64400A");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=644009*") ) != KErrNotFound )
+ {
+ // high profile level 1b
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 1b requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel10b;
+ newMimeType += _L8("644009");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=64400B*") ) != KErrNotFound )
+ {
+ // high profile level 1.1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 1.1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel11;
+ newMimeType += _L8("64400B");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=64400C*") ) != KErrNotFound )
+ {
+ // high profile level 1.2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 1.2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel12;
+ newMimeType += _L8("64400C");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=64400D*") ) != KErrNotFound )
+ {
+ // high profile level 1.3
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 1.3 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel13;
+ newMimeType += _L8("64400D");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=644014*") ) != KErrNotFound )
+ {
+ // high profile level 2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel20;
+ newMimeType += _L8("644014");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=644015*") ) != KErrNotFound )
+ {
+ // high profile level 2.1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 2.1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel21;
+ newMimeType += _L8("644015");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=644016*") ) != KErrNotFound )
+ {
+ // high profile level 2.2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 2.2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel22;
+ newMimeType += _L8("644016");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=64401E*") ) != KErrNotFound )
+ {
+ // high profile level 3
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 3 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel30;
+ newMimeType += _L8("64401E");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=64401F*") ) != KErrNotFound )
+ {
+ // high profile level 3.1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 3.1 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel31;
+ newMimeType += _L8("64401F");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=644020*") ) != KErrNotFound )
+ {
+ // high profile level 3.2
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 3.2 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel32;
+ newMimeType += _L8("644020");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=644028*") ) != KErrNotFound )
+ {
+ // high profile level 4
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, high profile level 4 requested.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel40;
+ newMimeType += _L8("644028");
+ }
+ else if ( aMimeType.MatchF( _L8("*profile-level-id=*") ) != KErrNotFound )
+ {
+ // no other profile-level ids supported
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() unsupported profile-level requested.")));
+ User::Leave(KErrNotSupported);
+ }
+ else
+ {
+ // Default is level 1
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC, no profile requested - defaulting to 1.")));
+ videoCodecLevel = KCMRH264AVCCodecLevel10;
+ newMimeType += _L8("42800A");
+ }
+
+ // recreate the codec data object
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() H.264/AVC recreating codecdata.")));
+ delete iVideoCodecData;
+ iVideoCodecData = NULL;
+ iVideoCodecData = new (ELeave) CCMRVideoCodecDataH264AVC(videoCodecLevel);
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() Unknown mimetype requested.")));
+ // unknown mimetype
+ }
+ }
+ }
+ }
+
+ // successfully interpreted the input mime type
+ if ( newMimeType != iMimeType )
+ {
+ iMimeType = newMimeType; //copy the contents
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() new supported video codec requested - updating available encoders.")));
+ iAvailableVideoEncoders.Reset();
+ if ( iPreferredEncoderUID != KNullUid )
+ {// We have preferred encoder UID from client - override encoder search and use it instead.
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() skipping encoder search. Using API user encoder: %d "), iPreferredEncoderUID.iUid));
+ iAvailableVideoEncoders.AppendL(iPreferredEncoderUID);
+ }
+ else if ( iConfig &&
+ ( iConfig->IsICMConfigDataAvailable() ) &&
+ ( iConfig->VideoQualitySettings().iVideoEncoderUID != KNullUid ) )
+ {// Video quality set has set UID value - override encoder search and use it instead.
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() skipping encoder search. Using ICM config encoder: %d "), iPreferredEncoderUID.iUid));
+ iAvailableVideoEncoders.AppendL(iPreferredEncoderUID);
+ }
+ else
+ {
+ iDevVideoRec->FindEncodersL(iMimeType, 0 /* aPreProc */, iAvailableVideoEncoders, EFalse );
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() search found %d encoder(s)"), iAvailableVideoEncoders.Count() ));
+ }
+ iConfig->SetVideoCodec(iMimeType);
+ iConfig->SetVideoPixelAspectRatio(numerator, denominator);
+
+ UpdateSupportedVideoFrameSizesRates();
+
+ // user has to call this->PrepareL() now that a setting has been changed !!!
+ // allow to start recording only when the state is "EReadyToRecord"
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() codec set, prepare needed before recording.")));
+ SetState(EStateOpen);
+ DoSendEventToClient( KCMRPrepareNeeded, iErrorCode ); // use iErrorCode in case we had stored error; it may be KErrNone too
+ iErrorCode = KErrNone;
+ }
+
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodecL() out")));
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::GetVideoCodec
+// Get the used video codec.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::GetVideoCodecL( TDes8& aVideoMimeType ) const
+ {
+ aVideoMimeType = iMimeType;
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetTargetBitRateL
+// Sets new target bitrate
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetTargetBitRateL(TInt aBitRate)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetTargetBitRate, aBitrate = % d"),aBitRate));
+
+ if ( aBitRate == KMMFVariableVideoBitRate )
+ {
+ // Variable rate requested; normalize the bit-rate (temporarily only in this function)
+ // but set a mode
+ aBitRate = iVideoCodecData->MaxBitRate();
+ iBitRateMode = EBitRateVariable;
+ }
+ else
+ {
+ iBitRateMode = EBitRateConstant;
+ }
+
+ // check that values are reasonable; check max bitrate only when the level is expected to be used for MMS
+ if ( (aBitRate < KCMRMinAcceptedBitRate) || ((iVideoCodecData->LevelForMMS()) && (aBitRate > iVideoCodecData->MaxBitRate())) )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetTargetBitRateL() illegal bitrate")));
+ User::Leave(KErrArgument);
+ }
+
+ // call base class implementation
+ CCMRRecorderBase::SetTargetBitRateL(aBitRate);
+
+ // inform the max bitrate to the sink
+ User::LeaveIfError( iOutput->SetMaxVideoBitRate( aBitRate ) );
+
+ //inform the estimated average bitrate to sink; use a HW-specific scaler to estimate the average
+ TReal videoBitrateScaler = 0;
+ if ( iConfig )
+ {
+ videoBitrateScaler = iConfig->PluginSettings().iCMRAvgVideoBitRateScaler;
+ }
+
+ // make sure we never get scaler value 0 (ends up Div0 error)
+ if ( videoBitrateScaler == 0 )
+ {
+ videoBitrateScaler = KCMRAvgVideoBitRateScaler;
+ }
+
+ TInt br = static_cast<TInt>(aBitRate*videoBitrateScaler);
+ User::LeaveIfError( iOutput->SetAverageVideoBitRate( br ) );
+
+ if ( StateRequiresDynamicSetting() )
+ {
+ // we have prepared => new value must be given via dynamic methods
+ FillRateControlOptions( iRateControlOptions );
+ iDevVideoRec->SetRateControlOptions(0, iRateControlOptions); // only base layer (layer 0) supported
+ }
+ else
+ {
+ // we are not recording or prepared => value will be given forward in prepare
+ }
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::TargetBitRateL
+// Gets current target bitrate
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::TargetBitRateL(TInt& aBitRate)
+ {
+ PRINT((_L("CCMRVideoRecorder::TargetBitRate")));
+
+
+ if ( iBitRateMode == EBitRateConstant )
+ {
+ aBitRate = iTargetBitRate;
+ }
+ else
+ {
+ aBitRate = KMMFVariableVideoBitRate;
+ }
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetFrameSizeL
+// Sets new input & output frame size
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetFrameSizeL(const TSize& aSize)
+ {
+
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL(), In, size: %dx%d"), aSize.iWidth, aSize.iHeight ));
+
+ // allow settings only when in "open" or "readytorecord" states
+ if ( ( State() != EStateOpen ) && ( State() != EStateReadyToRecord ) )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() wrong state")));
+ User::Leave(KErrNotReady);
+ }
+
+ // check that size is acceptable
+ if ( !iVideoCodecData->MaxFrameSize( aSize ) )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() illegal frame size")));
+ User::Leave(KErrArgument);//could be also KErrNotSupported
+ }
+
+ TSize size;
+ TInt i = 0;
+ TInt j = 0;
+ TInt k = 0;
+
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() looking match for frame size: %d x %d from camera"), aSize.iWidth, aSize.iHeight ));
+
+ // Some explanation of the logic here. Camera API must support the same rates for all formats, since there
+ // is just a single variable in the info class to check the number of rates.
+ // But the indices may differ and that's why we need enumerations
+ if ( iCameraInfo.iVideoFrameFormatsSupported & CCamera::EFormatYUV420Planar )
+ {
+ // check YUV420 planar
+ for ( i = 0; i < iCameraInfo.iNumVideoFrameSizesSupported; i++ )
+ {
+ iSource->EnumerateVideoFrameSizes(size, i, CCamera::EFormatYUV420Planar);
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() Camera EFormatYUV420Planar %d x %d found from camera"), size.iWidth, size.iHeight ));
+
+ if ( size == aSize )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() Camera EFormatYUV420Planar %d x %d Matched."), size.iWidth, size.iHeight ));
+ iSizeIndex420 = i;
+ break;
+ }
+ }
+ }
+ else
+ {
+ i = iCameraInfo.iNumVideoFrameSizesSupported;
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() Camera doesn't support EFormatYUV420Planar")));
+ }
+
+ if ( iCameraInfo.iVideoFrameFormatsSupported & CCamera::EFormatYUV422 )
+ {
+ // check YUV422 interleaved
+ for ( j = 0; j < iCameraInfo.iNumVideoFrameSizesSupported; j++ )
+ {
+ iSource->EnumerateVideoFrameSizes(size, j, CCamera::EFormatYUV422 );
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() Camera EFormatYUV422 %d x %d found from camera"), size.iWidth, size.iHeight ));
+
+ if ( size == aSize )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() Camera EFormatYUV422 %d x %d Matched."), size.iWidth, size.iHeight ));
+ iSizeIndex422 = j;
+ break;
+ }
+ }
+ }
+ else
+ {
+ j = iCameraInfo.iNumVideoFrameSizesSupported;
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() Camera doesn't support EFormatYUV422")));
+ }
+
+ // Search if we have direct capture encoder available -> no need to care about camera supported sizes.
+ if ( iAvailableVideoFrameSizesRates.Count() )
+ {
+ // check direct capture encoder size array
+ for ( k = 0; k < iAvailableVideoFrameSizesRates.Count(); k++ )
+ {
+ // All target resolutions that are below higher resolution found from encoders are accepted.
+ if ( (iAvailableVideoFrameSizesRates[k].iPictureSize.iWidth >= aSize.iWidth) &&
+ (iAvailableVideoFrameSizesRates[k].iPictureSize.iHeight >= aSize.iHeight) )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() DirectCapture encoder %d x %d Matched."), aSize.iWidth, aSize.iHeight ));
+ iSizeIndexDCEncoder = k;
+ break;
+ }
+ }
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() no directcapture encoder available for given size.")));
+ }
+
+ if ( (i == iCameraInfo.iNumVideoFrameSizesSupported) &&
+ (j == iCameraInfo.iNumVideoFrameSizesSupported) &&
+ (k == iAvailableVideoFrameSizesRates.Count()) )
+ {
+ // Requested size not supported by camera API or the directcapture encoder
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL(), Camera and/or direct capture encoder does not support this resolution")));
+ User::Leave(KErrNotSupported);
+ }
+
+ iFrameSize = aSize;
+ iOutput->SetVideoFrameSize(aSize);
+
+ // user has to call this->PrepareL() now that a setting has been changed !!!
+ // allow to start recording only when the state is "EReadyToRecord"
+ SetState(EStateOpen);
+ DoSendEventToClient( KCMRPrepareNeeded, iErrorCode ); // use iErrorCode in case we had stored error; it may be KErrNone too
+ iErrorCode = KErrNone;
+
+ // update MaxFrameRate to new frame size
+ iMaxFrameRate4GivenSize = iVideoCodecData->MaxFrameRate( iFrameSize );
+
+ // framerate is tied with framesize; if it was set before update it accordingly
+ // this may be overridden by user e.g. with a higher rate; this sets the rates <= previous encoding rate
+ if ( (iRateIndex420 >= 0) || (iRateIndex422 >= 0) || (iRateIndexDCEncoder >= 0) )
+ {
+ // must use iEncodingFrameRate, since other rates may raise encoding rate higher than wanted; this allows camera frame rate to be higher
+ TInt success = KErrNone;
+ do
+ {
+ TRAP( success, SetFrameRateL( iEncodingFrameRate ));
+ if ( success == KErrNone )
+ {
+ break;
+ }
+ // if picture size was increased, we may need to decrease framerate
+ iEncodingFrameRate--;
+ } while ( iEncodingFrameRate > 0 );
+ if ( success != KErrNone )
+ {
+ // This framesize - framerate combination not possible
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() illegal frame size - rate combination")));
+ User::Leave(success);
+ }
+ }
+
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL(), Out")));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::FrameSizeL
+// Gets current frame size
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::FrameSizeL(TSize& aSize) const
+ {
+ PRINT((_L("CCMRVideoRecorder::FrameSizeL()")));
+ aSize = iFrameSize;
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetFrameRateL
+// Sets new target frame rate
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetFrameRateL(TReal32 aFrameRate)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetFrameRate(), requested rate %f"), aFrameRate));
+
+ // Size has to be set before setting frame rate !!!
+ if ( (State() == EStateNone) || ((iSizeIndex420 < 0) && (iSizeIndex422 < 0) && (iSizeIndexDCEncoder < 0)) )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetFrameRateL() wrong state")));
+ User::Leave(KErrNotReady);
+ }
+
+ // check that values are reasonable
+ if ( (aFrameRate <= 0) || (aFrameRate > iVideoCodecData->MaxFrameRate(iFrameSize)) )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetFrameSizeL() illegal frame rate")));
+ User::Leave(KErrArgument);
+ }
+
+ // encoding frame rate is always the requested one
+ if ( aFrameRate <= iMaxFrameRate4GivenSize ) // actually iMaxFrameRate4GivenSize is valid only after prepare
+ {
+ // requested rate looks ok
+ iEncodingFrameRate = aFrameRate;
+ }
+ else if ( iEncodingFrameRate < iMaxFrameRate4GivenSize )
+ {
+ // too high rate requested, but the current rate is still lower than max possible, so we can set the rate higher anyway
+ iEncodingFrameRate = static_cast<TReal32>(iMaxFrameRate4GivenSize);
+ PRINT((_L("CCMRVideoRecorder::SetFrameRate(), requested rate is too high for the used encoder, but set it to this rate: %f"), iMaxFrameRate4GivenSize));
+ }
+ else
+ {
+ // The change has no effect; the used encoder is already set to max possible rate and can't be configured for the requested rate
+ // for now camera api capability is not checked; e.g. if H.263 level is 20 => framerate could be 30fps for QCIF but if max encoder
+ // impl. rate is 10fps, we don't leave, but just ignore
+ PRINT((_L("CCMRVideoRecorder::SetFrameRate(), requested rate is too high for the used encoder, max rate %f"), iMaxFrameRate4GivenSize));
+ return;
+ }
+
+ if ( StateRequiresDynamicSetting() )
+ {
+ // we are running, change only encoding rate now
+
+ // store the requested rate also, to be taken into use in the next prepare
+ if ( !iNightMode )
+ {
+ iRequestedFrameRate = aFrameRate;
+ }
+
+ if ( iEncodingFrameRate > iSourceFrameRate )
+ {
+ // can't be higher than capture rate, limit it
+ iEncodingFrameRate = iSourceFrameRate;
+ }
+
+ // set the rate control params
+ FillRateControlOptions( iRateControlOptions );
+ iDevVideoRec->SetRateControlOptions(0, iRateControlOptions); // only base layer (layer 0) supported
+ }
+ else
+ {
+ // we are in initialization phase, change also camera rate
+
+ TReal32 rate;
+ TInt i;
+
+ iRateIndex420 = -1; //reset just in case, needed to determine if the loop below was successful
+ iRateIndex422 = -1; //reset just in case, needed to determine if the loop below was successful
+ iRateIndexDCEncoder = -1;
+
+ // Some explanation of the logic here. Camera API must support the same rates for all formats, since there
+ // is just a single variable in the info class to check the number of rates.
+ // But the indices may differ and that's why we need enumerations
+ // Since we prefer YUV422, it can override the member variables.
+
+ if ( iCameraInfo.iVideoFrameFormatsSupported & CCamera::EFormatYUV420Planar )
+ {
+ // check YUV420 planar
+
+ // search for an exact match from camera's supported rates
+ for ( i = 0; i < iCameraInfo.iNumVideoFrameRatesSupported; i++ )
+ {
+
+ iSource->EnumerateVideoFrameRates(rate, i, CCamera::EFormatYUV420Planar, iSizeIndex420,
+ CCamera::EExposureAuto);
+
+ if ( rate == aFrameRate )
+ {
+ iRateIndex420 = i;
+ iSourceFrameRate = aFrameRate;
+ iSourceFrameInterval = convertFrameRateToInterval(iSourceFrameRate);
+ PRINT((_L("CCMRVideoRecorder::SetFrameRate, asked framerate found")));
+ break;
+ }
+ else if ( rate > aFrameRate )
+ {
+ // this is higher but could be used (encoder can match the rates)
+ // Check the other rates too if a better is found
+ iRateIndex420 = i;
+ iSourceFrameRate = rate;
+ iSourceFrameInterval = convertFrameRateToInterval(iSourceFrameRate);
+ PRINT((_L("CCMRVideoRecorder::SetFrameRate, a higher source framerate found")));
+ }
+ }
+ }
+
+ if ( iCameraInfo.iVideoFrameFormatsSupported & CCamera::EFormatYUV422 )
+ {
+ // check YUV422 interleaved
+
+ // search for an exact match from camera's supported rates
+ for ( i = 0; i < iCameraInfo.iNumVideoFrameRatesSupported; i++ )
+ {
+ iSource->EnumerateVideoFrameRates(rate, i, CCamera::EFormatYUV422, iSizeIndex422,
+ CCamera::EExposureAuto);
+
+ if ( rate == aFrameRate )
+ {
+ iRateIndex422 = i;
+ iSourceFrameRate = aFrameRate;
+ iSourceFrameInterval = convertFrameRateToInterval(iSourceFrameRate);
+ PRINT((_L("CCMRVideoRecorder::SetFrameRate, asked framerate found")));
+ break;
+ }
+ else if ( rate > aFrameRate )
+ {
+ // this is higher but could be used (encoder can match the rates)
+ // Check the other rates too if a better is found
+ iRateIndex422 = i;
+ iSourceFrameRate = rate;
+ iSourceFrameInterval = convertFrameRateToInterval(iSourceFrameRate);
+ PRINT((_L("CCMRVideoRecorder::SetFrameRate, a higher source framerate found")));
+ }
+ }
+ }
+
+ if ( iAvailableVideoFrameSizesRates.Count() && (iSizeIndexDCEncoder >= 0) && (iSizeIndexDCEncoder < iAvailableVideoFrameSizesRates.Count()) )
+ {
+ // check Direct capture encoder supported rates.
+
+ // search for an exact match from encoders supported rates
+ for ( i = 0; i < iAvailableVideoFrameSizesRates.Count(); i++ )
+ {
+ if ( (iAvailableVideoFrameSizesRates[i].iPictureRate == aFrameRate) &&
+ (iAvailableVideoFrameSizesRates[i].iPictureSize == iAvailableVideoFrameSizesRates[iSizeIndexDCEncoder].iPictureSize) )
+ {
+ iRateIndexDCEncoder = i;
+ iSizeIndexDCEncoder = i;
+ iSourceFrameRate = aFrameRate;
+ iSourceFrameInterval = convertFrameRateToInterval(iSourceFrameRate);
+ PRINT((_L("CCMRVideoRecorder::SetFrameRate, asked framerate found")));
+ break;
+ }
+ else if ( (iAvailableVideoFrameSizesRates[i].iPictureRate >= aFrameRate) &&
+ (iAvailableVideoFrameSizesRates[i].iPictureSize.iWidth >= iAvailableVideoFrameSizesRates[iSizeIndexDCEncoder].iPictureSize.iWidth ) &&
+ (iAvailableVideoFrameSizesRates[i].iPictureSize.iHeight >= iAvailableVideoFrameSizesRates[iSizeIndexDCEncoder].iPictureSize.iHeight ) )
+ {
+ // this is higher but could be used (encoder can match the rates)
+ // Check the other rates too if a better is found
+ iRateIndexDCEncoder = i;
+ iSizeIndexDCEncoder = i;
+ iSourceFrameRate = aFrameRate;
+ iSourceFrameInterval = convertFrameRateToInterval(iSourceFrameRate);
+ PRINT((_L("CCMRVideoRecorder::SetFrameRate, a higher source framerate found: %dx%d at %f"), iAvailableVideoFrameSizesRates[i].iPictureSize.iWidth,
+ iAvailableVideoFrameSizesRates[i].iPictureSize.iHeight,
+ iAvailableVideoFrameSizesRates[i].iPictureRate ));
+ }
+ }
+ }
+
+
+ if ( (iRateIndex420 < 0) && (iRateIndex422 < 0) && (iRateIndexDCEncoder < 0) )
+ {
+ // there is no suitable frame rate available, not even a higher one, return error
+ PRINT((_L("CCMRVideoRecorder::SetFrameRate, no suitable framerate found from Camera API and/or direct capture encoder.")));
+ User::Leave(KErrNotSupported);
+ }
+
+ // set to the actual rate to prevent trying set the rate again from PrepareL
+ iRequestedFrameRate = iSourceFrameRate;
+
+ // user has to call this->PrepareL() now that a setting has been changed !!!
+ // allow to start recording only when the state is "EReadyToRecord"
+ if ( State() != EStateOpen )
+ {
+ SetState(EStateOpen);
+ DoSendEventToClient( KCMRPrepareNeeded, iErrorCode ); // use iErrorCode in case we had stored error; it may be KErrNone too
+ iErrorCode = KErrNone;
+ }
+ }
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::FrameRateL
+// Get the used encoding frame rate
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::FrameRateL(TReal32& aFrameRate) const
+ {
+ PRINT((_L("CCMRVideoRecorder::FrameRateL()")));
+ aFrameRate = iEncodingFrameRate;
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetVideoCodingOptionsL
+// Set misc video coding options
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetVideoCodingOptionsL(const TCCMRVideoCodingOptions& aOptions)
+ {
+ if ( ( State() != EStateOpen ) && ( State() != EStateReadyToRecord ) )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoCodingOptionsL() wrong state")));
+ User::Leave( KErrNotReady );
+ }
+ if ( iMinRandomAccessPeriodInSeconds != KCMRUseDefault )
+ {
+ iMinRandomAccessPeriodInSeconds = aOptions.iMinRandomAccessPeriodInSeconds;
+ }
+
+ iVideoCodecData->SetVideoCodingOptionsL(aOptions);
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetVideoRateControlOptionsL
+// Set video rate control options
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetVideoRateControlOptionsL(const TRateControlOptions& aOptions)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() in")));
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() TBitrateControlType iControl: %d"), aOptions.iControl));
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() iBitrate: %d"), aOptions.iBitrate));
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() iPictureQuality: %d"), aOptions.iPictureQuality));
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() iPictureRate: %f"), aOptions.iPictureRate));
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() iQualityTemporalTradeoff: %f"), aOptions.iQualityTemporalTradeoff));
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() iLatencyQualityTradeoff: %f"), aOptions.iLatencyQualityTradeoff));
+
+ if ( !StateRequiresDynamicSetting() )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() illegal state for dynamic settings")));
+ User::Leave(KErrNotReady);
+ }
+
+ // Handle new bitrate value:
+ if ( (aOptions.iBitrate < KCMRMinAcceptedBitRate) || ((iVideoCodecData->LevelForMMS()) && (aOptions.iBitrate > iVideoCodecData->MaxBitRate())) )
+ {// check that values are reasonable; check max bitrate only when the level is expected to be used for MMS
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() illegal bitrate")));
+ User::Leave(KErrArgument);
+ }
+ CCMRRecorderBase::SetTargetBitRateL(aOptions.iBitrate);
+ // inform the max bitrate to the sink
+ User::LeaveIfError( iOutput->SetMaxVideoBitRate( aOptions.iBitrate ) );
+ //inform the estimated average bitrate to sink; use a HW-specific scaler to estimate the average
+ TReal videoBitrateScaler = 0;
+ if ( iConfig )
+ {
+ videoBitrateScaler = iConfig->PluginSettings().iCMRAvgVideoBitRateScaler;
+ }
+ if ( videoBitrateScaler == 0 )
+ {// make sure we never get scaler value 0 (ends up Div0 error)
+ videoBitrateScaler = KCMRAvgVideoBitRateScaler;
+ }
+ TInt br = static_cast<TInt>(aOptions.iBitrate*videoBitrateScaler);
+ User::LeaveIfError( iOutput->SetAverageVideoBitRate( br ) );
+
+
+ //Handle new framerate value:
+ if ( (aOptions.iPictureRate <= 0) || (aOptions.iPictureRate > iVideoCodecData->MaxFrameRate(iFrameSize)) )
+ {// check that values are reasonable
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() illegal frame rate, %f"), aOptions.iPictureRate));
+ User::Leave(KErrArgument);
+ }
+ if ( aOptions.iPictureRate <= iMaxFrameRate4GivenSize ) // actually iMaxFrameRate4GivenSize is valid only after prepare
+ {// encoding frame rate is always the requested one
+ iEncodingFrameRate = aOptions.iPictureRate; // requested rate looks ok
+ }
+ else if ( iEncodingFrameRate < iMaxFrameRate4GivenSize )
+ {// too high rate requested, but the current rate is still lower than max possible, so we can set the rate higher anyway
+ iEncodingFrameRate = static_cast<TReal32>(iMaxFrameRate4GivenSize);
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL(), requested rate is too high for the used encoder, but set it to this rate: %f"), iMaxFrameRate4GivenSize));
+ }
+ if ( !iNightMode )
+ {// store the requested rate also, to be taken into use in the next prepare
+ iRequestedFrameRate = aOptions.iPictureRate;
+ }
+ if ( iEncodingFrameRate > iSourceFrameRate )
+ {// can't be higher than capture rate, limit it
+ iEncodingFrameRate = iSourceFrameRate;
+ }
+
+ iRateControlOptions = aOptions;
+ iRateControlOptions.iPictureRate = iEncodingFrameRate;
+ if ( iDevVideoRec )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() settings stored and sending them to Devvideo.")));
+ iDevVideoRec->SetRateControlOptions(0, iRateControlOptions); // only base layer (layer 0) supported
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() no DevVideoRec! Leaving KErrNotReady -18")));
+ User::Leave(KErrNotReady);
+ }
+ PRINT((_L("CCMRVideoRecorder::SetVideoRateControlOptionsL() out")));
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::GetVideoRateControlOptionsL
+// Get video rate control options
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::GetVideoRateControlOptionsL(TRateControlOptions& aOptions)
+ {
+ PRINT((_L("CCMRVideoRecorder::GetVideoRateControlOptionsL() in")));
+ PRINT((_L("CCMRVideoRecorder::GetVideoRateControlOptionsL() TBitrateControlType iControl: %d"), iRateControlOptions.iControl));
+ PRINT((_L("CCMRVideoRecorder::GetVideoRateControlOptionsL() iBitrate: %d"), iRateControlOptions.iBitrate));
+ PRINT((_L("CCMRVideoRecorder::GetVideoRateControlOptionsL() iPictureQuality: %d"), iRateControlOptions.iPictureQuality));
+ PRINT((_L("CCMRVideoRecorder::GetVideoRateControlOptionsL() iPictureRate: %f"), iRateControlOptions.iPictureRate));
+ PRINT((_L("CCMRVideoRecorder::GetVideoRateControlOptionsL() iQualityTemporalTradeoff: %f"), iRateControlOptions.iQualityTemporalTradeoff));
+ PRINT((_L("CCMRVideoRecorder::GetVideoRateControlOptionsL() iLatencyQualityTradeoff: %f"), iRateControlOptions.iLatencyQualityTradeoff));
+
+ if ( !StateRequiresDynamicSetting() )
+ {
+ PRINT((_L("CCMRVideoRecorder::GetVideoRateControlOptionsL() illegal state for dynamic settings")));
+ User::Leave(KErrNotReady);
+ }
+ aOptions = iRateControlOptions;
+ PRINT((_L("CCMRVideoRecorder::GetVideoRateControlOptionsL() out")));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetPreferredVideoEncoderL
+// Set video encoder using its UID. Usage optional.
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetPreferredVideoEncoderL(TUid& aEncoder)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetPreferredVideoEncoderL() in")));
+ iPreferredEncoderUID = aEncoder;
+
+ if ( iPreferredEncoderUID != KNullUid )
+ {// We have preferred encoder UID from client - override encoder search and use it instead.
+ PRINT((_L("CCMRVideoRecorder::SetPreferredVideoEncoderL() skipping encoder search. Using API user encoder: %d "), iPreferredEncoderUID.iUid));
+ iAvailableVideoEncoders.Reset();
+ iAvailableVideoEncoders.AppendL(iPreferredEncoderUID);
+ UpdateSupportedVideoFrameSizesRates();
+ }
+
+ PRINT((_L("CCMRVideoRecorder::SetPreferredVideoEncoderL() out")));
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetPreferredVideoEncapsulationL
+// Set video encoder output format encapsulation. Usage optional.
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetPreferredVideoEncapsulationL(TVideoDataUnitEncapsulation aCapsulation)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetPreferredVideoEncapsulationL() in")));
+ iPreferredEncapsulation = aCapsulation;
+ iPreferredEncapsulationSet = ETrue;
+ PRINT((_L("CCMRVideoRecorder::SetPreferredVideoEncapsulationL() out")));
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::SetSegmentTargetSize
+// Set video segment target size
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::SetSegmentTargetSizeL(TUint aLayer, TUint aSizeBytes, TUint aSizeMacroblocks)
+ {
+ PRINT((_L("CCMRVideoRecorder::SetSegmentTargetSize() in")));
+ PRINT((_L("CCMRVideoRecorder::SetSegmentTargetSize() aLayer: %d"), aLayer));
+ PRINT((_L("CCMRVideoRecorder::SetSegmentTargetSize() aSizeBytes: %d"), aSizeBytes));
+ PRINT((_L("CCMRVideoRecorder::SetSegmentTargetSize() aSizeMacroblocks: %d"), aSizeMacroblocks));
+
+ if ( !StateRequiresDynamicSetting() )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetSegmentTargetSize() illegal state for dynamic settings")));
+ User::Leave(KErrNotReady);
+ }
+
+ if ( iDevVideoRec )
+ {
+ PRINT((_L("CCMRVideoRecorder::SetSegmentTargetSize() sending SetSegmentTargetSize to Devvideo.")));
+ iDevVideoRec->SetSegmentTargetSize(aLayer, aSizeBytes, aSizeMacroblocks); // only base layer (layer 0) supported
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::SetSegmentTargetSize() no DevVideoRec! Leaving KErrNotReady -18")));
+ User::Leave(KErrNotReady);
+ }
+ PRINT((_L("CCMRVideoRecorder::SetSegmentTargetSize() out")));
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::AdjustTimeStampsL
+// Adjust time stamps of video
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::AdjustTimeStampsL(const TInt aAdjustmentMs)
+ {
+ if ( State() != EStateRecording )
+ {
+ PRINT((_L("CCMRVideoRecorder::AdjustTimeStampsL() wrong state")));
+ User::Leave( KErrNotReady );
+ }
+
+ iAdjustmentTimeUs = TInt64( aAdjustmentMs * 1000 );
+
+ PRINT((_L("CCMRVideoRecorder::AdjustTimeStampsL() by %d us"),I64INT(iAdjustmentTimeUs)));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::MvsoReserveComplete
+// Called by camera API when camera API has been reserved
+// for exclusive use
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::MvsoReserveComplete(TInt aError)
+ {
+ PRINT((_L("CCMRVideoRecorder::MvsoReserveComplete() in")));
+
+ // Should not be called, if we use direct capture mode
+ if (iDirectCapture)
+ {
+ VRASSERT(0);
+ }
+
+ // need to set this already here if returns unsuccesfully, to enable error handling when encoder init completes
+ iSourceInitComplete = ETrue;
+
+ if ( iEncoderInitComplete && (iErrorCode != KErrNone) )
+ {
+ // there was an error in the encoder initialisation, but the reporting was
+ // waiting for the completion of the source initialisation => report it now
+ PRINT((_L("CCMRVideoRecorder::MvsoReserveComplete() but error in video encoder: %d"), iErrorCode));
+ DoSendEventToClient( KCMREncoderInitError, iErrorCode );
+ iErrorCode = KErrNone;
+ return;
+ }
+
+ if ( aError != KErrNone )
+ {
+ if ( iEncoderInitComplete )
+ {
+ PRINT((_L("CCMRVideoRecorder::MvsoReserveComplete() with error: %d"), aError));
+ DoSendEventToClient( KCMRCameraReserveError, aError );
+ }
+ else
+ {
+ // report only when encoder is completed too
+ PRINT((_L("CCMRVideoRecorder::MvsoReserveComplete() with error: %d, but must wait for encoder init completion"), aError));
+ iErrorCode = aError;
+ }
+ return;
+ }
+
+ TInt error;
+
+ TRAP(error, iSource->PrepareCaptureL(iVideoFormat, iSizeIndex, iRateIndex,
+ iNumCameraBuffers, KCMRNumFramesInCameraBuffer));
+
+ PRINT((_L("CCMRVideoRecorder::MvsoReserveComplete() camera prepared, error %d"),error));
+ if ( error != KErrNone )
+ {
+ if ( iEncoderInitComplete )
+ {
+ DoSendEventToClient( KCMRCameraPrepareError, error );
+ }
+ else
+ {
+ // report only when encoder is completed too
+ iErrorCode = error;
+ }
+ return;
+ }
+
+
+ if ( iEncoderInitComplete )
+ {
+ SetState(EStateReadyToRecord);
+
+ // everything ok, inform observer that we are prepared.
+ DoSendEventToClient( KCMRPrepareComplete, KErrNone );
+ }
+
+ PRINT((_L("CCMRVideoRecorder::MvsoReserveComplete() out")));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::MvsoFrameBufferReady
+// Called by camera API when a frame (or several frames)
+// has been captured
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::MvsoFrameBufferReady(MFrameBuffer* aFrameBuffer, TInt aError)
+ {
+ // Should not be called, if we use direct capture mode
+ if (iDirectCapture)
+ {
+ VRASSERT(0);
+ }
+
+ if ( aError != KErrNone || aFrameBuffer == NULL )
+ {
+ // some errors, inform observer and return
+ DoSendEventToClient( KCMRCameraCaptureError, aError );
+ if ( aFrameBuffer )
+ {
+ // in case of errors, aFrameBuffer may be NULL
+ aFrameBuffer->Release();
+ }
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady with error %d"), aError));
+ return;
+ }
+
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady in %x, frame & time & systemdrift\t%d\t%d\t%d"),aFrameBuffer,
+ aFrameBuffer->iIndexOfFirstFrameInBuffer,
+ I64INT(aFrameBuffer->iElapsedTime.Int64()),
+ I64INT(iSystemClockDelta.Int64())
+ ));
+
+#ifdef VIDEO_FILE_OUTPUT
+
+ // Write the frame to a file for testing purposes
+ TInt error = KErrNone;
+ TRAPD(error2, error = iOutputFile.Write( *(aFrameBuffer->DataL(0)) ));
+
+ // release frame
+ aFrameBuffer->Release();
+
+ if (error != KErrNone || error2 != KErrNone)
+ {
+ DoSendEventToClient( KCMRRunTimeError, error );
+ }
+
+
+#else
+ // the real deal
+
+ if ( (State() != EStateRecording) || (iErrorCode != KErrNone) )
+ {
+ // we are paused or stopping or there was an error => return the framebuffer immediately
+ // if system clock has been changed during pause take that account in future frame timestamp calculations.
+ if ( (((aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64()) < iLatestAbsoluteTimeStamp.Int64()) ||
+ ((aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64()) > (iLatestAbsoluteTimeStamp.Int64() + 2*iSourceFrameInterval))) &&
+ (aFrameBuffer->iIndexOfFirstFrameInBuffer == iPreviousCameraFrameIndex + 1) )
+ {
+ // something has changed system clock during pause, add cumulatively to drift delta.
+ iSystemClockDelta = (iLatestAbsoluteTimeStamp.Int64() + iSourceFrameInterval) -
+ (aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64())
+ + iSystemClockDelta.Int64();
+
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady() System time change: aFrameBuffer->iElapsedTime = %d, iSystemClockDelta = %d, iLatestAbsoluteTimeStamp = %d, iSourceFrameInterval = %d"),
+ I64INT(aFrameBuffer->iElapsedTime.Int64()),
+ I64INT(iSystemClockDelta.Int64()),
+ I64INT(iLatestAbsoluteTimeStamp.Int64()),
+ iSourceFrameInterval));
+ }
+
+ iLatestAbsoluteTimeStamp = aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64();
+ iPreviousCameraFrameIndex = aFrameBuffer->iIndexOfFirstFrameInBuffer;
+ aFrameBuffer->Release();
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady() not recording, skip this one")));
+ return;
+ }
+
+
+#if ( defined (__WINS__) || defined (__WINSCW__) )
+ if ( iEncoderInputQueueLength > 0 )
+ {
+ // Too many pictures already in the encoder's input queue - skip this one. In Wins the criteria is more strict since performance is lower
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady() - encoder already has one frame under processing, skip this one")));
+ iPreviousCameraFrameIndex = aFrameBuffer->iIndexOfFirstFrameInBuffer;
+ aFrameBuffer->Release();
+ return;
+ }
+#else
+ if ( iEncoderInputQueueLength >= KCMRMaxPreEncoderBufferPictures )
+ {
+ // Too many pictures already in the encoder's input queue - skip this one
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady() - Encoder input/output queue(s) already full, must skip a captured frame")));
+ iPreviousCameraFrameIndex = aFrameBuffer->iIndexOfFirstFrameInBuffer;
+ aFrameBuffer->Release();
+ return;
+ }
+#endif
+
+ // Regulate timestamp to avoid too small or too large variations
+
+ // use frame index instead of associated buffer timestamp, since timestamps may be
+ // less accurate and may cause rounding problems when converted to e.g. H.263 TRs
+ TInt64 frameTimeStamp;
+ frameTimeStamp = TInt64((aFrameBuffer->iIndexOfFirstFrameInBuffer - iDriftFrameSkipCount + iAddedFrameDurationCount) * 1E6/iSourceFrameRate + 0.5); // use accurate division here since rounded value in multiplication multiplies also the rounding error
+
+ // However, relying purely on frameindex causes drifting if the used source framerate value here is not exactly
+ // the camera input rate. Hence we need to do some adjustments.
+ // The frameindex may also be exclude skipped frames, but that should considered a bug in camera drivers, although the Camera API spec is not fully unambiguous about this
+ TInt64 expectedNewTimeStamp;
+ if ( iNumberOfCapturedFrames > 0 )
+ {
+ expectedNewTimeStamp = iLatestAbsoluteTimeStamp.Int64() + iSourceFrameInterval;
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), expectedNewTimeStamp (abs)\t%d\t"),I64INT(expectedNewTimeStamp)));
+ }
+ else
+ {
+ //first frame
+ expectedNewTimeStamp = 0;
+ iDriftFrameSkipCount = 0;
+ iAddedFrameDurationCount = 0;
+ iPreviousCameraFrameIndex = 0;
+ }
+
+ TInt frameDropCount = aFrameBuffer->iIndexOfFirstFrameInBuffer - iPreviousCameraFrameIndex - 1;
+ iPreviousCameraFrameIndex = aFrameBuffer->iIndexOfFirstFrameInBuffer;
+ //
+ // Handle case where camera has dropped frames.
+ //
+ if ( frameDropCount > 0 )
+ {// Camera dropped frame
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), Camera dropped %d frame(s), drift from expected: \t%d\t"),
+ frameDropCount,
+ I64INT((frameTimeStamp - expectedNewTimeStamp ))));
+ frameTimeStamp = expectedNewTimeStamp + (frameDropCount*iSourceFrameInterval);
+ }
+ //
+ // Handle case where camera frame timestamp is more than previous encoded timestamp + specified camera frame duration.
+ //
+ else if ( aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64() > expectedNewTimeStamp )
+ {
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), adjusted timestamp since it was greater than expected timestamp and we have delayed timestamps earlier\t%d\t"),I64INT((expectedNewTimeStamp - frameTimeStamp))));
+ // we have adjusted timestamps more to positive direction (delayed timestamps), try to compensate it now
+ // if camera frames have drifted more than 1 frame duration increase frame time stamp with one frame duration (as if frame dropped).
+ if ((aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64() - expectedNewTimeStamp) > iSourceFrameInterval )
+ {
+ iAddedFrameDurationCount++;
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), Camera timestamp has drifted more than 1 frame duration - adding 1 frame duration to timestamp from now on!\t%d\t"), iAddedFrameDurationCount ));
+ expectedNewTimeStamp = iLatestAbsoluteTimeStamp.Int64() + 2*iSourceFrameInterval;
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), new expectedNewTimeStamp (abs)\t%d\t"),I64INT(expectedNewTimeStamp)));
+ }
+ frameTimeStamp = expectedNewTimeStamp;
+ }
+ //
+ // Handles case where camera frame timestamp is less than previous encoded frame timestamp
+ // * happens when Camera is producing higher FPS that target FPS causing frame timestamp to drift below calculated cumulative recording time.
+ //
+ else if ( aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64() < iLatestAbsoluteTimeStamp.Int64() )
+ {
+ // frame with the timestamp of this frame was already encoded
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), drift between expected and frame buffer timestamp more than 1 frame duration, dropping frame!")));
+ iDriftFrameSkipCount++;
+ aFrameBuffer->Release();
+ return;
+ }
+ //
+ // Handle case where (camera frame index * framerate) is less than camera frame timestamp - specified camera frame duration
+ // * happens when Camera is skipping frames without extra increase in frame index count.
+ //
+ else if ( frameTimeStamp < ( (aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64()) - iSourceFrameInterval ) )
+ {
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), adjusted timestamp since frame counter was too small compared to elapsed time: difference\t%d\t"),I64INT(aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64() - frameTimeStamp)));
+ // frame numbers & elapsed time do not match, probably camera is skipping frames but doesn't know it / doesn't inform it to us
+ frameTimeStamp = Max( aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64(), expectedNewTimeStamp );
+ }
+ //
+ // Handle case where (camera frame index * framerate) is less than calculated estimated timestamp value for currect frame
+ // (based on previous encoded frame timestamp + specified camera frame duration)
+ //
+ else if ( frameTimeStamp < expectedNewTimeStamp )
+ {
+ // timestamp should not be too close to the previous timestamp (video codecs set max for framerate <=> min for ts diff)
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), adjusted timestamp since it was smaller than expected timestamp, difference\t%d\t"),I64INT(expectedNewTimeStamp - frameTimeStamp)));
+ frameTimeStamp = expectedNewTimeStamp;
+ }
+ else
+ {
+ // leave it as it is
+ }
+
+ // for now we've been playing with absolute times, excluding all pauses & adjustments. Save this value as reference for determining timestamp for the next frame
+ iLatestAbsoluteTimeStamp = frameTimeStamp;
+
+ if ( iAdjustmentTimeUs != 0 )
+ {
+ // at the moment, initial and pause values are in opposite direction (+/-)
+ // for initial adjustment:
+ // if audio is ahead (iAdjustmentTimeUs > 0) => video should fast forward (video timestamps should be increased),
+ // if audio is behind (iAdjustmentTimeUs < 0) => video should "pause" for a while (video timestamps should be decreased)
+ // note that we modify here the iPausedTime which is subtracted from aFrameBuffer->iElapsedTime, so these
+ // changes affect kind of reverse way
+
+ if ( ( frameTimeStamp - (iTotalPausedTime.Int64() - iAdjustmentTimeUs) ) >= (iLatestUsedTimeStamp.Int64() + iSourceFrameInterval) )
+ {
+ // iAdjustmentTimeUs > 0 or < 0 but not too much < 0 so that the resulting timestamp won't be < (latest time stamp + sourceframeinterval)
+ iTotalPausedTime = iTotalPausedTime.Int64() - iAdjustmentTimeUs;
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), adjusted paused time by %d us"), I64INT(iAdjustmentTimeUs)));
+ iAdjustmentTimeUs = 0;
+ }
+ else
+ {
+ // iAdjustmentTimeUs < 0 here, we should "pause" video (skip frames) until we reach the required time
+ // Skipping is needed to avoid the timestamps to skip back over already captured frames.
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), skipped a frame due to time adjustment request %d; buffer->elapsedTime %d; latest time stamp %d"), I64INT(iAdjustmentTimeUs), I64INT(aFrameBuffer->iElapsedTime.Int64() + iSystemClockDelta.Int64()), I64INT(iLatestUsedTimeStamp.Int64()) ));
+ aFrameBuffer->Release();
+ return;
+ }
+ }
+
+ // subtract the time we were paused
+ // (MSL assumes devvideorecord/hw device does this but we do it here)
+ TTimeIntervalMicroSeconds iPrevUsedTimeStamp = iLatestUsedTimeStamp;
+ iLatestUsedTimeStamp = frameTimeStamp - iTotalPausedTime.Int64();
+
+ // Increment timestamp in case we dropped frames during pause
+ while(( iLatestUsedTimeStamp < iPrevUsedTimeStamp ) && ( iNumberOfCapturedFrames > 0 ))
+ {
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady() incrementing timestamp by one frame")));
+ iLatestUsedTimeStamp = TTimeIntervalMicroSeconds( iLatestUsedTimeStamp.Int64() + iSourceFrameInterval );
+ }
+
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), iTotalPausedTime \t%d"), I64INT(iTotalPausedTime.Int64()) ));
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady(), iLatestUsedTimeStamp (relative) & drift from elapsed time\t%d"), I64INT(iLatestUsedTimeStamp.Int64()) ));
+
+ EncodeFrame(aFrameBuffer);
+
+ PRINT((_L("CCMRVideoRecorder::MvsoFrameBufferReady, out")));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::EncodeFrame
+// Encodes the given video frame.
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::EncodeFrame(MFrameBuffer* aFrameBuffer)
+ {
+ PRINT((_L("CCMRVideoRecorder::EncodeFrame, in")));
+
+ // Take a TVideoPicture into use
+ TVideoPicture* outputPicture = NULL;
+ if ( iCodingFifo->IsEmpty() )
+ {
+ // allocate a new
+ outputPicture = new TVideoPicture;
+ if ( outputPicture == NULL )
+ {
+ // out of memory, stop operation
+ DoSendEventToClient( KCMRRunTimeError, KErrNoMemory );
+ aFrameBuffer->Release();
+ return;
+ }
+ }
+ else
+ {
+ // take an old one from the queue
+ outputPicture = reinterpret_cast<TVideoPicture*>(iCodingFifo->Get());
+ }
+
+ iNumberOfCapturedFrames++;
+
+#ifdef _DEBUG
+ TTime current;
+ current.UniversalTime();
+ TInt elapsedMs = I64INT((current.MicroSecondsFrom(iLastCapture).Int64())) / 1000;
+ iCumulativeCaptureTime += elapsedMs;
+ iAverageCaptureTime = TReal(iCumulativeCaptureTime) / TReal(iNumberOfCapturedFrames);
+
+ PRINT((_L("CCMRVideoRecorder::EncodeFrame() recording time %d, ms since last: %d, average capture time: %4.2f"),
+ (I64INT(current.MicroSecondsFrom(iRecordStartTime).Int64())) / 1000,
+ elapsedMs,
+ iAverageCaptureTime));
+
+ iLastCapture = current;
+ iEncodingStartTime.UniversalTime();
+#endif
+
+ // wrap captured frame to encoder input buffer
+ outputPicture->iData.iDataFormat = EYuvRawData;
+ outputPicture->iData.iDataSize.iWidth = iFrameSize.iWidth;
+ outputPicture->iData.iDataSize.iHeight = iFrameSize.iHeight;
+ TRAPD(err, (outputPicture->iData.iRawData = (TPtr8*)aFrameBuffer->DataL(0)));
+ if ( err != KErrNone )
+ {
+ // error, stop operation
+ DoSendEventToClient( KCMRRunTimeError, err );
+ aFrameBuffer->Release();
+ delete outputPicture;
+ return;
+ }
+
+ outputPicture->iTimestamp = iLatestUsedTimeStamp;
+ outputPicture->iOptions = TVideoPicture::ETimestamp;
+ outputPicture->iUser = this;
+
+#ifdef _DEBUG__
+ TTime current;
+ current.UniversalTime();
+
+
+ PRINT((_L("CCMRVideoRecorder::EncodeFrame()\t%d"),
+ (I64INT(current.MicroSecondsFrom(iRecordStartTime).Int64())) ));
+#endif
+
+
+ // Put the captured buffer to iSourceFifo
+ TRAP( err, iSourceFifo->PutL( reinterpret_cast<TAny*>(aFrameBuffer) ));
+ if ( err != KErrNone )
+ {
+ // out of memory, stop operation
+ DoSendEventToClient( KCMRRunTimeError, err );
+ aFrameBuffer->Release();
+ delete outputPicture;
+ return;
+ }
+
+
+ // give the frame to devVideoRec for encoding
+ iEncoderInputQueueLength++;
+ TRAP( err, iDevVideoRec->WritePictureL( outputPicture ) );
+ if ( err != KErrNone )
+ {
+ // error, stop operation, aFrameBuffer will be released from fifo when stopped
+ DoSendEventToClient( KCMRRunTimeError, err );
+ return;
+ }
+
+ PRINT((_L("CCMRVideoRecorder::EncodeFrame, picture %x written to devvr"),outputPicture));
+
+#endif // #else if !VIDEO_FILE_OUTPUT
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::MdvroInitializeComplete
+// Called by DevVideoRecord when its initalization is complete
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::MdvroInitializeComplete(TInt aError)
+ {
+ PRINT((_L("CCMRVideoRecorder::MdvroInitializeComplete(), error[%d]"), aError));
+
+ if ( iDirectCapture )
+ {
+ // Since direct capture is used, it should be true. Actual init CAPI status is
+ // included to aError now.
+ iSourceInitComplete = ETrue;
+ }
+
+ iEncoderInitComplete = ETrue;
+
+ if ( iSourceInitComplete && (iErrorCode != KErrNone) )
+ {
+ // there was an error in the source initialisation, but the reporting was
+ // waiting for the completion of the encoder initialisation => report it now
+ PRINT((_L("CCMRVideoRecorder::MdvroInitializeComplete() but camera reservation had error: %d"), iErrorCode));
+ DoSendEventToClient( KCMRCameraReserveError, iErrorCode );
+ iErrorCode = KErrNone;
+ return;
+ }
+
+ if ( aError != KErrNone )
+ {
+ if ( aError == KErrHardwareNotAvailable && iVideoCodecHWAccelerated )
+ {
+ PRINT((_L("CCMRVideoRecorder::MdvroInitializeComplete() - initialization of HW accelerated video encoder failed, retrying with ARM codec")));
+ // hw accel codec and init failed due to resource problems => retry with non-hw-accelerated
+ iEncoderInitComplete = EFalse;
+ PRINT((_L("CCMRVideoRecorder::MdvroInitializeComplete() - initialization of HW accelerated video encoder failed, delete devvideo")));
+ if (iDevVideoRec)
+ {
+ delete iDevVideoRec;
+ iDevVideoRec = NULL;
+ }
+ TRAPD(err, iDevVideoRec = CMMFDevVideoRecord::NewL( *this ));
+ if ( err == KErrNone )
+ {
+ PRINT((_L("CCMRVideoRecorder::MdvroInitializeComplete() - initialization of HW accelerated video encoder failed, new devvideo created")));
+ TRAP(err,SetupEncoderL());
+ if ( err == KErrNone )
+ {
+ // ok, continue waiting for the callback
+ return;
+ }
+ else
+ {
+ // store the last error, to be used in the operations below
+ aError = err;
+ }
+ }
+ else
+ {
+ // store the last error, to be used in the operations below
+ PRINT((_L("CCMRVideoRecorder::MdvroInitializeComplete() - initialization of HW accelerated video encoder failed, new devvideo creation failed.")));
+ aError = err;
+ }
+ }
+
+ if ( iSourceInitComplete )
+ {
+ PRINT((_L("CCMRVideoRecorder::MdvroInitializeComplete() with error: %d"), aError));
+ DoSendEventToClient( KCMREncoderInitError, aError );
+ }
+ else
+ {
+ // report only when source is completed too
+ PRINT((_L("CCMRVideoRecorder::MdvroInitializeComplete() with error: %d, but must wait for camera reserve completion"), aError));
+ iErrorCode = aError;
+ }
+ return;
+ }
+
+ if ( aError == KErrNone )
+ {
+ // If Encoder initialized Ok, give other run-time settings
+ // 1. Give dynamic settings to encoder after initializing
+ if ( !iVideoCodecHWAccelerated && ( iDevVideoRec->NumComplexityLevels(iEncoderHWDeviceId) > 0 ) )
+ {
+ iVideoComplexity = iConfig->PluginSettings().iVideoComplexitySetting;
+ iDevVideoRec->SetComplexityLevel( iEncoderHWDeviceId, iVideoComplexity );
+ }
+ // HW codec-specific configuration is HW dependent, and hence outside the scope of Series 60
+
+ // 2. Rate control options
+ if ( iEncodingFrameRate > iMaxFrameRate4GivenSize )
+ {
+ iEncodingFrameRate = static_cast<TReal32>(iMaxFrameRate4GivenSize);
+ }
+
+ FillRateControlOptions( iRateControlOptions );
+ iDevVideoRec->SetRateControlOptions(0, iRateControlOptions); // only base layer (layer 0) supported
+ TRAPD(err,iVideoCodecData->SetPostInitParamsL(iDevVideoRec));
+ if ( err ) {}
+ // ignore the error, having some post init setting is not crucial
+
+ }
+
+ if ( iSourceInitComplete )
+ {
+ SetState(EStateReadyToRecord);
+
+ // everything ok, inform observer that we are prepared.
+ DoSendEventToClient( KCMRPrepareComplete, KErrNone );
+ }
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::MdvroReturnPicture
+// Called by DevVideoRecord when it does not need the given
+// input frame anymore
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::MdvroReturnPicture(TVideoPicture* aPicture)
+ {
+#ifdef _DEBUG
+ TTime current;
+ current.UniversalTime();
+
+
+ PRINT((_L("CCMRVideoRecorder::MdvroReturnPicture() time now\t%d"),
+ (I64INT(current.MicroSecondsFrom(iRecordStartTime).Int64())) ));
+#endif
+
+ // take oldest frame from fifo
+ VRASSERT( !iSourceFifo->IsEmpty() );// if empty, for some reason the buffer was already returned!!
+
+ MFrameBuffer* buffer = reinterpret_cast<MFrameBuffer*>(iSourceFifo->Get());
+
+ // check that its the oldest stored captured picture
+ TPtr8* tmp = NULL;
+ TRAPD(error, (tmp = static_cast<TPtr8*>(buffer->DataL(0))));
+ if ( tmp != aPicture->iData.iRawData )
+ {
+ // pictures are returned in different order than they were sent
+ RPointerArray<MFrameBuffer> buffers;
+ buffers.Append(buffer);
+
+ TUint i;
+ for ( i = 1; i < iNumCameraBuffers; i++ )
+ {
+ buffers.Append(reinterpret_cast<MFrameBuffer*>(iSourceFifo->Get()));
+
+ TRAP(error, (tmp = static_cast<TPtr8*>(buffers[i]->DataL(0))));
+ if ( tmp == aPicture->iData.iRawData )
+ {
+ // Found it! Put the retrieved buffers back to the queue
+ TInt err = KErrNone;
+ TUint j;
+ for ( j = 0; j < i; j++ )
+ {
+ TRAP( err, iSourceFifo->PutL( reinterpret_cast<TAny*>(buffers[j]) ));
+ // This can't really fail since we just got the buffers from the fifo and in that case PutL doesn't allocate anything => can't leave
+ VRASSERT( err == KErrNone );
+ buffers[j] = NULL;
+ }
+ break;
+ }
+ }
+ VRASSERT( i < iNumCameraBuffers );
+ buffer = buffers[i];
+ buffers.Close();
+ }
+
+ // release captured buffer
+ PRINT((_L("CCMRVideoRecorder::MdvroReturnPicture() releasing %x"), buffer ));
+ buffer->Release();
+ buffer = NULL;
+
+ iEncoderInputQueueLength--;
+
+
+ // save the picture holder to fifo
+ TRAPD( err, iCodingFifo->PutL( reinterpret_cast<TAny*>(aPicture) ));
+ if ( err != KErrNone )
+ {
+ // OOM, inform error and stop operation
+ DoSendEventToClient( KCMRRunTimeError, err );
+ delete aPicture;
+ return;
+ }
+ PRINT((_L("CCMRVideoRecorder::MdvroReturnPicture() stored %x"), aPicture ));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::MdvroSupplementalInfoSent
+// Called by DevVideoRecord when supplemental info (e.g.,
+// VOL header) has been sent to the encoder
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::MdvroSupplementalInfoSent()
+ {
+ PRINT((_L("CCMRVideoRecorder::MdvroSupplementalInfoSent()")));
+ User::Panic(_L("CCMRVIDEORECORDER"), KErrGeneral);
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::MdvroNewBuffers
+// Called by DevVideoRecord when new bitstream buffers are
+// available for retrieval
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::MdvroNewBuffers()
+ {
+ PRINT((_L("CCMRVideoRecorder::MdvroNewBuffers() - START - iRequestStatus: 0x%08x"), iRequestStatus ));
+ PRINT((_L("CCMRVideoRecorder::MdvroNewBuffers(), NumDataBuffers[%d]"), iDevVideoRec->NumDataBuffers() ));
+ iNumberOfEncodedFrames++;
+
+ TVideoOutputBuffer* buffer = NULL;
+ for (;;)
+ {
+ TRAPD( err, ( buffer = iDevVideoRec->NextBufferL()));
+
+ if ( err != KErrNone )
+ {
+ return;
+ }
+ if ( buffer == NULL )
+ {
+ break;
+ }
+/* TInt bufferSize = buffer->iData.Length();
+ TInt bufferTimeStamp = I64INT(buffer->iCaptureTimestamp.Int64());
+ OstTraceExt2( TRACE_PERFORMANCE, CCMRVIDEORECORDER_MDVRONEWBUFFERS, "CCMRVideoRecorder::MdvroNewBuffers %d %d", bufferSize, bufferTimeStamp );
+*/
+ // enter restricted area
+ iMutexObj.Wait();
+ // store
+ PRINT((_L("CCMRVideoRecorder::MdvroNewBuffers(), storing buffer: 0x%x, timestamp: high: %u low: %u"), buffer, I64HIGH(buffer->iCaptureTimestamp.Int64()),I64LOW(buffer->iCaptureTimestamp.Int64())));
+ iVideoOutputBufferInputQue.AddLast(*buffer);
+ iNumberOfVideoOutputBuffers++;
+
+ // leave restricted area
+ iMutexObj.Signal();
+ }
+
+ // enter restricted area
+ iMutexObj.Wait();
+ if ( iRequestStatus )
+ {
+ PRINT((_L("CCMRVideoRecorder::MdvroNewBuffers() - completing output request..." )));
+ iOutputThreadHandle.RequestComplete( iRequestStatus, KErrNone );
+ }
+ else
+ {
+ // else a new request has not been issued yet
+ PRINT((_L("CCMRVideoRecorder::MdvroNewBuffers() - skipping RequestComplete" )));
+ }
+ // leave restricted area
+ iMutexObj.Signal();
+
+ PRINT((_L("CCMRVideoRecorder::MdvroNewBuffers() - END - iRequestStatus: 0x%08x"), iRequestStatus ));
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::MdvroFatalError
+// Called by devVideoRecord when a fatal error has occurred
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::MdvroFatalError(TInt aError)
+ {
+ PRINT((_L("CCMRVideoRecorder::MdvroFatalError(), error[%d]"), aError));
+
+ // Since now don't touch DevVideo that expects only destruction from the client, otherwise panics..
+ iFatalError = ETrue;
+
+ // Cancel returning buffers to DevVideo
+ iBufferReturnAO->Cancel();
+
+ if ( iDevVideoRec )
+ {
+ delete iDevVideoRec;
+ iDevVideoRec = NULL;
+ }
+
+ if ( State() == EStateStopping )
+ {
+ // error occurred while stopping => carry out the rest of stopping & inform client
+ SetState( EStateReadyToRecord );
+ DoSendEventToClient( KCMRRecordingComplete, aError );
+ }
+ else
+ {
+ // inform client
+ DoSendEventToClient( KCMRRunTimeError, aError );
+ iErrorCode = aError;
+ }
+ }
+
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::MdvroStreamEnd
+// Called by devVideoRecord when all pictures have been processed
+// (after InputEnd is called)
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::MdvroStreamEnd()
+ {
+ PRINT((_L("CCMRVideoRecorder::MdvroStreamEnd() in")));
+
+ // MdvroStreamEnd was called by devVideo without InputEnd
+ VRASSERT( iInputEnd );
+
+ // get all available buffers from devvideo
+ PRINT((_L("CCMRVideoRecorder::MdvroStreamEnd(), getting all encoded frames from DevVideo.")));
+ TVideoOutputBuffer* buffer = NULL;
+ for (;;)
+ {
+ TRAPD( err, ( buffer = iDevVideoRec->NextBufferL()));
+ if ( err != KErrNone || buffer == NULL)
+ {
+ break;
+ }
+ // enter restricted area
+ iMutexObj.Wait();
+ // store
+ PRINT((_L("CCMRVideoRecorder::MdvroStreamEnd(), storing buffer: 0x%x, timestamp:%d"), buffer, I64INT(buffer->iCaptureTimestamp.Int64())));
+ iVideoOutputBufferInputQue.AddLast(*buffer);
+ iNumberOfVideoOutputBuffers++;
+
+ // leave restricted area
+ iMutexObj.Signal();
+ }
+
+ PRINT((_L("CCMRVideoRecorder::MdvroStreamEnd(), flushing written frames back to DevVideo.")));
+ iBufferReturnAO->Flush();
+
+ // stop
+ if ( iDevVideoRec )
+ {
+ PRINT((_L("CCMRVideoRecorder::MdvroStreamEnd() iDevVideoRec->Stop called")));
+ iDevVideoRec->Stop();
+ }
+
+ iStreamEnd = ETrue;
+
+ // set state & inform MR => it can return from stop
+ SetState( EStateReadyToRecord );
+
+ // everything ok, inform observer that we are ready for a new recording (iStopping does it too, though).
+ DoSendEventToClient( KCMRRecordingComplete, KErrNone );
+
+ PRINT((_L("CCMRVideoRecorder::MdvroStreamEnd() out")));
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::RequestNewData
+// Output active object is ready to accept new data
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::RequestNewData(TRequestStatus& aStatus)
+ {
+ PRINT((_L("CCMRVideoRecorder::RequestNewData() - START - aStatus: 0x%08x, iRequestStatus: 0x%08x"), &aStatus, iRequestStatus ));
+
+ // enter restricted area
+ iMutexObj.Wait();
+
+ iRequestStatus = &aStatus;
+ aStatus = KRequestPending;
+
+ // leave restricted area
+ iMutexObj.Signal();
+
+ PRINT((_L("CCMRVideoRecorder::RequestNewData() - END - aStatus: 0x%08x, iRequestStatus: 0x%08x"), &aStatus, iRequestStatus ));
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::RequestNewDataCancel
+//
+//
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::RequestNewDataCancel(TRequestStatus& aStatus)
+ {
+ PRINT((_L("CCMRVideoRecorder::RequestNewDataCancel() - START - aStatus: 0x%08x, iRequestStatus: 0x%08x"), &aStatus, iRequestStatus ));
+
+ // enter restricted area
+ iMutexObj.Wait();
+
+ if ( &aStatus == iRequestStatus )
+ {
+ iOutputThreadHandle.RequestComplete( iRequestStatus, KErrCancel );
+ }
+
+ // leave restricted area
+ iMutexObj.Signal();
+
+ PRINT((_L("CCMRVideoRecorder::RequestNewDataCancel() - END - aStatus: 0x%08x, iRequestStatus: 0x%08x"), &aStatus, iRequestStatus ));
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::GetNextBuffer
+// Output active object takes the next output buffer
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+CCMRMediaBuffer* CCMRVideoRecorder::GetNextBuffer()
+ {
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer() in") ));
+ if ( iDevVideoRec == NULL || iFatalError )
+ {
+ // probably we have not been opened yet
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer(), We have not been opened yet, or there's an error from devVideo")));
+ return NULL;
+ }
+
+ if ( iDecSpecInfo )
+ {
+ // we have MPEG-4 decoder configuration information, it must be processed first
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer() iDecSpecInfo") ));
+ iRemoveHeader = ETrue;
+ const TUint8* data = iDecSpecInfo->Ptr();
+ iDecSpecInfoLength = iDecSpecInfo->Length();
+
+ CCMRMediaBuffer::TBufferType decSpecType = CCMRMediaBuffer::EVideoMPEG4DecSpecInfo;
+ if ( iVideoBufferType == CCMRMediaBuffer::EVideoH264NAL )
+ {// H.264 AVC NAL / GenericPayload encapsulation
+ decSpecType = CCMRMediaBuffer::EVideoH264NALDecSpecInfo;
+ }
+ else if ( iVideoBufferType == CCMRMediaBuffer::EVideoH264Bytestream )
+ {
+ decSpecType = CCMRMediaBuffer::EVideoH264BytestreamDecSpecInfo;
+ }
+
+ // wrap the relevant data to CCMRMediaBuffer; iOutputSinkBuffer can't be != NULL here since it is created in ConstructL
+ iOutputSinkBuffer->Set( TPtrC8(data,iDecSpecInfoLength),
+ decSpecType,
+ iDecSpecInfoLength,
+ EFalse,
+ TTimeIntervalMicroSeconds(0) );
+
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer() decoder specific info to sink sent")));
+ return iOutputSinkBuffer;
+ }
+
+ // enter restricted area
+ iMutexObj.Wait();
+ if (iNumberOfVideoOutputBuffers > 0)
+ {
+ iOutputVideoBuffer = iVideoOutputBufferInputQue.First();
+
+ // Remove the picture from the list
+ iOutputVideoBuffer->iLink.Deque();
+ iNumberOfVideoOutputBuffers--;
+ // leave restricted area
+ iMutexObj.Signal();
+ }
+ else
+ {
+ // leave restricted area
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer()returning null, leave restricted area, iNumberOfVideoOutputBuffers == 0 %x"), this));
+ iMutexObj.Signal();
+ return NULL;
+ }
+
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer() got [0x%x] from devvr"), iOutputVideoBuffer ));
+
+ if ( iOutputVideoBuffer != NULL )
+ {
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer() processing iOutputVideoBuffer") ));
+ TBool outputMediaBufferSet = EFalse;
+ if ( iRemoveHeader )
+ {
+ // check if we need to remove VOS+VO+VOL header from the bitstream, since it is stored in metadata, and
+ // having it in 2 places may cause interoperability problems
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer() remove extra header info") ));
+ iRemoveHeader = EFalse;
+ if ( iVideoBufferType == CCMRMediaBuffer::EVideoMPEG4 )
+ {// MPEG-4
+ RemoveSeqHeader( reinterpret_cast<TDesC8*>(&iOutputVideoBuffer->iData) );
+ }
+ else if ( iVideoBufferType == CCMRMediaBuffer::EVideoH264NAL )
+ {// H.264 AVC NAL / EDuGenericPayload / NAL encapsulation
+ // Removes SPS & PPS from first frame from encoder to avoid situation where its both in
+ // .mp4 file metadata and in bitstream of video track.
+ // Make sure buffer contains only one frame and rewrite encapsulation to make sure its ok.
+ RemoveNalDecSpecInfoHeader( reinterpret_cast<TDesC8*>(&iOutputVideoBuffer->iData) );
+ outputMediaBufferSet = ETrue;
+ }
+ else if ( iVideoBufferType == CCMRMediaBuffer::EVideoH264Bytestream )
+ {// H.264 AVC NAL / EDuElementarystream / Bytestream
+ // Removes SPS & PPS from first frame from encoder to avoid situation where its both in
+ // .mp4 file metadata and in bitstream of video track.
+ RemoveByteStreamDecSpecInfoHeader( reinterpret_cast<TDesC8*>(&iOutputVideoBuffer->iData) );
+ outputMediaBufferSet = ETrue;
+ }
+ }
+
+ if ( !outputMediaBufferSet )
+ {
+ // wrap the relevant data to CCMRMediaBuffer
+ iOutputSinkBuffer->Set( iOutputVideoBuffer->iData,
+ iVideoBufferType,
+ iOutputVideoBuffer->iData.Length(),
+ iOutputVideoBuffer->iRandomAccessPoint,
+ iOutputVideoBuffer->iCaptureTimestamp );
+ }
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer() iOutputSinkBuffer set") ));
+
+#ifdef _DEBUG__
+ TTime current;
+ current.UniversalTime();
+ TInt elapsedMs = (I64INT(current.MicroSecondsFrom(iEncodingStartTime).Int64())) / 1000;
+ iCumulativeEncodingTime += elapsedMs;
+ iAverageEncodingTime = TReal(iCumulativeEncodingTime) / TReal(iNumberOfEncodedFrames);
+
+
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer() %d, time consumed: %d, average encoding time: %4.2f"),
+ (I64INT(current.MicroSecondsFrom(iRecordStartTime).Int64())) / 1000,
+ (I64INT(current.MicroSecondsFrom(iEncodingStartTime).Int64())) / 1000,
+ iAverageEncodingTime));
+#endif
+
+#ifdef VIDEO_BS_FILE_OUTPUT
+ // Write the frame to a file for testing purposes; have to ignore possible errors
+ iOutputFile.Write(videoBuffer->iData);
+#endif
+
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer() a buffer to sink sent with timestamp high: %u low: %u"),I64HIGH(iOutputVideoBuffer->iCaptureTimestamp.Int64()),I64LOW(iOutputVideoBuffer->iCaptureTimestamp.Int64())));
+ return iOutputSinkBuffer;
+ }
+ else
+ {
+ PRINT((_L("CCMRVideoRecorder::GetNextBuffer() returning NULL - no new buffers in queue") ));
+ // no more new buffers in queue
+ return NULL;
+ }
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::NumBuffersWaiting
+// Return the number of buffers in the source
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TInt CCMRVideoRecorder::NumBuffersWaiting()
+ {
+ if (iFatalError)
+ {
+ // NumDataBuffers() cannot be called since fatalError was reported
+ return 0;
+ }
+
+ if ( iDecSpecInfo )
+ {
+ // decspecinfo is not counted in iNumberOfVideoOutputBuffers
+ return (iNumberOfVideoOutputBuffers+1);
+ }
+ else
+ {
+ return iNumberOfVideoOutputBuffers;
+ }
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::LatestTimeStampL
+// Return the latest time stamp from the input stream
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::LatestTimeStampL(TTimeIntervalMicroSeconds& aTimeStamp) const
+ {
+ aTimeStamp = iLatestUsedTimeStamp;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::DurationL
+// Get duration of the recording (in practice the same as the latest time stamp)
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::DurationL(TTimeIntervalMicroSeconds& aDuration) const
+ {
+ LatestTimeStampL( aDuration );
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::ReturnBuffer
+// Output active object returns an emptied buffer
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::ReturnBuffer(CCMRMediaBuffer* aBuffer)
+ {
+ PRINT((_L("CCMRVideoRecorder::ReturnBuffer() buffers 0x%x, 0x%x"),aBuffer, iOutputSinkBuffer));
+ VRASSERT( iOutputSinkBuffer == aBuffer );
+
+ if ( iFatalError )
+ {
+ PRINT((_L("CCMRVideoRecorder::ReturnBuffer(), don't return anything, since there's an error from devVideo")));
+ iOutputVideoBuffer = NULL;
+ return;
+ }
+
+ iOutputSinkBuffer = aBuffer;
+ if ( iDecSpecInfo )
+ {
+ delete iDecSpecInfo;
+ iDecSpecInfo = NULL;
+ // iOutputVideoBuffer is not used with decspecinfo
+ }
+ else
+ {
+ // normal case; iOutputVideoBuffer != NULL if functions are called in correct order
+ PRINT((_L("CCMRVideoRecorder::ReturnBuffer() iOutputVideoBuffer 0x%x"), iOutputVideoBuffer));
+ VRASSERT( iOutputVideoBuffer != NULL );
+ // store the buffer for sending from this thread
+ iBufferReturnAO->EnqueueReturnBuffer(iOutputVideoBuffer);
+
+ iOutputVideoBuffer = NULL;
+ }
+ }
+
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::FillRateControlOptions
+// Fill DevVideoRecord rate control options structure
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::FillRateControlOptions( TRateControlOptions& aRC )
+ {
+ TRAPD(error,CCMRRecorderBase::TargetBitRateL((TInt&)(aRC.iBitrate)));
+
+ // error can be ignored, it will never be != KErrNone at this stage
+ if (error != KErrNone)
+ {
+ VRASSERT( error == KErrNone );
+ }
+
+ if ( iBitRateMode == EBitRateConstant )
+ {
+ aRC.iControl = EBrControlStream;
+ }
+ else
+ {
+ // Variable bitrate; rc.iBitrate is probably not needed but it gives now the upper limit
+ aRC.iControl = EBrControlNone;
+ }
+
+ aRC.iPictureRate = iEncodingFrameRate;
+ // these are filled already in constructor:
+ //aRC.iPictureQuality
+ //aRC.iLatencyQualityTradeoff
+ //aRC.iQualityTemporalTradeoff
+ PRINT((_L("CCMRVideoRecorder::FillRateControlOptions() - framerate %f, bitrate %d"), aRC.iPictureRate, aRC.iBitrate));
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::CheckExposure
+// Check exposure from camera and adjust framerates if needed
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::CheckExposure()
+ {
+ if ( iSource->GetExposure() == CCamera::EExposureNight )
+ {
+ iNightMode = ETrue;
+ iSourceFrameRate = iConfig->PluginSettings().iVideoNightFrameRate;
+ iSourceFrameInterval = convertFrameRateToInterval(iSourceFrameRate);
+
+ // new framerate should be set to the encoder too
+ TRAPD(error, SetFrameRateL( iSourceFrameRate ));
+
+ // error can be ignored, it will never be != KErrNone here
+ if (error != KErrNone)
+ {
+ VRASSERT( error == KErrNone );
+ }
+ }
+ else
+ {
+ // other modes don't affect to framerates, except if we switch back from night mode to normal
+ if ( iNightMode )
+ {
+ iSourceFrameRate = iRequestedFrameRate;
+ iSourceFrameInterval = convertFrameRateToInterval(iSourceFrameRate);
+
+ // new framerate should be set to the encoder too
+ TRAPD(error,SetFrameRateL( iSourceFrameRate ));
+
+ // error can be ignored, it will never be != KErrNone here
+ if (error != KErrNone)
+ {
+ VRASSERT( error == KErrNone );
+ }
+
+ iNightMode = EFalse;
+ }
+ }
+
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::UpdateSupportedFrameSizesRates
+// Update supported video frames and sizes table from ECAM and MDF encoders.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::UpdateSupportedVideoFrameSizesRates()
+ {
+ PRINT((_L("CCMRVideoRecorder::UpdateSupportedFrameSizesRates() in")));
+ iAvailableVideoFrameSizesRates.Reset();
+
+ if ( iAvailableVideoEncoders.Count() )
+ {
+ for ( TInt i = 0 ; i < iAvailableVideoEncoders.Count(); i++ )
+ {
+ // encoder info for retrieving capabilities
+ CVideoEncoderInfo* encoderInfo = NULL;
+ PRINT((_L("CCMRVideoRecorder::UpdateSupportedFrameSizesRates() - getting info from a plugin index[%d] with Uid 0x%x"), i, iAvailableVideoEncoders[i].iUid ));
+ TRAPD(error, (encoderInfo = ReadEncoderInfoL(iAvailableVideoEncoders[i])) );
+
+ if ( (encoderInfo != NULL) && (error == KErrNone))
+ {
+ // Check directCapture support for current codec
+ if ( encoderInfo->SupportsDirectCapture() )
+ {
+ // check max framerate for given picture size
+ RArray<TPictureRateAndSize> rateAndSize = encoderInfo->MaxPictureRates();
+ for ( TUint j = 0; j < rateAndSize.Count(); j++ )
+ {
+ TPictureRateAndSize newSizeRate = rateAndSize[j];
+ TInt addError = iAvailableVideoFrameSizesRates.InsertInOrder(newSizeRate, TLinearOrder<TPictureRateAndSize>(TLinearOrderFuncVideoSizeRate) );
+ if (addError == KErrNone)
+ {
+ PRINT((_L("CCMRVideoRecorder::UpdateSupportedFrameSizesRates() - Added size: %d x %d, rate: %f"),
+ rateAndSize[j].iPictureSize.iWidth,
+ rateAndSize[j].iPictureSize.iHeight,
+ rateAndSize[j].iPictureRate));
+ }
+ }
+ }
+ delete encoderInfo;
+ }
+ }
+ }
+ PRINT((_L("CCMRVideoRecorder::UpdateSupportedFrameSizesRates() out")));
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::DoSendEventToClient
+// Send event to client
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TInt CCMRVideoRecorder::DoSendEventToClient(TUid aEventType, TInt aErrorCode)
+ {
+ PRINT((_L("CCMRVideoRecorder::DoSendEventToClient(), aEventType %d"),aEventType.iUid));
+ TMMFEvent event(aEventType, aErrorCode);
+ return iEventHandler.SendEventToClient(event);
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::ReturnBufferToDevVR
+// CCMRReturnAO uses this function to return buffer to DevVideoRecord
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::ReturnBufferToDevVR(TVideoOutputBuffer* aBuffer)
+ {
+ PRINT((_L("CCMRVideoRecorder::ReturnBufferToDevVR() in buffer: 0x%x, timestamp: high: %u low: %u"), aBuffer, I64HIGH(aBuffer->iCaptureTimestamp.Int64()),I64LOW(aBuffer->iCaptureTimestamp.Int64())));
+ if (iDevVideoRec)
+ {
+ iDevVideoRec->ReturnBuffer(aBuffer);
+ }
+ PRINT((_L("CCMRVideoRecorder::ReturnBufferToDevVR() out")));
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::RemoveSeqHeader( TDesC8* aVideoBuffer )
+// RemoveSeqHeader remove MPEG4 decoder configuration info (VOS+VO+VOL header)
+// from the 1st video packet (it is saved in metadata, and duplication not allowed)
+// Returns: TInt number of bytes removed
+// -----------------------------------------------------------------------------
+//
+TInt CCMRVideoRecorder::RemoveSeqHeader( TDesC8* aVideoBuffer )
+ {
+ // MPEG-4 VOP header == the 1st element in the buffer to not remove
+ const TUint8 KHeader[4] = {0x00, 0x00, 0x01, 0xb6};
+ const TUint KSampleLength = 4;
+
+ TPtr8* buffer = reinterpret_cast<TPtr8*>(aVideoBuffer);
+ // Bitstream start point
+ const TUint8* data = buffer->Ptr();
+ TUint headerLength;
+ TBool iFound = EFalse;
+
+ // Search for VOP start code
+ for ( headerLength = 0; headerLength < (buffer->Length() - (KSampleLength - 1)); headerLength++ )
+ {
+ if ( (data[headerLength] == KHeader[0])
+ && (data[headerLength+1] == KHeader[1])
+ && (data[headerLength+2] == KHeader[2])
+ && (data[headerLength+3] == KHeader[3]) )
+ {
+ // VOP start code is found => video data starts from here
+ iFound = ETrue;
+ break;
+ }
+ }
+
+ if ( (headerLength > 0) && iFound )
+ {
+ // adjust ptr & length depending which is longer VOP header location or earlier iDecSpecInfoLength length
+ if ( iDecSpecInfoLength && (iDecSpecInfoLength <= headerLength ) )
+ {
+ buffer->Delete(0,iDecSpecInfoLength);
+ }
+ else
+ {
+ buffer->Delete(0,headerLength);
+ }
+ }
+ return headerLength;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::RemoveNalDecSpecInfoHeader( TDesC8* aVideoBuffer )
+// Removes decoder configuration info (SPS & PPS) from first NAL encapsulated H.264/AVC video buffer
+// from encoder to avoid situation where its both in .mp4 file metadata and in bitstream of video track.
+// Makes sure buffer contains only one frame and rewrite encapsulation to make sure its ok.
+// -----------------------------------------------------------------------------
+//
+TInt CCMRVideoRecorder::RemoveNalDecSpecInfoHeader( TDesC8* aVideoBuffer )
+ {
+ // H.264 AVC NAL / EDuGenericPayload / NAL encapsulation
+
+ // Get reference to the buffer
+ TPtr8* ptr = reinterpret_cast<TPtr8*>(aVideoBuffer);
+ // Bitstream start point
+ TPtr8& buffer(*ptr);
+ TInt frameStart = 0;
+ TInt frameSize = 0;
+ TInt offset = 0;
+ TInt bufType = 0;
+ TInt i=0;
+ TInt totalNALLength = 0;
+ TInt firstCopiedNAL = 0;
+ TInt nalCount = 0;
+ RArray<TInt> nalSizes;
+ RArray<TInt> nalStarts;
+
+ PRINT((_L("CCMRVideoRecorder::RemoveNalDecSpecInfoHeader() in, length: %d "), buffer.Length() ));
+
+ // There should be enough data for the NAL header + frame
+ if ( buffer.Length() > 12 )
+ {
+ // Offset to the end and get NAL unit count
+ offset = buffer.Length()-4; //last 4 bytes are the NAL unit count
+ nalCount = TInt(buffer[offset]) +
+ (TInt(buffer[offset + 1]) << 8) +
+ (TInt(buffer[offset + 2]) << 16) +
+ (TInt(buffer[offset + 3]) << 24);
+
+ for(i=0; i<nalCount; i++)
+ {//go through all NAL units in buffer
+ // Offset to the start of NAL Unit infos
+ offset = buffer.Length()-4-(8*nalCount); // 4 is the NAL unit count at end of buffer, 8 bytes used per NAL Unit for FrameStartOffset and FrameSize.
+
+ // Get frame start offset
+ offset += 8*i;
+ frameStart = TInt(buffer[offset]) +
+ (TInt(buffer[offset + 1]) << 8) +
+ (TInt(buffer[offset + 2]) << 16) +
+ (TInt(buffer[offset + 3]) << 24);
+
+ PRINT((_L("CCMRVideoRecorder::RemoveNalDecSpecInfoHeader() NAL unit %d frame start: %d "), i, frameStart ));
+
+ // Get frame size
+ offset += 4;
+ frameSize = TInt(buffer[offset]) +
+ (TInt(buffer[offset + 1]) << 8) +
+ (TInt(buffer[offset + 2]) << 16) +
+ (TInt(buffer[offset + 3]) << 24);
+
+ PRINT((_L("CCMRVideoRecorder::RemoveNalDecSpecInfoHeader() NAL unit %d frame size: %d "), i, frameSize ));
+ bufType = buffer[frameStart] & 0x1F;
+ PRINT((_L("CCMRVideoRecorder::RemoveNalDecSpecInfoHeader() NAL unit %d type: %d "), i, bufType ));
+ if ( bufType != 7 && //SPS
+ bufType != 8 )//PPS
+ {
+ // we found first NAL unit that isn't SPS or PPS
+ nalSizes.Append(frameSize);
+ if (firstCopiedNAL==0)
+ {
+ firstCopiedNAL = frameStart;
+ }
+ nalStarts.Append(frameStart-firstCopiedNAL);
+ totalNALLength = frameStart+frameSize-firstCopiedNAL;
+ PRINT((_L("CCMRVideoRecorder::RemoveNalDecSpecInfoHeader() new total length: %d, padding: %d "), totalNALLength, totalNALLength%4 ));
+ totalNALLength += totalNALLength%4;
+ }
+ }
+
+ // The buffer should have enough space for the new NAL header
+
+ // We need to write a new NAL header just after the frame end
+ PRINT((_L("CCMRVideoRecorder::RemoveNalDecSpecInfoHeader() writing new header.")));
+ offset = firstCopiedNAL + totalNALLength;
+
+ if ( (offset + nalSizes.Count()*8 + 4) > buffer.Length() )
+ {
+ PRINT((_L("CCMRVideoRecorder::RemoveNalDecSpecInfoHeader() Fatal error, cannot create header, non-align 32bit encoder output buffer.")));
+ VRASSERT(0);
+ }
+
+ for (TInt j=0; j<nalSizes.Count(); j++)
+ {
+ PRINT((_L("CCMRVideoRecorder::RemoveNalDecSpecInfoHeader() new header, unit: %d, start: %d, size: %d."), j, nalStarts[j], nalSizes[j] ));
+ // Write NAL unit start position
+ buffer[offset + 0] = TUint8(nalStarts[j] & 0xff);
+ buffer[offset + 1] = TUint8((nalStarts[j] >> 8) & 0xff);
+ buffer[offset + 2] = TUint8((nalStarts[j] >> 16) & 0xff);
+ buffer[offset + 3] = TUint8((nalStarts[j] >> 24) & 0xff);
+
+ // Write NAL unit size
+ offset +=4;
+ buffer[offset + 0] = TUint8(nalSizes[j] & 0xff);
+ buffer[offset + 1] = TUint8((nalSizes[j] >> 8) & 0xff);
+ buffer[offset + 2] = TUint8((nalSizes[j] >> 16) & 0xff);
+ buffer[offset + 3] = TUint8((nalSizes[j] >> 24) & 0xff);
+
+ offset +=4;
+ }
+
+ // Write the number of NAL units
+ buffer[offset + 0] = TUint8(nalSizes.Count() & 0xff);;
+ buffer[offset + 1] = TUint8((nalSizes.Count() >> 8) & 0xff);;
+ buffer[offset + 2] = TUint8((nalSizes.Count() >> 16) & 0xff);
+ buffer[offset + 3] = TUint8((nalSizes.Count() >> 24) & 0xff);
+ }
+
+ // Get a pointer to the position where the frame starts
+ TPtrC8 tmp( (buffer.Ptr() + firstCopiedNAL), (totalNALLength + (nalSizes.Count()*8) + 4) );
+
+ // Set output media buffer
+ iOutputSinkBuffer->Set( tmp, CCMRMediaBuffer::EVideoH264NAL,
+ tmp.Length(), iOutputVideoBuffer->iRandomAccessPoint,
+ iOutputVideoBuffer->iCaptureTimestamp );
+
+ nalSizes.Close();
+ nalStarts.Close();
+ return firstCopiedNAL;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::RemoveByteStreamDecSpecInfoHeader( TDesC8* aVideoBuffer )
+// Removes decoder configuration info (SPS & PPS) from first bytestream encapsulated H.264/AVC video buffer
+// from encoder to avoid situation where its both in .mp4 file metadata and in bitstream of video track.
+// -----------------------------------------------------------------------------
+//
+TInt CCMRVideoRecorder::RemoveByteStreamDecSpecInfoHeader( TDesC8* aVideoBuffer )
+ {// H.264 AVC NAL / EDuElementarystream / Bytestream
+ TInt skippedBytes = -1;
+ TInt bufType = 0;
+
+ // Get reference to the buffer
+ TPtr8* buffer = reinterpret_cast<TPtr8*>(aVideoBuffer);
+ // Bitstream start point
+ const TUint8* data = buffer->Ptr();
+
+ PRINT((_L("CCMRVideoRecorder::RemoveByteStreamDecSpecInfoHeader() in, length: %d "), buffer->Length() ));
+
+ // check buffer for bytestream header and possible decSpecInfo (SPS & PPS)
+ for (TInt i=0; i+4< buffer->Length(); i++)
+ {
+ // check for bytestream encapsulation [00 00 00 01]
+ if ( data[i] == 0 &&
+ data[i+1] == 0 &&
+ data[i+2] == 0 &&
+ data[i+3] == 1 )
+ {
+ PRINT((_L("CCMRVideoRecorder::RemoveByteStreamDecSpecInfoHeader() 0001 header at: %d "), i ));
+ // search buffer that isn't SPS or PPS
+ bufType = data[i+4] & 0x1F;
+ PRINT((_L("CCMRVideoRecorder::RemoveByteStreamDecSpecInfoHeader() buffer type: %d "), bufType ));
+ if ( (bufType == 7 || //SPS
+ bufType == 8) && //PPS
+ skippedBytes == -1 )
+ {// we need to skip SPS and PPS so activate next if clause
+ skippedBytes = 0;
+ }
+ if ( bufType != 7 && //SPS
+ bufType != 8 && //PPS
+ skippedBytes == 0)
+ {// found first non SPS / PPS buffer content -> stop stripping here
+ skippedBytes = i;
+ PRINT((_L("CCMRVideoRecorder::RemoveByteStreamDecSpecInfoHeader() skipping: %d bytes"), skippedBytes ));
+ }
+ }
+ }
+/*
+ if (skippedBytes)
+ {
+ for (TInt j=0; j<skippedBytes;j++)
+ {
+ PRINT((_L("CCMRVideoRecorder::RemoveByteStreamDecSpecInfoHeader() skipping %d: %x "), j, data[j] ));
+ }
+ }
+*/
+ // Get a pointer to the position where the frame starts
+ TPtrC8 tmp(buffer->Ptr() + skippedBytes, buffer->Length() - skippedBytes);
+
+ // Set output media buffer
+ iOutputSinkBuffer->Set( tmp, CCMRMediaBuffer::EVideoH264Bytestream,
+ tmp.Length(), iOutputVideoBuffer->iRandomAccessPoint,
+ iOutputVideoBuffer->iCaptureTimestamp );
+
+ return skippedBytes;
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::CCMRReturnAO::NewL
+// Two-phased constructor.
+// ---------------------------------------------------------
+//
+CCMRVideoRecorder::CCMRReturnAO* CCMRVideoRecorder::CCMRReturnAO::NewL(CCMRVideoRecorder* aHost)
+ {
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::NewL(), In")))
+
+ CCMRReturnAO* self = new (ELeave) CCMRReturnAO(aHost);
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ CleanupStack::Pop();
+
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::NewL(), Out")))
+ return self;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::CCMRReturnAO::CCMRReturnAO
+// Default constructor for CCMRReturnAO
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+CCMRVideoRecorder::CCMRReturnAO::CCMRReturnAO(CCMRVideoRecorder* aHost) :
+ CActive(EPriorityNormal),
+ iVideoOutputBufferReturnQue(_FOFF(TVideoOutputBuffer,iLink)),
+ iVideoOutputBufferReturnQueIter(iVideoOutputBufferReturnQue),
+ iHost(aHost)
+ {
+ CActiveScheduler::Add(this);
+ }
+
+// ---------------------------------------------------------
+// CCMRVideoRecorder::CCMRReturnAO::ConstructL()
+// Symbian 2nd phase constructor can leave.
+// ---------------------------------------------------------
+//
+void CCMRVideoRecorder::CCMRReturnAO::ConstructL()
+ {
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::ConstructL() in")));
+ User::LeaveIfError(iMutexObj.CreateLocal());
+ iMutexCreated = ETrue;
+ User::LeaveIfError(iVideoThreadHandle.Open(RThread().Id()));
+ iThreadHandleOpened = ETrue;
+
+ iStatus = KRequestPending;
+ SetActive();
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::ConstructL() out")));
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::CCMRReturnAO::~CCMRReturnAO
+// Destructor for CCMRReturnAO
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+CCMRVideoRecorder::CCMRReturnAO::~CCMRReturnAO()
+ {
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::~CCMRReturnAO")));
+ if ( iMutexCreated )
+ {
+ iMutexObj.Close();
+ }
+ if ( iThreadHandleOpened )
+ {
+ iVideoThreadHandle.Close();
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::CCMRReturnAO::EnqueueReturnBuffer
+// Enqueue TVideoOutputBuffer from controller thread to be returned to
+// DevVideoRecord from this thread
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::CCMRReturnAO::EnqueueReturnBuffer(TVideoOutputBuffer* aBuffer)
+ {
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::EnqueueReturnBuffer() in aBuffer=0x%x "), aBuffer));
+ // enter restricted area
+ iMutexObj.Wait();
+ iVideoOutputBufferReturnQue.AddLast(*aBuffer);
+ if ( iStatus == KRequestPending )
+ {
+ TRequestStatus* tmp = &iStatus;
+ iVideoThreadHandle.RequestComplete(tmp, KErrNone);
+ }
+ // leave restricted area
+ iMutexObj.Signal();
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::EnqueueReturnBuffer() out")));
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::CCMRReturnAO::Flush
+// Flush content of return queueu to DevVideo
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::CCMRReturnAO::Flush()
+ {
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::Flush() in.")));
+ iMutexObj.Wait();
+ while (!iVideoOutputBufferReturnQue.IsEmpty())
+ {
+ // take the oldest buffer
+ TVideoOutputBuffer* tmp = iVideoOutputBufferReturnQue.First();
+ // Remove the picture from the list
+ tmp->iLink.Deque();
+ // send it to DevVR using the CCMRVideoRecorder object
+ iHost->ReturnBufferToDevVR(tmp);
+ tmp = NULL;
+ }
+ // leave restricted area
+ iMutexObj.Signal();
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::Flush() out")));
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::CCMRReturnAO::RunL
+// Returns the queued buffers
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::CCMRReturnAO::RunL()
+ {
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::RunL() - iStatus: %d"), iStatus.Int() ));
+ SetActive();
+ // enter restricted area
+ iMutexObj.Wait();
+ iStatus = KRequestPending;
+ while (!iVideoOutputBufferReturnQue.IsEmpty())
+ {
+ // take the oldest buffer
+ TVideoOutputBuffer* tmp = iVideoOutputBufferReturnQue.First();
+ // Remove the picture from the list
+ tmp->iLink.Deque();
+ // send it to DevVR using the CCMRVideoRecorder object
+ iHost->ReturnBufferToDevVR(tmp);
+ tmp = NULL;
+ }
+ // leave restricted area
+ iMutexObj.Signal();
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::RunL() out")));
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::CCMRReturnAO::RunError
+// Handles errors from RunL
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TInt CCMRVideoRecorder::CCMRReturnAO::RunError(TInt aError)
+ {
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::RunError")));
+ // currently RunL can't leave, but keep this here to remind the possible need in the future
+ if (aError != KErrNone)
+ {
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::RunError() error %d"),aError));
+ }
+ return KErrNone;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMRVideoRecorder::CCMRReturnAO::DoCancel
+// Cancels the active object
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCMRVideoRecorder::CCMRReturnAO::DoCancel()
+ {
+ // Cancel the request
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::DoCancel() in")));
+ if ( iStatus == KRequestPending )
+ {
+ TRequestStatus *stat = &iStatus;
+ User::RequestComplete(stat, KErrCancel);
+ }
+ PRINT((_L("CCMRVideoRecorder::CCMRReturnAO::DoCancel() out")));
+ }
+
+// End of file