--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/camcordermmfplugin/filecomposer/Src/CamC3GPDataSinkImp.cpp Thu Dec 17 08:51:24 2009 +0200
@@ -0,0 +1,2301 @@
+/*
+* Copyright (c) 2002-2006 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: 3GP file composer
+*
+*/
+
+// INCLUDE FILES
+#include <f32file.h>
+#include <mmf/server/mmffile.h>
+#include <sysutildomaincrkeys.h> // for critical disk level CentralRepository keys
+#include <3gplibrary/mp4lib.h>
+#include "CamC3GPDataSink.h"
+#include "CamC3GPDataSinkImp.h"
+#include "CCMRMediaSink.h"
+#include "CCMRSupportedCodecs.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/unistd.h>
+
+// MACROS
+// Debug print macro
+#ifdef _DEBUG
+#include <e32svr.h>
+#define PRINT(x) RDebug::Print x
+#else
+#define PRINT(x)
+#endif
+
+// CONSTANTS
+const TUint KVideoAverageBitRate = 48000; // Default average bitrate for video
+const TUint KVideoMaxBitRate = 64000; // Default maximum bitrate for video
+const TUint KVideoXResolution = 176; // Default width of video
+const TUint KVideoYResolution = 144; // Default height of video
+const TUint KVideoBufferSize = 8192; // Maximum video frame size
+const TUint KAudioMaxFrameSize = 32; // Maximum AMR audio frame size
+const TUint KAudioBufferNumber = 10; // Number of audio frames to be buffered
+const TUint KVideoTimeScale = 30000; // Video timescale in the output file; use 30000 since it corresponds roughly to ~30 fps.
+ // 29970 would be exact for H.263, but it may not be exactly camera's baseframerate. And it may be harder for some players than 30000
+ // The most important is now that this value is in the same scale as used in MediaRecorder for Camera API framerate.
+ // If that is 15, then this should be 30000. But some other, e.g. 14.985 fps is used there, then it is better to change this one too.
+const TUint KAudioTimeScale = 8000; // Audio timescale in the output file, safer to use the same value as sampling rate although makes the times more difficult to understand
+const TUint KAudioFrameDuration = 8*20; // AMR frame is 20 ms, but timescale is 8000 => duration is 160 / 8000 s
+const TUint8 KAMRAudioFramesPerSample = 10; // Number of AMR audio frames per sample in the output file
+const TUint8 KAACAudioFramesPerSample = 1; // Number of AAC audio frames per sample in the output file
+const TUint KAACDefaultSampleRate = 16000; // Default samplerate for AAC that is used as audio track timescale in case we get no audiobuffers, but audio codec is selected.
+const TUint16 KAudioModeSet = 0x81ff; // AMR modeset: all modes possible
+const TUint KDiskSafetyLimit = 400000; // Amount of free disk space to leave unused
+const TReal KMetaDataCoeff = 1.03; // Coefficient to estimate metadata amount
+const TInt KFreeDiskSpaceCounter = 10; // Interval when to find out real free disk space
+const TUint KFTYPSize = 24; // Size of FTYP box in bytes in 3GP files
+const TUint KCamCMaxClipDurationInSecs = 5400; // Maximun video clip duration in seconds
+const TInt KLimitForLargeFileBuffers = 100000; // Bitrate limit to toggle to larger output file buffers in composer.
+const TInt KDelayUseBitrates = 3000000; // Delay used in GetRemainingTime until function uses real file sizes instead of avarage bitrates.
+const TInt KCamC3GPDeleteFileQueueGranularity = 10; // Optimal value is the number of temporary files
+const TUint32 KCamC3GPMaximumFileSize = 4294967295UL; // max size for RFile
+
+_LIT(KTmpFileName, "\\system\\Temp\\CamcorderTMP"); // Temporary output file name
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSink::NewL
+//
+// Sink constructor.
+// -----------------------------------------------------------------------------
+//
+EXPORT_C CCamC3GPDataSink* CCamC3GPDataSink::NewL(M3GPDataSinkObserver *aObserver)
+ {
+ CCamC3GPDataSinkImp* self = new (ELeave) CCamC3GPDataSinkImp();
+ CleanupStack::PushL(self);
+ self->ConstructL(aObserver);
+ CleanupStack::Pop();
+ return self;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::~CCamC3GPDataSinkImp
+//
+// Sink destructor.
+// -----------------------------------------------------------------------------
+//
+CCamC3GPDataSinkImp::~CCamC3GPDataSinkImp(void)
+ {
+ PRINT(_L("CCamC3GPDataSinkImp::~CCamC3GPDataSinkImp enter"));
+ delete [] iVideoBuffer;
+ delete [] iAudioBuffer;
+
+ if ( iFileName != KNullDesC )
+ {
+ // we need to try to stop sink
+ TInt error = KErrNone;
+ TRAP(error, SinkStopL());
+ iFileName = KNullDesC;
+ }
+
+ if (iMP4Handle)
+ {
+ MP4ComposeClose(iMP4Handle);
+ iMP4Handle = NULL;
+ }
+
+ if (iFS)
+ {
+ iFS->Delete(iTmpFileName);
+ iFS->Close();
+ delete iFS;
+ iFS = NULL;
+ }
+
+ iObserver = NULL;
+ iMMFFile = NULL; // not owned
+ iFile = NULL; // not owned
+
+ // Delete the CIdle async file remover object
+ delete iIdleDelete;
+ iIdleDelete = 0;
+
+ // Do async temp file deletion for rest of files
+ if ( iDeleteFileQueue )
+ {
+ // Reset and destroy the file name pointers from queue (if any left).
+ iDeleteFileQueue->ResetAndDestroy();
+ }
+ delete iDeleteFileQueue;
+ iDeleteFileQueue = 0;
+ PRINT(_L("CCamC3GPDataSinkImp::~CCamC3GPDataSinkImp exit"));
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::ConstructL
+//
+// Symbian 2nd phase constructor.
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::ConstructL(M3GPDataSinkObserver *aObserver)
+ {
+ PRINT(_L("CCamC3GPDataSinkImp::ConstructL enter"));
+ iObserver = aObserver;
+
+ iVideoBuffer = new (ELeave) TUint8[KVideoBufferSize];
+ iVideoBufferSize = KVideoBufferSize;
+
+ iAudioBuffer = new (ELeave) TUint8[KAudioMaxFrameSize * KAudioBufferNumber];
+ iAudioBufferSize = KAudioMaxFrameSize * KAudioBufferNumber;
+
+ iMP4Handle = NULL;
+ iMMFFile = NULL;
+
+ iFileName = KNullDesC;
+
+ iBytesReceived = 0;
+ iBytesOfMetadata = 0;
+
+ iVideoXResolution = 0;
+ iVideoYResolution = 0;
+ iVideoAverageBitRate = -1;
+ iVideoMaxBitRate = -1;
+ iAudioAverageBitRate = -1;
+
+ iBufferSize = 0;
+ iFileCodecType = MP4_TYPE_NONE;
+ iVideoTimestamp = 0;
+ iVideoBufferTimestamp = -1;
+ iFirstVideoFrameTimestamp = 0;
+ iVideoRandomAccessPoint = EFalse;
+ iVideoBufferRandomAccessPoint = EFalse;
+ iVideoFrameDuration = 0;
+ iVideoBufferFrameSize = 0;
+ iVideoFrameNumber = 0;
+ iVideoIntraFrameNumber = 0;
+ iVideoDecSpecInfoSize = 0;
+ iAudioDecSpecInfoSize = 0;
+ iAudioBufferFrameSize = 0;
+ iAudioFrameNumber = 0;
+ iAudioFramesInBuffer = 0;
+ iFreeDiskSpace = 0;
+ iFreeDiskSpaceCounter = 0;
+ iAvailableSpaceAtStart = 0;
+ iAvarageEndTime = -1;
+ iAudioAACFrameDuration = 0;
+ iAudioAACSamplerate = 0;
+ iAVCOutputLevel = 10;
+ iCriticalDiskVal = 0;
+
+ iSizeLimit = KCamC3GPMaximumFileSize; // max size for RFile
+ iFileSizeLimitReached = EFalse;
+ iDiskFull = EFalse;
+ iFS = NULL;
+
+ iDeleteFileQueue = new( ELeave ) RPointerArray<MP4FileName>( KCamC3GPDeleteFileQueueGranularity );
+
+ // Idle priority Active object for async video temp file deletion
+ iIdleDelete = CIdle::NewL( CActive::EPriorityIdle );
+
+ PRINT(_L("CCamC3GPDataSinkImp::ConstructL exit"));
+ }
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::OpenFileL
+//
+// Opens a 3GP file for writing.
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::OpenFileL(CMMFFile* aMMFFile, TFourCC aAudioCodecType, const TDesC8& aVideoCodecType, TCamCSinkFileFormat aFileFormat )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL CMMFFile enter")));
+
+ TFileName aFileName;
+ iMMFFile = aMMFFile;
+ aFileName = iMMFFile->FullName();
+
+ OpenFileL(aFileName, aAudioCodecType, aVideoCodecType, aFileFormat);
+ }
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::OpenFileL
+//
+// Opens a 3GP file for writing.
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::OpenFileL(TFileName aFileName, TFourCC aAudioCodecType, const TDesC8& aVideoCodecType, TCamCSinkFileFormat aFileFormat)
+ {
+ MP4Err error;
+
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL TFileName enter")));
+
+ if (iMP4Handle)
+ {
+ User::Leave(KErrGeneral);
+ }
+
+ iVideoBufferTimestamp = -1; // reset duration information for new file
+ iFirstVideoFrameTimestamp = 0;
+ iAvailableSpaceAtStart = 0;
+ iAvarageEndTime = -1;
+ iVideoFrameNumber = 0;
+ iVideoIntraFrameNumber = 0;
+ iBytesReceived = 0;
+ iAudioAACFrameDuration = 0;
+ iAudioAACSamplerate = 0;
+ iFileHandleExists = EFalse;
+
+ iDiskFull = EFalse;
+ iTmpFileName = KTmpFileName;
+ iFileName = aFileName;
+ TInt errorcode;
+
+ if (!iFS) // Don't allocate new file server, if there is one already
+ {
+ iFS = new (ELeave) RFs;
+
+ errorcode = iFS->Connect();
+ if ( errorcode != KErrNone)
+ {
+ delete(iFS);
+ iFS = NULL;
+ User::Leave( errorcode );
+ }
+ }
+
+ TParse fp;
+ User::LeaveIfError(iFS->Parse(iFileName, fp));
+ TPtrC driveletter = fp.Drive();
+ TChar drl = driveletter[0];
+ User::LeaveIfError(iFS->CharToDrive(drl, iDriveNumber));
+
+ // Get critical level for this drive type
+ TDriveInfo driveInfo;
+ iFS->Drive(driveInfo, iDriveNumber);
+
+ iCriticalDiskVal = 0;
+ if ( driveInfo.iType == EMediaRam ) // RAM drives have different critical levent than others
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Asking disk critical level, memtype: EMediaRam")));
+ CRepository* repository = CRepository::NewLC( KCRUidDiskLevel );
+ User::LeaveIfError( repository->Get( KRamDiskCriticalLevel, iCriticalDiskVal ) );
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Got disk critical level: %d"),iCriticalDiskVal ));
+ CleanupStack::PopAndDestroy( repository );
+ }
+ else // Some other media type
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Asking disk critical level, memtype: other")));
+ CRepository* repository = CRepository::NewLC( KCRUidDiskLevel );
+ User::LeaveIfError( repository->Get( KDiskCriticalThreshold, iCriticalDiskVal ) );
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Got disk critical level: %d"),iCriticalDiskVal ));
+ CleanupStack::PopAndDestroy( repository );
+ }
+
+ errorcode = iFS->MkDirAll(fp.DriveAndPath());
+ if ( (errorcode != KErrAlreadyExists ) && ( errorcode != KErrNone ) )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Error creating output path: '%s', error: %d"), fp.DriveAndPath().Ptr(), errorcode ));
+ User::Leave(errorcode);
+ }
+
+ errorcode = iFS->SetAtt( iFileName, KEntryAttNormal, KEntryAttReadOnly );
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Deleting File: '%s'"), iFileName.Ptr()));
+ errorcode = iFS->Delete(iFileName);
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Deleting File Error: %d"), errorcode));
+
+ if ( errorcode == KErrInUse && iMMFFile )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL using rfiles")));
+ iMMFFile->SinkPrimeL();
+ iFile = &(iMMFFile->FileL());
+ iFileHandleExists = ETrue;
+ }
+ else if ( ( errorcode != KErrNone ) && ( errorcode != KErrNotFound ) && ( errorcode != KErrPathNotFound ) )
+ {
+ User::Leave(errorcode);
+ }
+
+ iTmpFileName.Insert(0, fp.Drive());
+ errorcode = iFS->SetAtt( iTmpFileName, KEntryAttNormal, KEntryAttReadOnly );
+ errorcode = iFS->Delete(iTmpFileName);
+ if ( ( errorcode != KErrNone ) && ( errorcode != KErrNotFound ) && ( errorcode != KErrPathNotFound ) )
+ {
+ if ( errorcode == KErrInUse )
+ {
+ // use actual output filename incase other instance still running with temporary file.
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL temporary output file in use (-14). Using actual output filename instead.")));
+ iTmpFileName = aFileName;
+ errorcode = iFS->SetAtt( iTmpFileName, KEntryAttNormal, KEntryAttReadOnly );
+ errorcode = iFS->Delete(iTmpFileName);
+ if ( ( errorcode != KErrNone ) && ( errorcode != KErrNotFound ) && ( errorcode != KErrPathNotFound ) )
+ {
+ User::Leave(errorcode);
+ }
+ }
+ else
+ {
+ User::Leave(errorcode);
+ }
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Temp files cleared")));
+
+ // Find used audio codec
+ if ( ( aAudioCodecType == TFourCC(KCMRFourCCIdAMRNB) ) ) // AMR-NB
+ {
+ iFileCodecType |= MP4_TYPE_AMR_NB;
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Audio codec: AMR-NB")));
+ }
+ else if ( aAudioCodecType == TFourCC(KCMRFourCCIdMPEG4AAC) ) // AAC
+ {
+ iFileCodecType |= MP4_TYPE_MPEG4_AUDIO;
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Audio codec: AAC")));
+ }
+ else
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Audio codec: none")));
+ // if audio codec is null we don't generate audiotrack to composed file.
+ }
+
+ // Find used video codec
+ mp4_u8 videoLevel = 10;
+
+ if ( aVideoCodecType == KNullDesC8 ) // No video codec set.
+ {
+ // filecomposer will always create videotrack, whether we get video frames or not.
+ iFileCodecType |= MP4_TYPE_H263_PROFILE_0;
+ }
+ else
+ {
+ TBuf8<256> matchstring;
+ matchstring = KCMRMimeTypeH263;
+ matchstring += _L8( "*" );
+
+ if ( ( aVideoCodecType.MatchF( matchstring ) != KErrNotFound ) ) // H.263
+ {
+ matchstring = KCMRMimeTypeH263Profile3;
+ matchstring += _L8( "*" );
+
+ if ( aVideoCodecType.MatchF( matchstring ) != KErrNotFound )
+ {
+ iFileCodecType |= MP4_TYPE_H263_PROFILE_3; // H.263 profile 3
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Video codec: H.263 profile 3")));
+ }
+ else
+ {
+ iFileCodecType |= MP4_TYPE_H263_PROFILE_0; // H.263 profile 0
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Video codec: H.263 profile 0")));
+ }
+ // check if level is indicated too
+ matchstring = _L8("*level=*");
+ if (aVideoCodecType.MatchF( matchstring ) != KErrNotFound )
+ {
+ // yes, there is, check what it is
+ if ( aVideoCodecType.MatchF( _L8("*level=10*") ) != KErrNotFound )
+ {
+ videoLevel = 10;
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Video codec: H.263 level 10")));
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*level=20*") ) != KErrNotFound )
+ {
+ videoLevel = 20;
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Video codec: H.263 level 20")));
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*level=30*") ) != KErrNotFound )
+ {
+ videoLevel = 30;
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Video codec: H.263 level 30")));
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*level=40*") ) != KErrNotFound )
+ {
+ videoLevel = 40;
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Video codec: H.263 level 40")));
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*level=45*") ) != KErrNotFound )
+ {
+ videoLevel = 45;
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Video codec: H.263 level 45")));
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*level=50*") ) != KErrNotFound )
+ {
+ videoLevel = 50;
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Video codec: H.263 level 50")));
+ }
+ else
+ {
+ // assume 10
+ videoLevel = 10;
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Video codec: H.263 level 10")));
+ }
+ }
+ }
+ else // MPEG-4
+ {
+ matchstring = KCMRMimeTypeMPEG4V;
+ matchstring += _L8( "*" );
+
+ if ( aVideoCodecType.MatchF(matchstring) != KErrNotFound )
+ {
+ iFileCodecType |= MP4_TYPE_MPEG4_VIDEO; // MPEG-4
+ }
+ else // H.264 AVC
+ {
+ matchstring = KCMRMimeTypeH264AVC;
+ matchstring += _L8( "*" );
+
+ if ( aVideoCodecType.MatchF(matchstring) != KErrNotFound )
+ {
+ iAVCOutputLevel = 10;
+ // check if profile & level is indicated too
+ matchstring = _L8("*profile-level-id=*");
+ if (aVideoCodecType.MatchF( matchstring ) != KErrNotFound )
+ {
+ // yes, there is, check what it is
+ // Determine if other AVC profile is used:
+ matchstring = _L8( "*profile-level-id=42*" ); // Main Profile
+ if (aVideoCodecType.MatchF( matchstring ) != KErrNotFound)
+ {
+ iFileCodecType |= MP4_TYPE_AVC_PROFILE_BASELINE; // H.264 AVC Baseline profile found
+ }
+ matchstring = _L8( "*profile-level-id=4D*" ); // Main Profile
+ if (aVideoCodecType.MatchF( matchstring ) != KErrNotFound)
+ {
+ iFileCodecType |= MP4_TYPE_AVC_PROFILE_MAIN; // H.264 AVC Main profile found
+ }
+ matchstring = _L8( "*profile-level-id=64*" ); // High Profile
+ if (aVideoCodecType.MatchF( matchstring ) != KErrNotFound)
+ {
+ iFileCodecType |= MP4_TYPE_AVC_PROFILE_HIGH; // H.264 AVC Baseline profile found
+ }
+
+ // Determine if other AVC level is used:
+ if ( aVideoCodecType.MatchF( _L8("*00A*") ) != KErrNotFound )
+ {
+ iAVCOutputLevel = 10; // Level 1
+ }
+ else if ( (aVideoCodecType.MatchF( _L8("*profile-level-id=42900B*") ) != KErrNotFound) ||
+ (aVideoCodecType.MatchF( _L8("*profile-level-id=4D500B*") ) != KErrNotFound) ||
+ (aVideoCodecType.MatchF( _L8("*profile-level-id=644009*") ) != KErrNotFound) )
+ {
+ iAVCOutputLevel = 101; // Level 1b
+ }
+ else if ( (aVideoCodecType.MatchF( _L8("*profile-level-id=42800B*") ) != KErrNotFound) ||
+ (aVideoCodecType.MatchF( _L8("*profile-level-id=4D400B*") ) != KErrNotFound) ||
+ (aVideoCodecType.MatchF( _L8("*profile-level-id=64400B*") ) != KErrNotFound) )
+ {
+ iAVCOutputLevel = 11; // Level 1.1
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*00C*") ) != KErrNotFound )
+ {
+ iAVCOutputLevel = 12; // Level 1.2
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*00D*") ) != KErrNotFound )
+ {
+ iAVCOutputLevel = 13; // Level 1.3
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*014*") ) != KErrNotFound )
+ {
+ iAVCOutputLevel = 20; // Level 2
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*015*") ) != KErrNotFound )
+ {
+ iAVCOutputLevel = 21; // Level 2.1
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*016*") ) != KErrNotFound )
+ {
+ iAVCOutputLevel = 22; // Level 2.2
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*01E*") ) != KErrNotFound )
+ {
+ iAVCOutputLevel = 30; // Level 3
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*01F*") ) != KErrNotFound )
+ {
+ iAVCOutputLevel = 31; // Level 3.1
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*020*") ) != KErrNotFound )
+ {
+ iAVCOutputLevel = 32; // Level 3.2
+ }
+ else if ( aVideoCodecType.MatchF( _L8("*028*") ) != KErrNotFound )
+ {
+ iAVCOutputLevel = 4; // Level 4
+ }
+ else
+ {
+ // assume level 1
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Warning unknown video codec type - defaulting level 1.0")));
+ iAVCOutputLevel = 10;
+ }
+ }
+ }
+ else
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Unsupported video codec type")));
+ User::Leave(KErrArgument);
+ }
+ }
+ }
+ }
+
+ if ( iFileHandleExists )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL using rfile to open mp4handle")));
+ error = MP4ComposeOpenFileHandle(&iMP4Handle, iFile, (TDriveNumber)iDriveNumber, iFileCodecType);
+ }
+ else
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL using descriptors to open mp4handle")));
+ error = MP4ComposeOpen(&iMP4Handle, (MP4FileName)iTmpFileName.Ptr(), iFileCodecType);
+ }
+
+ if ( error == MP4_OUT_OF_MEMORY )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL MP4ComposeOpen, error=%d"), error));
+ User::Leave(KErrNoMemory);
+ }
+ else if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL MP4ComposeOpen, error=%d"), error));
+ TInt64 currentdiskspace = DriveFreeSpaceL();
+ iFileName = KNullDesC;
+
+ if ( currentdiskspace < ((TInt64)KDiskSafetyLimit+iCriticalDiskVal+CurrentMetadataSize()) )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL disk full, available: %d"), I64INT(currentdiskspace)));
+ iDiskFull = ETrue;
+ User::Leave(KErrDiskFull);
+ }
+ else
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Compose and Open failed, error=%d"), error));
+ iFileName = KNullDesC;
+ User::Leave(KErrGeneral);
+ }
+ }
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Compose and Open done")));
+
+ mp4_u32 composeFlags = 0;
+ if ( aFileFormat == E3GPP2 )
+ {
+ composeFlags |= ( MP4_FLAG_METADATALAST | MP4_FLAG_LONGCLIP | MP4_FLAG_GENERATE_3G2);
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Fileformat: 3G2")));
+ }
+ else if ( aFileFormat == EMPEG4 )
+ {
+ composeFlags |= ( MP4_FLAG_METADATALAST | MP4_FLAG_LONGCLIP | MP4_FLAG_GENERATE_MP4);
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Fileformat: MP4")));
+ }
+ else // E3GPP
+ {
+ composeFlags |= ( MP4_FLAG_METADATALAST | MP4_FLAG_LONGCLIP );
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Fileformat: 3GP")));
+ }
+
+ if ( ( iVideoMaxBitRate + iAudioAverageBitRate ) >= KLimitForLargeFileBuffers )
+ {
+ composeFlags |= MP4_FLAG_LARGEFILEBUFFER;
+ }
+
+ error = MP4ComposeSetFlags(iMP4Handle, composeFlags );
+ if ( error == MP4_OUT_OF_MEMORY )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL MP4ComposeSetFlags, error=%d"), error));
+ User::Leave(KErrNoMemory);
+ }
+ else if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL MP4ComposeSetFlags, error=%d"), error));
+ TInt64 currentdiskspace2 = DriveFreeSpaceL();
+
+ if ( currentdiskspace2 < ((TInt64)KDiskSafetyLimit+iCriticalDiskVal+CurrentMetadataSize()) )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL disk full, available: %d"), I64INT(currentdiskspace2)));
+ iDiskFull = ETrue;
+ User::Leave(KErrDiskFull);
+ }
+ else
+ {
+ User::Leave(KErrGeneral);
+ }
+ }
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Compose flags set")));
+
+ if ((iFileCodecType & MP4_TYPE_H263_PROFILE_0) ||
+ (iFileCodecType & MP4_TYPE_H263_PROFILE_3) ||
+ (iFileCodecType & MP4_TYPE_MPEG4_VIDEO) ||
+ (iFileCodecType & MP4_TYPE_AVC_PROFILE_BASELINE) ||
+ (iFileCodecType & MP4_TYPE_AVC_PROFILE_MAIN) ||
+ (iFileCodecType & MP4_TYPE_AVC_PROFILE_HIGH))
+ {
+ // Set default values for video parameters if they are not set
+ if (iVideoAverageBitRate < 0)
+ {
+ iVideoAverageBitRate = KVideoAverageBitRate;
+ }
+ if (iVideoMaxBitRate < 0)
+ {
+ iVideoMaxBitRate = KVideoMaxBitRate;
+ }
+ if (iVideoXResolution == 0)
+ {
+ iVideoXResolution = KVideoXResolution;
+ }
+ if (iVideoYResolution == 0)
+ {
+ iVideoYResolution = KVideoYResolution;
+ }
+
+ error = MP4ComposeAddVideoDescription(iMP4Handle,
+ (mp4_u32)KVideoTimeScale,
+ (mp4_u16)iVideoXResolution,
+ (mp4_u16)iVideoYResolution,
+ (mp4_u32)iVideoMaxBitRate,
+ (mp4_u32)iVideoAverageBitRate);
+
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL MP4ComposeAddVideoDescription, error=%d"), error));
+ if (error != MP4_OK)
+ {
+ User::Leave(KErrGeneral);
+ }
+ if ( (iFileCodecType & (MP4_TYPE_H263_PROFILE_0 | MP4_TYPE_H263_PROFILE_3))
+ && ( videoLevel != 10) )
+ {
+ // H.263 level should be given to 3gp library like this
+ error = MP4ComposeWriteVideoDecoderSpecificInfo(iMP4Handle,
+ (mp4_u8*)&videoLevel,
+ (mp4_u32)1);
+ if ( error != MP4_OK )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL MP4ComposeWriteVideoDecoderSpecificInfo, error=%d"), error));
+ User::Leave(KErrGeneral);
+ }
+ }
+ }
+
+ if (iFileCodecType & MP4_TYPE_AMR_NB)
+ {
+ error = MP4ComposeAddAudioDescription(iMP4Handle,
+ (mp4_u32)KAudioTimeScale,
+ (mp4_u8)KAMRAudioFramesPerSample,
+ (mp4_u16)KAudioModeSet);
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL MP4ComposeAddAudioDescription, AMR-NB error=%d"), error));
+ if (error != MP4_OK)
+ {
+ User::Leave(KErrGeneral);
+ }
+ }
+
+ if ( iFileCodecType & MP4_TYPE_MPEG4_AUDIO)
+ {
+ // To make sure we always write proper timescale to output file (even in case when we get no audio
+ // buffers) the default samplerate is passed to 3GP library here.
+ // When we get audio decoder specific information in WriteBuffer() the MP4ComposeAddAudioDescription()
+ // will be called again with correct samplerate.
+ error = MP4ComposeAddAudioDescription(iMP4Handle,
+ (mp4_u32)KAACDefaultSampleRate,
+ (mp4_u8)KAACAudioFramesPerSample,
+ (mp4_u16)KAudioModeSet);
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL MP4ComposeAddAudioDescription, AAC error=%d"), error));
+ if (error != MP4_OK)
+ {
+ User::Leave(KErrGeneral);
+ }
+ }
+
+ if ( iAvailableSpaceAtStart == 0 )
+ {
+ TVolumeInfo volumeinfo;
+ User::LeaveIfError(iFS->Volume(volumeinfo, iDriveNumber));
+ iAvailableSpaceAtStart = volumeinfo.iFree - (TInt64)(KDiskSafetyLimit+iCriticalDiskVal);
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Setting async file remover handler")));
+ error = MP4ComposeSetTempFileRemoverObserver(&iMP4Handle, this);
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL Setting async file remover handler FAILED")));
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::OpenFileL exit")));
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::SetSizeLimit
+//
+// Set size limit of the 3GP file to be recorded in bytes. The limit must be
+// set before the recording starts.
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::SetSizeLimit(TUint aSize)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::SetSizeLimit enter, requested limit: %u"), aSize));
+ if (iBytesReceived)
+ {
+ PRINT(_L("CCamC3GPDataSinkImp::SetSizeLimit NOT set, recording"));
+ return;
+ }
+
+ TInt64 rfileMaxSize = KCamC3GPMaximumFileSize;
+ if ( aSize == 0 || aSize > rfileMaxSize )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::SetSizeLimit 0 or over RFile max size, using internal max instead.")));
+ iSizeLimit = rfileMaxSize; //max size for RFile
+ }
+ else
+ {
+ iSizeLimit = aSize;
+ }
+ PRINT((_L("CCamC3GPDataSinkImp::SetSizeLimit set to: high:%u low:%u"), I64HIGH(iSizeLimit), I64LOW(iSizeLimit)));
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::GetElapsedTime
+//
+// Return the amount of time recording has been on in microseconds.
+// -----------------------------------------------------------------------------
+//
+TTimeIntervalMicroSeconds CCamC3GPDataSinkImp::GetElapsedTime()
+ {
+ PRINT(_L("CCamC3GPDataSinkImp::GetElapsedTime in"));
+ TTimeIntervalMicroSeconds elapsed;
+ if (iVideoBufferTimestamp < TTimeIntervalMicroSeconds(0))
+ {
+ elapsed = 0;
+ }
+ else
+ {
+ elapsed = iVideoBufferTimestamp.Int64() - iFirstVideoFrameTimestamp.Int64();
+ }
+ PRINT((_L("CCamC3GPDataSinkImp::GetElapsedTime out, elapsed=%d"), I64INT(elapsed.Int64()) ));
+ return elapsed;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::GetRemainingTime
+//
+// Return the estimated remaining time for the recording in microseconds.
+// This method takes into account the file size and disk full restrictions.
+// -----------------------------------------------------------------------------
+//
+TTimeIntervalMicroSeconds CCamC3GPDataSinkImp::GetRemainingTimeL()
+ {
+ TTimeIntervalMicroSeconds elapsed;
+ TTimeIntervalMicroSeconds remaining;
+ TTimeIntervalMicroSeconds endtime;
+ TInt64 availableSpace;
+ TInt64 usedSpace;
+ TInt64 metaDataSize;
+ TBool remainingFromSizeLimit = EFalse;
+
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL in")));
+
+ if ( iAvailableSpaceAtStart == 0 )
+ {
+ TVolumeInfo volumeinfo;
+ User::LeaveIfError(iFS->Volume(volumeinfo, iDriveNumber));
+ iAvailableSpaceAtStart = volumeinfo.iFree - (TInt64)(KDiskSafetyLimit+iCriticalDiskVal);
+ }
+
+ if (iSizeLimit && ( iSizeLimit < iAvailableSpaceAtStart ) )
+ {
+ // use sizelimit as available space.
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL Limiting remainingtime by sizelimit: %d."), I64INT(iSizeLimit)));
+ remainingFromSizeLimit = ETrue;
+ }
+ else
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL Limiting remainingtime by available space at start: %d "), I64INT(iAvailableSpaceAtStart) ));
+ }
+
+ if ( iVideoBufferTimestamp < TTimeIntervalMicroSeconds(0) )
+ {
+ elapsed = 0;
+ }
+ else
+ {
+ elapsed = iVideoBufferTimestamp.Int64() - iFirstVideoFrameTimestamp.Int64();
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL elapsed with first=%d current=%d elapsed=%d"), I64INT(iFirstVideoFrameTimestamp.Int64()), I64INT(iVideoBufferTimestamp.Int64()), I64INT(elapsed.Int64()) ));
+ }
+
+ if (elapsed < (TTimeIntervalMicroSeconds)((TInt64)KDelayUseBitrates) )
+ {
+ // Use average audio/video bitrates to estimate remaining time
+ TUint averageBitRate;
+ TUint averageByteRate;
+
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL: BitRates Video: %d , Audio: %d"), iVideoAverageBitRate, iAudioAverageBitRate ));
+
+ averageBitRate = (TUint)((iVideoAverageBitRate + iAudioAverageBitRate) * KMetaDataCoeff);
+ averageByteRate = averageBitRate / 8;
+
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL: avaragebitrate: %d , avaragebyterate: %d"), averageBitRate, averageByteRate ));
+
+ usedSpace = elapsed.Int64() * averageByteRate / 1000000; // 1000000 is for conversion between microseconds and seconds
+
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL: elapsed: %d, usedspace: %d"), I64INT(elapsed.Int64()), usedSpace ));
+
+ metaDataSize = 2*CurrentMetadataSize();
+ if (remainingFromSizeLimit)
+ {
+ availableSpace = iSizeLimit - usedSpace - metaDataSize;
+ }
+ else
+ {
+ availableSpace = iAvailableSpaceAtStart - usedSpace - metaDataSize;
+ }
+
+ if (availableSpace <= 0 || averageByteRate == 0)
+ {
+ remaining = 0;
+ }
+ else
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL: availablespace: %d, atStart: %d "), I64INT(elapsed.Int64()), I64INT(iAvailableSpaceAtStart) ));
+
+ remaining = availableSpace * 1000000 / averageByteRate; // 1000000 is for conversion between microseconds and seconds
+
+ if ( (remaining.Int64() + elapsed.Int64()) > (TInt64(KCamCMaxClipDurationInSecs)*1000000) )
+ {
+ remaining = (TInt64(KCamCMaxClipDurationInSecs)*1000000) - elapsed.Int64();
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL: remaining: %d "), I64INT(remaining.Int64()) ));
+ }
+
+ // update also iAvarageEndTime to smooth jump at KDelayUseBitrates sec point.
+ iAvarageEndTime = elapsed.Int64()+ remaining.Int64();
+ }
+ else // use real filesize estimates.
+ {
+ // used space is mediadata + 2x metadata (metadata in temp-files written and additionaö reserved space for stop copying it to output file.
+ usedSpace = CurrentFileSize()+ 2*CurrentMetadataSize();
+ if (remainingFromSizeLimit)
+ {
+ availableSpace = iSizeLimit - usedSpace;
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL availableSpace from sizelimit: %d "), I64INT(iAvailableSpaceAtStart) ));
+ }
+ else
+ {
+ availableSpace = iAvailableSpaceAtStart - usedSpace;
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL availableSpace from availablespaceAtStart: %d "), I64INT(iAvailableSpaceAtStart) ));
+ }
+
+ if (availableSpace <= 0)
+ {
+ remaining = 0;
+ }
+ else
+ {
+ // preserve integer precision by scaling the first dividend up in calculation
+ if (remainingFromSizeLimit)
+ {
+ // divide the greater of iSizeLimit and elapsed with usedSpace first to prevent overflow
+ if ( iSizeLimit > elapsed.Int64() )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL iSizeLimit > elapsed: %d vs. %d"), I64INT(iSizeLimit), I64INT(elapsed.Int64()) ));
+ endtime = (((iSizeLimit * 1000) / usedSpace ) * elapsed.Int64() ) / 1000;
+ }
+ else
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL iSizeLimit < elapsed: %d vs. %d"), I64INT(iSizeLimit), I64INT(elapsed.Int64()) ));
+ endtime = ( iSizeLimit * ( (elapsed.Int64() * 1000) / usedSpace )) / 1000;
+ }
+ }
+ else
+ {
+ // divide the greater of iAvailableSpaceAtStart and elapsed with usedSpace first to prevent overflow
+ if (iAvailableSpaceAtStart > elapsed.Int64() )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL iAvailableSpaceAtStart > elapsed: %d vs. %d"), I64INT(iAvailableSpaceAtStart), I64INT(elapsed.Int64()) ));
+ endtime = (( (iAvailableSpaceAtStart * 1000) / usedSpace ) * elapsed.Int64() ) / 1000;
+ }
+ else
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL iAvailableSpaceAtStart < elapsed: %d vs. %d"), I64INT(iAvailableSpaceAtStart), I64INT(elapsed.Int64()) ));
+ endtime = ( iAvailableSpaceAtStart * ( (elapsed.Int64() * 1000) / usedSpace )) / 1000;
+ }
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL Endtime: %d"), I64INT(endtime.Int64()) ));
+ if ( iAvarageEndTime.Int64() == -1 )
+ {
+ iAvarageEndTime = endtime;
+ }
+ else
+ {
+ iAvarageEndTime = (( iAvarageEndTime.Int64() * 7 ) + endtime.Int64() ) / 8;
+ }
+
+ if ( iAvarageEndTime.Int64() > (TInt64(KCamCMaxClipDurationInSecs)*1000000) )
+ {
+ iAvarageEndTime = (TInt64(KCamCMaxClipDurationInSecs)*1000000);
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL elapsed: %d , usedspace: %d"), I64INT(elapsed.Int64()), I64INT(usedSpace) ));
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL available: %d, atStart: %d"), I64INT(availableSpace), iAvailableSpaceAtStart));
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL AvgEndtime: %d"), I64INT(iAvarageEndTime.Int64()) ));
+ remaining = iAvarageEndTime.Int64() - elapsed.Int64();
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL remaining: %d"), I64INT(remaining.Int64()) ));
+ }
+ }
+
+ // Check if remaining time has reached 0 and we need to stop right away
+ if ( remaining <= TInt64(0) )
+ {
+ if ( elapsed >= TInt64(0) ) // we are recording.
+ {
+ if ( remainingFromSizeLimit || (elapsed >= (TInt64(KCamCMaxClipDurationInSecs)*1000000)) )
+ {
+ // Size limit has been set and we reach wanted size -> remaining time is 0
+ iFileSizeLimitReached = ETrue;
+ iObserver->MfcoSizeLimitReachedL();
+ }
+ else
+ {
+ // Diskfull
+ iDiskFull = ETrue;
+ iObserver->MfcoDiskFullL();
+ }
+ }
+ remaining = 0;
+ }
+ PRINT((_L("CCamC3GPDataSinkImp::GetRemainingTimeL out")));
+ return remaining;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::SinkStopL
+//
+// Order the sink to finalize and close the current 3GP file.
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::SinkStopL()
+ {
+ MP4Err error;
+
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL enter")));
+
+ if (!iMP4Handle)
+ {
+ return;
+ }
+
+ if (iVideoFrameDuration) // Write remaining video frame to disk
+ {
+ error = MP4ComposeWriteVideoFrame(iMP4Handle,
+ (mp4_u8 *)iVideoBuffer,
+ (mp4_u32)iVideoBufferFrameSize,
+ (mp4_u32)iVideoFrameDuration,
+ (mp4_bool)iVideoBufferRandomAccessPoint);
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL MP4ComposeWriteVideoFrame, error=%d"), error));
+ User::Leave(KErrGeneral);
+ }
+ }
+
+
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL video frames written")));
+
+
+ if (iAudioFramesInBuffer) // Write remaining audio frames to disk
+ {
+ error = MP4ComposeWriteAudioFrames(iMP4Handle,
+ (mp4_u8 *)iAudioBuffer,
+ (mp4_u32)iAudioBufferFrameSize,
+ (mp4_u32)iAudioFramesInBuffer,
+ (mp4_u32)iAudioFramesInBuffer * KAudioFrameDuration);
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL MP4ComposeWriteAudioFrames, error=%d"), error));
+ User::Leave(KErrGeneral);
+ }
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL audio frames written")));
+
+
+ error = MP4ComposeClose(iMP4Handle);
+ iMP4Handle = NULL;
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL MP4ComposeClose, error=%d"), error));
+ TInt64 currentdiskspace = DriveFreeSpaceL();
+
+ if ( currentdiskspace < ((TInt64)KDiskSafetyLimit+iCriticalDiskVal+CurrentMetadataSize()) )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL disk full, available: %d"), I64INT(currentdiskspace)));
+ iDiskFull = ETrue;
+ User::Leave(KErrDiskFull);
+ }
+ else
+ {
+ User::Leave(KErrGeneral);
+ }
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL file composed and closed")));
+
+ if ( iFileHandleExists )
+ {
+ iMMFFile->SinkStopL();
+ iMMFFile = NULL; // not owned
+ }
+ else
+ {
+ if (!iBytesReceived)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL No data written, iTmpFileName left: '%s'"), iTmpFileName.Ptr()));
+ }
+ else
+ {
+ iFS->SetEntry(iTmpFileName, TTime(0), NULL, KEntryAttHidden);
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL Renaming temp file to: '%s'"), iFileName.Ptr()));
+ User::LeaveIfError(iFS->Rename(iTmpFileName, iFileName));
+ }
+ }
+
+ iFileName = KNullDesC;
+
+ iBytesOfMetadata = 0;
+ iBufferSize = 0;
+ iFileCodecType = MP4_TYPE_NONE;
+ iVideoTimestamp = 0;
+ iVideoBufferTimestamp = -1;
+ iVideoRandomAccessPoint = EFalse;
+ iVideoBufferRandomAccessPoint = EFalse;
+ iVideoFrameDuration = 0;
+ iVideoBufferFrameSize = 0;
+ iVideoDecSpecInfoSize = 0;
+ iAudioDecSpecInfoSize = 0;
+ iAudioBufferFrameSize = 0;
+ iAudioFrameNumber = 0;
+ iAudioFramesInBuffer = 0;
+ iFileSizeLimitReached = EFalse;
+ iDiskFull = EFalse;
+ iFreeDiskSpace = 0;
+ iFreeDiskSpaceCounter = 0;
+ iAudioAACFrameDuration = 0;
+ iAudioAACSamplerate = 0;
+
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL() updating available diskspace - in")));
+ TVolumeInfo volumeinfo;
+ if ( iFS && iFS->Volume(volumeinfo, iDriveNumber) == KErrNone )
+ {
+ iAvailableSpaceAtStart = volumeinfo.iFree - (TInt64)(KDiskSafetyLimit+iCriticalDiskVal);
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL() updating available diskspace - done")));
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::SinkStopL exit")));
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::WriteBuffer
+//
+// Write an audio/video buffer to the sink. The sink copies the given buffer
+// and writes it to the file.
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::WriteBufferL(CCMRMediaBuffer* aBuffer)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL enter")));
+ TInt64 videoduration = 0;
+ TUint8* tmpaudiobuffer = 0;
+ TInt64 currentfilesize;
+ MP4Err error = MP4_OK;
+
+
+ if (!iMP4Handle)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL No MP4Handle, returning.")));
+ return;
+ }
+
+ if (iFileSizeLimitReached || iDiskFull)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL sizelimit reached or disk full returning")));
+ return;
+ }
+
+ iBufferType = aBuffer->Type();
+ iBufferSize = aBuffer->BufferSize();
+
+ TTimeIntervalMicroSeconds elapsed;
+ if (iVideoBufferTimestamp < TTimeIntervalMicroSeconds(0))
+ {
+ elapsed = 0;
+ }
+ else
+ {
+ elapsed = iVideoBufferTimestamp.Int64() - iFirstVideoFrameTimestamp.Int64();
+ }
+
+ currentfilesize = CurrentFileSize() + CurrentMetadataSize();
+ if ( ( iSizeLimit && ((currentfilesize + (TUint)iBufferSize) > iSizeLimit) ) ||
+ (elapsed.Int64() >= (TInt64(KCamCMaxClipDurationInSecs)*1000000)) )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL sizelimit reached, filesize: %d"), I64INT(currentfilesize)));
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL sizelimit is set to: high:%u low:%u"), I64HIGH(iSizeLimit), I64LOW(iSizeLimit)));
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL elapsed time: high:%u low:%u"), I64HIGH(elapsed.Int64()), I64LOW(elapsed.Int64())));
+ iFileSizeLimitReached = ETrue;
+ iObserver->MfcoSizeLimitReachedL();
+ return;
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL, filesize: %d, bufsize: %d")
+ , I64INT(currentfilesize), iBufferSize));
+
+ currentfilesize += CurrentMetadataSize(); // extra reserve for stop (copy metadata from temp files to end of output file.
+ if ( currentfilesize >= iAvailableSpaceAtStart )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL disk full, calc size: %d"), I64INT(currentfilesize)));
+ iDiskFull = ETrue;
+ iObserver->MfcoDiskFullL();
+ return;
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL, available: %d")
+ , I64INT(iAvailableSpaceAtStart-currentfilesize)));
+
+ iBytesReceived += (TUint)iBufferSize;
+ switch (iBufferType)
+ {
+ case CCMRMediaBuffer::EAudioAMRNB:
+ {
+ iAudioFrameNumber++;
+
+ if ( ( (TUint)iBufferSize + iAudioBufferFrameSize ) > iAudioBufferSize) // Incoming buffer doesn't fit into allocated buffer
+ {
+ tmpaudiobuffer = new (ELeave) TUint8[(TUint)iBufferSize + iAudioBufferFrameSize];
+ Mem::Copy(tmpaudiobuffer, iAudioBuffer, (TInt)iAudioBufferFrameSize);
+ delete [] iAudioBuffer;
+ iAudioBuffer = tmpaudiobuffer;
+ iAudioBufferSize = (TUint)iBufferSize + iAudioBufferFrameSize;
+ }
+
+ Mem::Copy(iAudioBuffer + iAudioBufferFrameSize, aBuffer->Data().Ptr(), iBufferSize);
+ iAudioBufferFrameSize += (TUint)iBufferSize;
+ iAudioFramesInBuffer++;
+
+ if (iAudioFramesInBuffer == KAMRAudioFramesPerSample) // Buffer several audio frames before writing to disk
+ {
+ error = MP4ComposeWriteAudioFrames(iMP4Handle,
+ (mp4_u8 *)iAudioBuffer,
+ (mp4_u32)iAudioBufferFrameSize,
+ (mp4_u32)iAudioFramesInBuffer,
+ (mp4_u32)iAudioFramesInBuffer * KAudioFrameDuration);
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL AMR-NB MP4ComposeWriteAudioFrames, error=%d"), error));
+ if (error != MP4_OK)
+ {
+ User::Leave(KErrGeneral);
+ }
+
+ iAudioFramesInBuffer = 0;
+ iAudioBufferFrameSize = 0;
+ }
+
+ break;
+ }
+ case CCMRMediaBuffer::EAudioMPEG4AAC:
+ {
+ if ( !iAudioAACFrameDuration )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL AAC Audio frameduration not set! Error= -18"), error));
+ User::Leave ( KErrNotReady );
+ }
+
+ iAudioFrameNumber++;
+ error = MP4ComposeWriteAudioFrames(iMP4Handle,
+ (mp4_u8 *)aBuffer->Data().Ptr(),
+ (mp4_u32)aBuffer->BufferSize(),
+ (mp4_u32)KAACAudioFramesPerSample,
+ (mp4_u32)KAACAudioFramesPerSample * iAudioAACFrameDuration);
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL AAC MP4ComposeWriteAudioFrames, error=%d"), error));
+ if (error != MP4_OK)
+ {
+ User::Leave(KErrGeneral);
+ }
+ break;
+ }
+ case CCMRMediaBuffer::EAudioAMRWB:
+ {
+ break;
+ }
+ case CCMRMediaBuffer::EVideoH263:
+ case CCMRMediaBuffer::EVideoMPEG4:
+ {
+ iVideoTimestamp = aBuffer->TimeStamp();
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL EVideoH263/EVideoMPEG4 Timestamp of the buffer is %d"), I64INT(iVideoTimestamp.Int64())));
+ iVideoRandomAccessPoint = aBuffer->RandomAccessPoint();
+ iVideoFrameNumber++;
+ if (iVideoRandomAccessPoint)
+ {
+ iVideoIntraFrameNumber++;
+ }
+
+ if (iVideoBufferTimestamp < TTimeIntervalMicroSeconds(0)) // First frame
+ {
+ iFirstVideoFrameTimestamp = iVideoTimestamp;
+ iVideoBufferTimestamp = iVideoTimestamp;
+ iVideoBufferRandomAccessPoint = iVideoRandomAccessPoint;
+
+ if ((TUint)iBufferSize > iVideoBufferSize)
+ {
+ delete [] iVideoBuffer;
+ iVideoBuffer = new (ELeave) TUint8[(TUint)iBufferSize];
+ iVideoBufferSize = (TUint)iBufferSize;
+ }
+
+ Mem::Copy(iVideoBuffer, aBuffer->Data().Ptr(), iBufferSize);
+ iVideoBufferFrameSize = (TUint)iBufferSize;
+
+ break;
+ }
+
+ videoduration = iVideoTimestamp.Int64() - iVideoBufferTimestamp.Int64(); // Duration in microseconds
+ videoduration = TInt64((videoduration * KVideoTimeScale) / 1E6 + 0.5); // Duration scaled to KVideoTimeScale
+ iVideoFrameDuration = (TUint)I64INT(videoduration);
+
+ error = MP4ComposeWriteVideoFrame(iMP4Handle,
+ (mp4_u8 *)iVideoBuffer,
+ (mp4_u32)iVideoBufferFrameSize,
+ (mp4_u32)iVideoFrameDuration,
+ (mp4_bool)iVideoBufferRandomAccessPoint);
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteVideoFrame, error=%d"), error));
+ if (error != MP4_OK)
+ {
+ User::Leave(KErrGeneral);
+ }
+
+ if ((TUint)iBufferSize > iVideoBufferSize)
+ {
+ delete [] iVideoBuffer;
+ iVideoBuffer = new (ELeave) TUint8[(TUint)iBufferSize];
+ iVideoBufferSize = (TUint)iBufferSize;
+ }
+
+ Mem::Copy(iVideoBuffer, aBuffer->Data().Ptr(), iBufferSize);
+ iVideoBufferFrameSize = (TUint)iBufferSize;
+ iVideoBufferTimestamp = iVideoTimestamp;
+ iVideoBufferRandomAccessPoint = iVideoRandomAccessPoint;
+
+ break;
+ }
+ case CCMRMediaBuffer::EVideoMPEG4DecSpecInfo:
+ {
+ iVideoDecSpecInfoSize = iBufferSize;
+
+ error = MP4ComposeWriteVideoDecoderSpecificInfo(iMP4Handle,
+ (mp4_u8 *)aBuffer->Data().Ptr(),
+ (mp4_u32)iBufferSize);
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteVideoDecoderSpecificInfo, error=%d"), error));
+ User::Leave(KErrGeneral);
+ }
+
+ break;
+ }
+ case CCMRMediaBuffer::EVideoH264NAL:
+ {
+ iVideoTimestamp = aBuffer->TimeStamp();
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL EVideoH264NAL Timestamp of the buffer is %d"), I64INT(iVideoTimestamp.Int64())));
+ iVideoRandomAccessPoint = aBuffer->RandomAccessPoint();
+ iVideoFrameNumber++;
+ if (iVideoRandomAccessPoint)
+ {
+ iVideoIntraFrameNumber++;
+ }
+
+ if (iVideoBufferTimestamp < TTimeIntervalMicroSeconds(0)) // First frame
+ {
+ iFirstVideoFrameTimestamp = iVideoTimestamp;
+ iVideoBufferTimestamp = iVideoTimestamp;
+ iVideoBufferRandomAccessPoint = iVideoRandomAccessPoint;
+
+ if ((TUint)(iBufferSize) > iVideoBufferSize)
+ {
+ delete [] iVideoBuffer;
+ iVideoBuffer = new (ELeave) TUint8[(TUint)iBufferSize];
+ iVideoBufferSize = (TUint)iBufferSize;
+ }
+
+ ConvertNALEncapsulationToNALSizes( aBuffer );
+ break;
+ }
+
+ videoduration = iVideoTimestamp.Int64() - iVideoBufferTimestamp.Int64(); // Duration in microseconds
+ videoduration = TInt64((videoduration * KVideoTimeScale) / 1E6 + 0.5); // Duration scaled to KVideoTimeScale
+ iVideoFrameDuration = (TUint)I64INT(videoduration);
+
+ error = MP4ComposeWriteVideoFrame(iMP4Handle,
+ (mp4_u8 *)iVideoBuffer,
+ (mp4_u32)iVideoBufferFrameSize,
+ (mp4_u32)iVideoFrameDuration,
+ (mp4_bool)iVideoBufferRandomAccessPoint);
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteVideoFrame, error=%d"), error));
+ User::Leave(KErrGeneral);
+ }
+
+ if ((TUint)(iBufferSize) > iVideoBufferSize)
+ {
+ delete [] iVideoBuffer;
+ iVideoBuffer = new (ELeave) TUint8[(TUint)iBufferSize];
+ iVideoBufferSize = (TUint)iBufferSize;
+ }
+
+ ConvertNALEncapsulationToNALSizes( aBuffer );
+
+ iVideoBufferTimestamp = iVideoTimestamp;
+ iVideoBufferRandomAccessPoint = iVideoRandomAccessPoint;
+ break;
+ }
+ case CCMRMediaBuffer::EVideoH264Bytestream:
+ {
+ // need to add NAL header to end on bytestream buffer
+ iVideoTimestamp = aBuffer->TimeStamp();
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL EVideoH264Bytestream Timestamp of the buffer is %d"), I64INT(iVideoTimestamp.Int64())));
+ iVideoRandomAccessPoint = aBuffer->RandomAccessPoint();
+ iVideoFrameNumber++;
+ if (iVideoRandomAccessPoint)
+ {
+ iVideoIntraFrameNumber++;
+ }
+
+ if (iVideoBufferTimestamp < TTimeIntervalMicroSeconds(0)) // First frame
+ {
+ iFirstVideoFrameTimestamp = iVideoTimestamp;
+ iVideoBufferTimestamp = iVideoTimestamp;
+ iVideoBufferRandomAccessPoint = iVideoRandomAccessPoint;
+
+ if ((TUint)(iBufferSize) > iVideoBufferSize)
+ {
+ delete [] iVideoBuffer;
+ iVideoBuffer = new (ELeave) TUint8[(TUint)iBufferSize];
+ iVideoBufferSize = (TUint)iBufferSize;
+ }
+
+ ConvertBytestreamHeadersToNALSizes(aBuffer);
+ Mem::Copy(iVideoBuffer, aBuffer->Data().Ptr(), iBufferSize);
+ iVideoBufferFrameSize = iBufferSize;
+ break;
+ }
+
+ videoduration = iVideoTimestamp.Int64() - iVideoBufferTimestamp.Int64(); // Duration in microseconds
+ videoduration = TInt64((videoduration * KVideoTimeScale) / 1E6 + 0.5); // Duration scaled to KVideoTimeScale
+ iVideoFrameDuration = (TUint)I64INT(videoduration);
+
+ error = MP4ComposeWriteVideoFrame(iMP4Handle,
+ (mp4_u8 *)iVideoBuffer,
+ (mp4_u32)iVideoBufferFrameSize,
+ (mp4_u32)iVideoFrameDuration,
+ (mp4_bool)iVideoBufferRandomAccessPoint);
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteVideoFrame, error=%d"), error));
+ User::Leave(KErrGeneral);
+ }
+
+ if ((TUint)(iBufferSize) > iVideoBufferSize)
+ {
+ delete [] iVideoBuffer;
+ iVideoBuffer = new (ELeave) TUint8[(TUint)iBufferSize];
+ iVideoBufferSize = (TUint)iBufferSize;
+ }
+
+ ConvertBytestreamHeadersToNALSizes(aBuffer);
+ Mem::Copy(iVideoBuffer, aBuffer->Data().Ptr(), iBufferSize);
+
+ iVideoBufferFrameSize = (TUint)iBufferSize;
+ iVideoBufferTimestamp = iVideoTimestamp;
+ iVideoBufferRandomAccessPoint = iVideoRandomAccessPoint;
+ break;
+ }
+ case CCMRMediaBuffer::EVideoH264NALDecSpecInfo:
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL EVideoH264NALDecSpecInfo")));
+ HBufC8* outputAVCHeader = 0;
+ outputAVCHeader = (HBufC8*) HBufC8::NewLC(16384);
+ TPtr8 destptr = outputAVCHeader->Des();
+
+ // parse header & convert it to AVCDecoderConfigurationRecord -format
+ ConvertAVCHeaderNALL(aBuffer, destptr);
+ iVideoDecSpecInfoSize = destptr.Length();
+
+ error = MP4ComposeWriteVideoDecoderSpecificInfo(iMP4Handle,
+ (mp4_u8 *)destptr.Ptr(),
+ (mp4_u32)iVideoDecSpecInfoSize);
+
+ CleanupStack::PopAndDestroy( outputAVCHeader );
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteVideoDecoderSpecificInfo, error=%d"), error));
+ User::Leave(KErrGeneral);
+ }
+ break;
+ }
+ case CCMRMediaBuffer::EVideoH264BytestreamDecSpecInfo:
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL EVideoH264BytestreamDecSpecInfo")));
+ HBufC8* outputAVCHeader = 0;
+ outputAVCHeader = (HBufC8*) HBufC8::NewLC(16384);
+ TPtr8 destptr = outputAVCHeader->Des();
+
+ // parse header & convert it to AVCDecoderConfigurationRecord -format
+ ConvertAVCHeaderByteStreamL(aBuffer, destptr);
+ iVideoDecSpecInfoSize = destptr.Length();
+
+ error = MP4ComposeWriteVideoDecoderSpecificInfo(iMP4Handle,
+ (mp4_u8 *)destptr.Ptr(),
+ (mp4_u32)iVideoDecSpecInfoSize);
+
+ CleanupStack::PopAndDestroy( outputAVCHeader );
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteVideoDecoderSpecificInfo, error=%d"), error));
+ User::Leave(KErrGeneral);
+ }
+ break;
+ }
+ case CCMRMediaBuffer::EAudioDecSpecInfo:
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteAudioDecoderSpecificInfo in"), error));
+ iAudioDecSpecInfoSize = iBufferSize;
+
+ error = MP4ComposeWriteAudioDecoderSpecificInfo(iMP4Handle,
+ (mp4_u8 *)aBuffer->Data().Ptr(),
+ (mp4_u32)iBufferSize);
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteAudioDecoderSpecificInfo, error=%d"), error));
+ if (error != MP4_OK)
+ {
+ User::Leave(KErrGeneral);
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteAudioDecoderSpecificInfo - flags: iFileCodecType=%d Frameduration=%d"), iFileCodecType, iAudioAACFrameDuration));
+ if ( (iFileCodecType & MP4_TYPE_MPEG4_AUDIO) && !iAudioAACFrameDuration )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteAudioDecoderSpecificInfo determining new AAC samplerate."), error));
+ DetermineAACFrameDurationL( aBuffer );
+ // done here because timescale is dependent of samplerate.
+ error = MP4ComposeAddAudioDescription(iMP4Handle,
+ (mp4_u32)iAudioAACSamplerate,
+ (mp4_u8)KAACAudioFramesPerSample,
+ (mp4_u16)KAudioModeSet);
+ if (error != MP4_OK)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeAddAudioDescription, error=%d"), error));
+ User::Leave(KErrGeneral);
+ }
+ }
+ PRINT((_L("CCamC3GPDataSinkImp::WriteBufferL MP4ComposeWriteAudioDecoderSpecificInfo out"), error));
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::SetVideoFrameSize
+//
+// Give video frame size to sink.
+// -----------------------------------------------------------------------------
+//
+TInt CCamC3GPDataSinkImp::SetVideoFrameSize(TSize aSize)
+ {
+ if (aSize.iWidth < 0)
+ {
+ return KErrArgument;
+ }
+ if (aSize.iHeight < 0)
+ {
+ return KErrArgument;
+ }
+
+ if (iBytesReceived)
+ {
+ return KErrNotReady;
+ }
+
+ iVideoXResolution = aSize.iWidth;
+ iVideoYResolution = aSize.iHeight;
+ return KErrNone;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::SetAverageVideoBitRate
+//
+// Give average video bitrate to sink.
+// -----------------------------------------------------------------------------
+//
+TInt CCamC3GPDataSinkImp::SetAverageVideoBitRate(TInt aBitRate)
+ {
+ if (aBitRate < 0)
+ {
+ return KErrArgument;
+ }
+
+ iVideoAverageBitRate = aBitRate;
+ return KErrNone;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::SetMaxVideoBitRate
+//
+// Give maximum video bitrate to sink.
+// -----------------------------------------------------------------------------
+//
+TInt CCamC3GPDataSinkImp::SetMaxVideoBitRate(TInt aBitRate)
+ {
+ if (aBitRate < 0)
+ {
+ return KErrArgument;
+ }
+
+ iVideoMaxBitRate = aBitRate;
+ return KErrNone;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::SetAverageAudioBitRate
+//
+// Give average audio bitrate to sink.
+// -----------------------------------------------------------------------------
+//
+TInt CCamC3GPDataSinkImp::SetAverageAudioBitRate(TInt aBitRate)
+ {
+ if (aBitRate < 0)
+ {
+ return KErrArgument;
+ }
+
+ iAudioAverageBitRate = aBitRate;
+ return KErrNone;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::CurrentFileSize
+//
+// Estimate current output file size.
+// Total file size = metadata size + media data size
+// -----------------------------------------------------------------------------
+//
+TUint CCamC3GPDataSinkImp::CurrentFileSize() const
+ {
+ TUint filesize = 0;
+
+ // Media data
+ filesize += 8; // mdat box type and size
+ filesize += iBytesReceived; // Data received from media recorder
+
+ return filesize;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::CurrentMetadataSize
+//
+// Calculates current metadata size.
+// -----------------------------------------------------------------------------
+//
+TUint CCamC3GPDataSinkImp::CurrentMetadataSize() const
+ {
+ TBool haveAudio;
+ TBool haveVideo;
+ TUint metadatasize = 0;
+
+ haveAudio = EFalse;
+ haveVideo = EFalse;
+
+
+ // Metadata
+
+ metadatasize += KFTYPSize; // FTYP
+
+ if ((iFileCodecType & MP4_TYPE_H263_PROFILE_0) ||
+ (iFileCodecType & MP4_TYPE_H263_PROFILE_3)) // H.263
+ {
+ haveVideo = ETrue;
+ metadatasize += 574; // Constant size H.263 metadata
+ metadatasize += (iVideoFrameNumber * 16 + iVideoIntraFrameNumber * 4); // Content dependent H.263 metadata
+ }
+
+ if (iFileCodecType & MP4_TYPE_MPEG4_VIDEO) // MPEG-4 video
+ {
+ haveVideo = ETrue;
+ metadatasize += 596; // Constant size MPEG-4 video metadata
+ metadatasize += (iVideoFrameNumber * 16 + iVideoIntraFrameNumber * 4 + (TUint)iVideoDecSpecInfoSize); // Content dependent MPEG-4 video metadata
+ }
+
+ if ( iFileCodecType & MP4_TYPE_AMR_NB ) // AMR-NB
+ {
+ haveAudio = ETrue;
+ metadatasize += 514; // Constant size AMR metadata
+ metadatasize += ((iAudioFrameNumber + KAMRAudioFramesPerSample - 1) / KAMRAudioFramesPerSample) * 16;
+ }
+
+ if ( iFileCodecType & MP4_TYPE_MPEG4_AUDIO ) // MPEG-4 AAC-LC
+ {
+ haveAudio = ETrue;
+ metadatasize += 514; // Constant size metadata
+ metadatasize += (iAudioFrameNumber * 16) + (TUint)iAudioDecSpecInfoSize;
+ }
+
+ if (haveAudio && haveVideo)
+ metadatasize -= 116; // There is only one moov and mvhd in a file
+
+ return metadatasize;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::DriveFreeSpaceL
+//
+// Calculate free space on a drive in bytes.
+// -----------------------------------------------------------------------------
+//
+TInt64 CCamC3GPDataSinkImp::DriveFreeSpaceL()
+ {
+ TVolumeInfo volumeinfo;
+
+ if (iFreeDiskSpaceCounter % KFreeDiskSpaceCounter == 0)
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::DriveFreeSpaceL Asking Disk Free space")));
+ User::LeaveIfError(iFS->Volume(volumeinfo, iDriveNumber));
+ iFreeDiskSpace = volumeinfo.iFree;
+ PRINT((_L("CCamC3GPDataSinkImp::DriveFreeSpaceL Received Disk Free space info")));
+ }
+
+ iFreeDiskSpaceCounter++;
+ return iFreeDiskSpace;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::DetermineAACFrameDuration
+//
+// Determines AAC audio frame duration.
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::DetermineAACFrameDurationL( CCMRMediaBuffer* aBuffer )
+ {
+ TInt sampleRate = 0;
+ TUint8 sampleRateIndex = *aBuffer->Data().Mid(0).Ptr();
+ sampleRateIndex <<= 5;
+ sampleRateIndex >>= 4;
+ sampleRateIndex |= ((TUint8)*aBuffer->Data().Mid(1).Ptr())>>7;
+
+ switch ( sampleRateIndex )
+ {
+ case 0x3:
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::DetermineAACFrameDurationL AAC Samplerate reset to 48000")));
+ sampleRate = 48000;
+ break;
+ }
+ case 0x5:
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::DetermineAACFrameDurationL AAC Samplerate reset to 32000")));
+ sampleRate = 32000;
+ break;
+ }
+ case 0x6:
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::DetermineAACFrameDurationL AAC Samplerate reset to 24000")));
+ sampleRate = 24000;
+ break;
+ }
+ case 0x8:
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::DetermineAACFrameDurationL AAC Samplerate reset to 16000")));
+ sampleRate = 16000;
+ break;
+ }
+ case 0xb:
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::DetermineAACFrameDurationL AAC Samplerate reset to 8000")));
+ sampleRate = 8000;
+ break;
+ }
+ default:
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::DetermineAACFrameDurationL unsupported AAC Samplerate - leaving -6")));
+ User::Leave( KErrArgument );
+ break;
+ }
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::DetermineAACFrameDurationL AAC Samplerate reset from %d to %d"), iAudioAACSamplerate, sampleRate));
+ iAudioAACSamplerate = sampleRate;
+ // formula to calculate frameduration from samplerate is: frameduration = (1024/samplerate)*1000
+ // and in movie timescale: frameDurationInMovieTimescale = frameduration*samplerate/1000
+ // thus with equal samplerate = timescale frameDurationInMovieTimescale for AAC is always 1024
+ iAudioAACFrameDuration = 1024;
+}
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::ConvertAVCHeaderL
+//
+// Convert AVC specific decoder config info to
+// AVC Decoder Configuration Record -format
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::ConvertAVCHeaderNALL( CCMRMediaBuffer* aBuffer, TDes8& aDstBuf )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderNALL in Buffer length: %d"), aBuffer->Data().Length() ));
+ TUint8* inputPtr = (TUint8*)(aBuffer->Data().Ptr());
+ TUint8* outputPtr = (TUint8*)(aDstBuf.Ptr());
+ TUint8* spsPtr;
+ TUint8* ppsPtr;
+
+ TUint numSPS = 0;
+ TUint numPPS = 0;
+
+ TUint totalSPSLength = 0;
+ TUint totalPPSLength = 0;
+
+ TUint headerLength = aBuffer->Data().Length();
+ TUint endIndex = headerLength;
+
+ TInt nalType = 0;
+ TUint nalLength;
+ TUint nalIndex;
+ TUint nalOffset;
+
+ // Allocate memory for the temporary buffers
+ HBufC8* temp1 = (HBufC8*) HBufC8::NewLC(1000);
+ HBufC8* temp2 = (HBufC8*) HBufC8::NewLC(5000);
+
+ spsPtr = const_cast<TUint8*>( temp1->Des().Ptr() );
+ ppsPtr = const_cast<TUint8*>( temp2->Des().Ptr() );
+
+ TUint numNalUnits = inputPtr[endIndex-4] + (inputPtr[endIndex-3]<<8) + (inputPtr[endIndex-2]<<16) + (inputPtr[endIndex-1]<<24);
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderNALL Nal Unit count: %d"), numNalUnits));
+
+ // Move endIndex to point to the first NAL unit's offset information
+ endIndex = headerLength - numNalUnits*8 - 4;
+ nalIndex = 0;
+
+ while (nalIndex < numNalUnits)
+ {
+ nalIndex++;
+
+ TInt tmp1 = inputPtr[endIndex++];
+ TInt tmp2 = inputPtr[endIndex++]<<8;
+ TInt tmp3 = inputPtr[endIndex++]<<16;
+ TInt tmp4 = inputPtr[endIndex++]<<24;
+
+ nalOffset = tmp1 + tmp2 + tmp3 + tmp4;
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderNALL Nal Unit start offset: %d"), nalOffset));
+
+ tmp1 = inputPtr[endIndex++];
+ tmp2 = inputPtr[endIndex++]<<8;
+ tmp3 = inputPtr[endIndex++]<<16;
+ tmp4 = inputPtr[endIndex++]<<24;
+
+ nalLength = tmp1 + tmp2 + tmp3 + tmp4;
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderNALL Nal Unit length: %d"), nalLength));
+
+ nalType = inputPtr[nalOffset] & 0x1F;
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderNALL Nal Unit type: %d"), nalType));
+
+ if(nalType == 7)
+ {
+ numSPS++;
+
+ // First store the SPS unit length with two bytes
+ spsPtr[totalSPSLength] = (nalLength >> 8) & 0xFF;
+ spsPtr[totalSPSLength+1] = nalLength & 0xFF;
+
+ // Copy the SPS unit to the buffer
+ Mem::Copy(&spsPtr[totalSPSLength+2], inputPtr+nalOffset , nalLength);
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderNALL stored SPS from offset: %d size: %d"), inputPtr+nalOffset, nalLength));
+ totalSPSLength += nalLength + 2; // Two more for the size
+ }
+ else if(nalType == 8)
+ {
+ numPPS++;
+
+ // First store the SPS unit length with two bytes
+ ppsPtr[totalPPSLength] = (nalLength >> 8) & 0xFF;
+ ppsPtr[totalPPSLength+1] = nalLength & 0xFF;
+
+ // Copy the SPS unit to the buffer
+ Mem::Copy(&ppsPtr[totalPPSLength+2], inputPtr+nalOffset , nalLength);
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderNALL stored PPS from offset: %d size: %d"), inputPtr+nalOffset, nalLength));
+ totalPPSLength += nalLength + 2; // Two more for the size
+ }
+ else
+ {
+ }
+ }
+
+ // When the header has been parsed, form the AVC Decoder Configuration Record
+ outputPtr[0] = 0x01; // configurationVersion
+ // AVCProfileIndication contains the profile code as defined in the AVC specification
+ if (iFileCodecType & MP4_TYPE_AVC_PROFILE_BASELINE)
+ {
+ outputPtr[1] = 0x42;
+ outputPtr[2] = 0x80; // Bitstream obeys all Baseline profile constraints.
+ // Profile compatibility, i.e. all 4 constrain set flags + reserved 4 zero bits
+ if ( iAVCOutputLevel == 101 )
+ {
+ outputPtr[2] |= 0x10; // For level 1b, the 4th bit shall be == 1, otherwise it must be zero
+ }
+ // AVCLevelIndication contains the level code as defined in the AVC specification
+ outputPtr[3] = (iAVCOutputLevel == 101) ? 0x0B : iAVCOutputLevel;
+ }
+ else if (iFileCodecType & MP4_TYPE_AVC_PROFILE_MAIN)
+ {
+ outputPtr[1] = 0x4D;
+ outputPtr[2] = 0x40; // Bitstream obeys all main profile constraints.
+ if ( iAVCOutputLevel == 101 )
+ {
+ outputPtr[2] |= 0x10; // For level 1b, the 4th bit shall be == 1, otherwise it must be zero
+ }
+ // AVCLevelIndication contains the level code as defined in the AVC specification
+ outputPtr[3] = (iAVCOutputLevel == 101) ? 0x0B : iAVCOutputLevel;
+ }
+ else if (iFileCodecType & MP4_TYPE_AVC_PROFILE_HIGH)
+ {
+ outputPtr[1] = 0x64;
+ outputPtr[2] = 0x40; // Bitstream obeys all Baseline profile constraints.
+ outputPtr[3] = (iAVCOutputLevel == 101) ? 0x09 : iAVCOutputLevel;
+ }
+ else
+ {
+ User::Leave(KErrNotSupported);
+ }
+
+ // lengthSizeMinusOne indicates the length in bytes of the NALUnitLength field minus one.
+ outputPtr[4] = 0x03; // 4 bytes
+ outputPtr[4] |= 0x0FC; // 6 reserved bits (all 1)
+ // numOfSequenceParameterSets indicates the number of sequence parameter sets
+ outputPtr[5] = numSPS;
+ outputPtr[5] |= 0xE0; // 3 reserved bits (all 1)
+
+ TInt len = 6;
+ // Copy the SPS unit(s) to the buffer
+ Mem::Copy(&outputPtr[6], spsPtr , totalSPSLength);
+ len += totalSPSLength;
+ outputPtr[6+totalSPSLength] = numPPS;
+ len += 1;
+
+ // Copy the PPS unit(s) to the buffer
+ Mem::Copy(&outputPtr[6+totalSPSLength+1], ppsPtr , totalPPSLength);
+ len += totalPPSLength;
+ aDstBuf.SetLength(len);
+
+ CleanupStack::PopAndDestroy(temp2);
+ CleanupStack::PopAndDestroy(temp1);
+}
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL
+//
+// Convert AVC specific decoder config info from Bytestream (ElementaryStream) encapsulation to
+// AVC Decoder Configuration Record -format
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL( CCMRMediaBuffer* aBuffer, TDes8& aDstBuf )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL in")));
+ TUint8* inputPtr = (TUint8*)(aBuffer->Data().Ptr());
+ TUint8* outputPtr = (TUint8*)(aDstBuf.Ptr());
+ TUint8* spsPtr;
+ TUint8* ppsPtr;
+
+ TUint numSPS = 0;
+ TUint numPPS = 0;
+
+ TUint totalSPSLength = 0;
+ TUint totalPPSLength = 0;
+
+ TUint headerLength = aBuffer->Data().Length();
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL buffer length: %d"), headerLength));
+
+ TInt nalType = 0;
+
+ // Allocate memory for the temporary buffers
+ HBufC8* temp1 = (HBufC8*) HBufC8::NewLC(1000);
+ HBufC8* temp2 = (HBufC8*) HBufC8::NewLC(5000);
+
+ spsPtr = const_cast<TUint8*>( temp1->Des().Ptr() );
+ ppsPtr = const_cast<TUint8*>( temp2->Des().Ptr() );
+
+ // scan from beginning of buffer to end for SPS and PSP
+ TInt i = 0;
+ TInt j = 0;
+ for (i=0; i<headerLength; i++)
+ {
+ if ( inputPtr[i] == 0 &&
+ inputPtr[i+1] == 0 &&
+ inputPtr[i+2] == 0 &&
+ inputPtr[i+3] == 1 )
+ { // found bytestream header [00 00 00 01]
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL found header at: %d"), i));
+ nalType = inputPtr[i+4] & 0x1F;
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL NAL type: %d"), nalType));
+ if(nalType == 7) // SPS
+ {
+ numSPS++;
+ // find length of SPS
+ TInt j;
+ for (j=4; i+j+3<headerLength; j++)
+ {
+ if ( inputPtr[i+j] == 0 &&
+ inputPtr[i+j+1] == 0 &&
+ inputPtr[i+j+2] == 0 &&
+ inputPtr[i+j+3] == 1 )
+ {
+ totalSPSLength = j-i-4;
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL SPS length: %d, count: %d"), totalSPSLength, numSPS));
+ break;
+ }
+ }
+ // if we didn't find next bytestream header then this is last buffer
+ if ( totalSPSLength == 0 )
+ {
+ totalSPSLength = headerLength - i - 4;
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL SPS length: %d (last), count: %d"), totalSPSLength, numSPS));
+ }
+
+ // First store the SPS unit length with two bytes
+ spsPtr[0] = (totalSPSLength >> 8) & 0xFF;
+ spsPtr[1] = totalSPSLength & 0xFF;
+
+ // Copy the SPS unit to the buffer
+ Mem::Copy(&spsPtr[2], &inputPtr[i+4] , totalSPSLength);
+ totalSPSLength +=2;
+ }
+ else if ( nalType == 8 ) // PPS)
+ {
+ numPPS++;
+ // find length of PPS
+ for (j=4; i+j+3<headerLength; j++)
+ {
+ if ( inputPtr[i+j] == 0 &&
+ inputPtr[i+j+1] == 0 &&
+ inputPtr[i+j+2] == 0 &&
+ inputPtr[i+j+3] == 1 )
+ {
+ totalPPSLength = j-i-4;
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL PPS length: %d, count: %d"), totalPPSLength, numPPS));
+ break;
+ }
+ }
+ // if we didn't find next bytestream header then this is last buffer
+ if ( totalPPSLength == 0 )
+ {
+ totalPPSLength = headerLength - i - 4;
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL PPS length: %d (last), count: %d"), totalPPSLength, numPPS));
+ }
+
+ // First store the SPS unit length with two bytes
+ ppsPtr[0] = (totalPPSLength >> 8) & 0xFF;
+ ppsPtr[1] = totalPPSLength & 0xFF;
+
+ // Copy the SPS unit to the buffer
+ Mem::Copy(&ppsPtr[2], &inputPtr[i+4], totalPPSLength);
+ totalPPSLength +=2;
+ }
+ }
+ }
+
+ // When the header has been parsed, form the AVC Decoder Configuration Record
+ outputPtr[0] = 0x01; // configurationVersion
+ // AVCProfileIndication contains the profile code as defined in the AVC specification
+ if (iFileCodecType & MP4_TYPE_AVC_PROFILE_BASELINE)
+ {
+ outputPtr[1] = 0x42;
+ outputPtr[2] = 0x80; // Bitstream obeys all Baseline profile constraints.
+ // Profile compatibility, i.e. all 4 constrain set flags + reserved 4 zero bits
+ if ( iAVCOutputLevel == 101 )
+ {
+ outputPtr[2] |= 0x10; // For level 1b, the 4th bit shall be == 1, otherwise it must be zero
+ }
+ // AVCLevelIndication contains the level code as defined in the AVC specification
+ outputPtr[3] = (iAVCOutputLevel == 101) ? 0x0B : iAVCOutputLevel;
+ }
+ else if (iFileCodecType & MP4_TYPE_AVC_PROFILE_MAIN)
+ {
+ outputPtr[1] = 0x4D;
+ outputPtr[2] = 0x40; // Bitstream obeys all main profile constraints.
+ if ( iAVCOutputLevel == 101 )
+ {
+ outputPtr[2] |= 0x10; // For level 1b, the 4th bit shall be == 1, otherwise it must be zero
+ }
+ // AVCLevelIndication contains the level code as defined in the AVC specification
+ outputPtr[3] = (iAVCOutputLevel == 101) ? 0x0B : iAVCOutputLevel;
+ }
+ else if (iFileCodecType & MP4_TYPE_AVC_PROFILE_HIGH)
+ {
+ outputPtr[1] = 0x64;
+ outputPtr[2] = 0x40; // Bitstream obeys all Baseline profile constraints.
+ outputPtr[3] = (iAVCOutputLevel == 101) ? 0x09 : iAVCOutputLevel;
+ }
+ else
+ {
+ User::Leave(KErrNotSupported);
+ }
+
+ // lengthSizeMinusOne indicates the length in bytes of the NALUnitLength field minus one.
+ outputPtr[4] = 0x03; // 4 bytes
+ outputPtr[4] |= 0x0FC; // 6 reserved bits (all 1)
+ // numOfSequenceParameterSets indicates the number of sequence parameter sets
+ outputPtr[5] = numSPS;
+ outputPtr[5] |= 0xE0; // 3 reserved bits (all 1)
+
+ TInt len = 6;
+
+ // Copy the SPS unit(s) to the buffer
+ Mem::Copy(&outputPtr[6], spsPtr , totalSPSLength);
+ len += totalSPSLength;
+ outputPtr[6+totalSPSLength] = numPPS;
+ len += 1;
+
+ // Copy the PPS unit(s) to the buffer
+ Mem::Copy(&outputPtr[6+totalSPSLength+1], ppsPtr , totalPPSLength);
+ len += totalPPSLength;
+ aDstBuf.SetLength(len);
+
+ CleanupStack::PopAndDestroy(temp2);
+ CleanupStack::PopAndDestroy(temp1);
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertAVCHeaderByteStreamL out")));
+}
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::ConvertBytestreamHeadersToNALSizesL
+//
+// Converts AVC frame from Bytestream (ElementaryStream) encapsulation to
+// file format AVC sample structure by replacing bytestream headers with NAL unit sizes.
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::ConvertBytestreamHeadersToNALSizes( CCMRMediaBuffer* aBuffer )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertBytestreamHeadersToNALSizesL in")));
+ TUint8* inputPtr = (TUint8*)(aBuffer->Data().Ptr());
+ TUint headerLength = aBuffer->Data().Length();
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertBytestreamHeadersToNALSizesL buffer length: %d"), headerLength));
+
+ TInt nalLength = 0;
+ TBool moreThanOneNAL = EFalse;
+ TInt i = 0;
+ TInt j = 0;
+ for (i=0; i<headerLength; i++)
+ {
+ if ( inputPtr[i] == 0 &&
+ inputPtr[i+1] == 0 &&
+ inputPtr[i+2] == 0 &&
+ inputPtr[i+3] == 1 )
+ { // found bytestream header [00 00 00 01]
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertBytestreamHeadersToNALSizesL found header at: %d"), i));
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertBytestreamHeadersToNALSizesL NAL type: %d"), TInt(inputPtr[i+4] & 0x1F) ));
+ if (moreThanOneNAL)
+ {// we found start of next NAL unit in memory buffer so update previous size
+ nalLength = i-j-4; // 4 is the bytestream header
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertBytestreamHeadersToNALSizesL NAL length: %d"), nalLength));
+ inputPtr[j] = TUint8((nalLength >> 24) & 0xff);
+ inputPtr[j+1] = TUint8((nalLength >> 16) & 0xff);
+ inputPtr[j+2] = TUint8((nalLength >> 8) & 0xff);
+ inputPtr[j+3] = TUint8(nalLength & 0xff);
+ }
+ moreThanOneNAL = ETrue;
+ j=i;
+ }
+ }
+ // and update last (or if only 1 NAL size:
+ nalLength = headerLength-j-4; // 4 is the bytestream header
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertBytestreamHeadersToNALSizesL last NAL length: %d"), nalLength));
+ inputPtr[j] = TUint8((nalLength >> 24) & 0xff);
+ inputPtr[j+1] = TUint8((nalLength >> 16) & 0xff);
+ inputPtr[j+2] = TUint8((nalLength >> 8) & 0xff);
+ inputPtr[j+3] = TUint8(nalLength & 0xff);
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertBytestreamHeadersToNALSizesL out")));
+}
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::ConvertNALEncapsulationToNALSizes
+//
+// Converts AVC frame from NAL (EGenericPayload) encapsulation to
+// file format AVC sample structure by replacing NAL encapsulation with NAL unit sizes.
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::ConvertNALEncapsulationToNALSizes( CCMRMediaBuffer* aBuffer )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertNALEncapsulationToNALSizes in")));
+ TUint8* inputPtr = (TUint8*)(aBuffer->Data().Ptr());
+ TUint bufferLength = aBuffer->Data().Length();
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertNALEncapsulationToNALSizes buffer length: %d"), bufferLength));
+
+ // Offset to the end and get NAL unit count
+ TInt offset = bufferLength-4; //last 4 bytes are the NAL unit count
+ TInt nalCount = TInt(inputPtr[offset]) +
+ (TInt(inputPtr[offset + 1]) << 8) +
+ (TInt(inputPtr[offset + 2]) << 16) +
+ (TInt(inputPtr[offset + 3]) << 24);
+
+ TInt frameStart = 0;
+ TInt frameSize = 0;
+ TInt outputOffset = 0;
+ for(TInt i=0; i<nalCount; i++)
+ {//go through all NAL units in buffer
+ // Offset to the start of NAL Unit infos
+ offset = bufferLength-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(inputPtr[offset]) +
+ (TInt(inputPtr[offset + 1]) << 8) +
+ (TInt(inputPtr[offset + 2]) << 16) +
+ (TInt(inputPtr[offset + 3]) << 24);
+
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertNALEncapsulationToNALSizes() NAL unit %d frame start: %d "), i, frameStart ));
+
+ // Get frame size
+ offset += 4;
+ frameSize = TInt(inputPtr[offset]) +
+ (TInt(inputPtr[offset + 1]) << 8) +
+ (TInt(inputPtr[offset + 2]) << 16) +
+ (TInt(inputPtr[offset + 3]) << 24);
+
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertNALEncapsulationToNALSizes() NAL unit %d frame size: %d "), i, frameSize ));
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertNALEncapsulationToNALSizes() NAL unit %d type: %d "), i, TInt(inputPtr[frameStart] & 0x1F) ));
+
+ iVideoBuffer[outputOffset] = TUint8((frameSize >> 24) & 0xff);
+ iVideoBuffer[outputOffset+1] = TUint8((frameSize >> 16) & 0xff);
+ iVideoBuffer[outputOffset+2] = TUint8((frameSize >> 8) & 0xff);
+ iVideoBuffer[outputOffset+3] = TUint8(frameSize & 0xff);
+
+ Mem::Copy(iVideoBuffer+outputOffset+4, inputPtr+frameStart, frameSize);
+ outputOffset += 4 + frameSize; // 4 bytes for length information.
+ }
+ iVideoBufferFrameSize = outputOffset;
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertNALEncapsulationToNALSizes() new video buffer size: %d "), iVideoBufferFrameSize ));
+ PRINT((_L("CCamC3GPDataSinkImp::ConvertNALEncapsulationToNALSizes out")));
+ }
+
+// -----------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::M3GPMP4LibDeleteTempFileName
+// -----------------------------------------------------------------------------
+//
+void CCamC3GPDataSinkImp::M3GPMP4LibDeleteTempFileName( MP4FileName tempFileName )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::M3GPMP4LibDeleteTempFileName entering, tempFileName=%x, file count=%d"),tempFileName, iDeleteFileQueue->Count()));
+ MP4FileName* tempFileNamePtr = NULL;
+ TInt result = KErrNoMemory;
+
+ // Add image to the queue.
+ tempFileNamePtr = new MP4FileName;
+ PRINT((_L("CCamC3GPDataSinkImp::M3GPMP4LibDeleteTempFileName tempFileName=%x, tempFileNamePtr=%x"), tempFileName, tempFileNamePtr));
+ if ( tempFileNamePtr && iDeleteFileQueue )
+ {
+ *tempFileNamePtr = tempFileName;
+ result = iDeleteFileQueue->Append( tempFileNamePtr );
+ }
+ if ( result != KErrNone ) // Append failed -> do sync remove
+ {
+ TInt err = wremove( tempFileName );
+ PRINT((_L("CCamC3GPDataSinkImp::M3GPMP4LibDeleteTempFileName wremove sync err=%d, tempFileName=%x"), err, tempFileName));
+ free(tempFileName);
+ tempFileName = 0;
+ if ( tempFileNamePtr )
+ {
+ delete tempFileNamePtr;
+ tempFileNamePtr = 0;
+ }
+ }
+ else // Append OK, start async delete if not running already
+ {
+ if (iDeleteFileQueue->Count())
+ {
+
+ if ( !iIdleDelete->IsActive() )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::M3GPMP4LibDeleteTempFileName() Start IdleDelete, file count=%d"), iDeleteFileQueue->Count()));
+ iIdleDelete->Start( TCallBack( IdleDelete, this ) );
+ }
+ }
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::M3GPMP4LibDeleteTempFileName exiting")));
+ }
+
+
+// ---------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::IdleDelete
+// ---------------------------------------------------------------------------
+//
+TInt CCamC3GPDataSinkImp::IdleDelete( TAny* aCont )
+ {
+ CCamC3GPDataSinkImp* appCont = static_cast<CCamC3GPDataSinkImp*>( aCont );
+ return ( appCont->DoIdleDelete() );
+ }
+
+// ---------------------------------------------------------------------------
+// CCamC3GPDataSinkImp::DoIdleDelete
+// ---------------------------------------------------------------------------
+//
+TInt CCamC3GPDataSinkImp::DoIdleDelete()
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::DoIdleDelete() in, file count=%d"), iDeleteFileQueue->Count()));
+ TInt err = KErrNone;
+ MP4FileName tempFileName;
+ TInt filesLeft = EFalse;
+
+ // Delete one file from queue
+ if ( iDeleteFileQueue )
+ {
+ if ( iDeleteFileQueue->Count() )
+ {
+ tempFileName = *(*iDeleteFileQueue)[0];
+ PRINT((_L("CCamC3GPDataSinkImp::DoIdleDelete index 0:tempFileName=%x %s"), tempFileName, tempFileName));
+ delete (*iDeleteFileQueue)[0];
+ iDeleteFileQueue->Remove( 0 );
+ err = wremove( tempFileName );
+ PRINT((_L("CCamC3GPDataSinkImp::DoIdleDelete wremove async err=%d, tempFileName=%x %s"), err, tempFileName, tempFileName));
+ err++; // remove compiler warning
+ free(tempFileName);
+ tempFileName = 0;
+ }
+
+ // Start next deletion if queue is not empty
+ if ( iDeleteFileQueue->Count() )
+ {
+ PRINT((_L("CCamC3GPDataSinkImp::DoIdleDelete() continue, file count=%d"), iDeleteFileQueue->Count()));
+ filesLeft = ETrue;
+ }
+ }
+
+ PRINT((_L("CCamC3GPDataSinkImp::DoIdleDelete() out")));
+ return ( filesLeft );
+ }
+
+
+// End of File