diff -r 000000000000 -r 1bce908db942 multimediacommscontroller/mmccfilesourcesink/src/mccfilevideo.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/multimediacommscontroller/mmccfilesourcesink/src/mccfilevideo.cpp Tue Feb 02 01:04:58 2010 +0200 @@ -0,0 +1,716 @@ +/* +* Copyright (c) 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: +* +*/ + + + + +#include +#include +#include +#include "mccfilevideo.h" +#include "mmcccodecinformation.h" +#include "mccperiodicrunner.h" +#include "mccfilesourcelogs.h" +#include "mccfilesourceimpl.h" +#include "mccinternaldef.h" +#include "mccresources.h" + + +const TUint32 KMccFileSourceParameterSetsIntervalInMicroSeconds = 2000000; + +// ----------------------------------------------------------------------------- +// CMccFileVideo::NewL +// ----------------------------------------------------------------------------- +// +CMccFileVideo* CMccFileVideo::NewL( + CMccFileSourceImpl& aSource, + MMccResources* aMccResources, + TUint32 aEndpointId ) + { + CMccFileVideo* self = + new (ELeave) CMccFileVideo( aSource, aMccResources, aEndpointId ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + + return self; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::ConstructL +// ----------------------------------------------------------------------------- +// +void CMccFileVideo::ConstructL() + { + CMccFileSourceTypeBase::ConstructL(); + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::CMccFileVideo +// ----------------------------------------------------------------------------- +// +CMccFileVideo::CMccFileVideo( + CMccFileSourceImpl& aSource, MMccResources* aMccResources, TUint32 aEndpointId ) + : CMccFileSourceTypeBase ( aSource, KUidMediaTypeVideo ), + iMccResources( aMccResources ), + iEndpointId( aEndpointId ), + iBufferType ( CCMRMediaBuffer::EVideoH263 ) + { + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::~CMccFileVideo +// ----------------------------------------------------------------------------- +// +CMccFileVideo::~CMccFileVideo() + { + delete iAVCNALUsBuffer; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::FillBufferL +// ----------------------------------------------------------------------------- +// +void CMccFileVideo::FillBufferL( + CMMFBuffer* aBuffer, + MDataSink* aConsumer ) + { + CMccFileSourceTypeBase::FillBufferL( aBuffer, aConsumer ); + + // There might be pending video frames, read the oldest one immediately + WriteBufferL(); + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::ParseUpdateAudioDescriptions +// ----------------------------------------------------------------------------- +// +TBool CMccFileVideo::ParseUpdateVideoDescriptions( MP4Handle aMP4Handle ) + { + TBool containVideo = EFalse; + mp4_u32 timeScale = 0; + if ( MP4ParseRequestVideoDescription( aMP4Handle, + ( mp4_u32* ) &iLength, &iFrameRate, ( mp4_u32* ) &iType, + ( mp4_u32* ) &iWidth, ( mp4_u32* ) &iHeight, + ( mp4_u32* ) &timeScale ) + == MP4_OK ) + { + if ( iType == MP4_TYPE_H263_PROFILE_0 ) + { + __FILESOURCE_CONTROLL( "CMccFileVideo::ParseUpdateVideoDescriptions \ +MP4_TYPE_H263_PROFILE_0" ) + + iBufferType = CCMRMediaBuffer::EVideoH263; + containVideo = ETrue; + iFourCC = TFourCC( KMccFourCCIdH263 ); + + TVideoClipProperties clipProperties; + MP4ParseGetVideoClipProperties( aMP4Handle, clipProperties ); + iLevel = clipProperties.iH263Level; + } + else if ( iType == MP4_TYPE_MPEG4_VIDEO ) + { + __FILESOURCE_CONTROLL( "CMccFileVideo::ParseUpdateVideoDescriptions \ +MP4_TYPE_MPEG4_VIDEO!" ) + + iBufferType = CCMRMediaBuffer::EVideoH263; + containVideo = ETrue; + iFourCC = TFourCC( KMccFourCCIdAVC ); + } + else if ( IsAVC() ) + { + __FILESOURCE_CONTROLL( "CMccFileVideo::\ +ParseUpdateVideoDescriptions AVC!" ) + + iBufferType = CCMRMediaBuffer::EVideoH263; + containVideo = ETrue; + iFourCC = TFourCC( KMccFourCCIdAVC ); + iIsFirstFrame = ETrue; + iInsertParameterSets = ETrue; + } + else if ( iType != MP4_TYPE_NONE ) + { + __FILESOURCE_CONTROLL( "CMccFileVideo::\ +ParseUpdateVideoDescriptions contains unsupported video" ) + iFourCC = TFourCC( KMccFourCCUnsupported ); + } + else + { + // NOP + } + } + + return containVideo; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::IsAVC +// ----------------------------------------------------------------------------- +// +TBool CMccFileVideo::IsAVC() + { + if ( ( iType == MP4_TYPE_AVC_PROFILE_BASELINE ) || + ( iType == MP4_TYPE_AVC_PROFILE_MAIN ) || + ( iType == MP4_TYPE_AVC_PROFILE_EXTENDED ) + ) + { + return ETrue; + } + + return EFalse; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::DoSetPositionL +// ----------------------------------------------------------------------------- +// +void CMccFileVideo::DoSetPositionL( TUint32 aPosition ) + { + __FILESOURCE_CONTROLL( "CMccFileVideo::DoSetPositionL" ) + + __ASSERT_ALWAYS( aPosition <= iLength, User::Leave( KErrArgument ) ); + mp4_u32 audioPos = 0; + // if retrun value is MP4_OK, iPosition is updated + if ( MP4ParseSeek( iMP4Handle, ( mp4_u32 ) aPosition, + ( mp4_u32* )&audioPos, ( mp4_u32* ) &iPosition, MP4TRUE ) + != MP4_OK ) + { + User::Leave( KErrGeneral ); + } + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::Type +// ----------------------------------------------------------------------------- +// +CCMRMediaBuffer::TBufferType CMccFileVideo::Type() + { + return iBufferType; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::GetFourCC +// ----------------------------------------------------------------------------- +// +TFourCC CMccFileVideo::GetFourCC() + { + return iFourCC; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::TickCallBack +// ----------------------------------------------------------------------------- +// +TCallBack CMccFileVideo::TickCallBack() + { + return TCallBack( TickVideoL, this ); + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::GetConfigKeyL +// ----------------------------------------------------------------------------- +// +HBufC8* CMccFileVideo::GetConfigKeyL() + { + __ASSERT_ALWAYS( IsAVC(), User::Leave( KErrNotSupported ) ); + HBufC8* configKey = NULL; + ExtractH264ParameterSetNALUsL( &configKey, ETrue ); + return configKey; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::Position +// ----------------------------------------------------------------------------- +// +TUint32 CMccFileVideo::Position() + { + return iPosition; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::DurationL +// ----------------------------------------------------------------------------- +// +TUint32 CMccFileVideo::Duration() + { + return iLength; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::AudioBitRateL +// ----------------------------------------------------------------------------- +// +TUint32 CMccFileVideo::AudioBitRate() + { + return 0; + } + + +// ----------------------------------------------------------------------------- +// CMccFileVideo::VideoFrameSizeL +// ----------------------------------------------------------------------------- +// +TSize CMccFileVideo::VideoFrameSize() + { + TSize size; + size.iWidth = iWidth; + size. iHeight = iHeight; + + return size; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::VideoFrameRateL +// ----------------------------------------------------------------------------- +// +TReal CMccFileVideo::VideoFrameRateL() + { + // Round to have zero decimals for clearness sake + TReal roundedFrameRate; + User::LeaveIfError( Math::Round( roundedFrameRate, iFrameRate, 0 ) ); + return roundedFrameRate; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::StartTimerL +// ----------------------------------------------------------------------------- +// +void CMccFileVideo::StartTimerL() + { + __FILESOURCE_CONTROLL( "CMccFileVideo::StartTimerL" ) + + if ( IsPaused() ) + { + SetPaused( EFalse ); + + SetPositionL( iPosition, ETrue ); + } + else + { + SetStartTime(); + } + + const TReal KMccSecondInMicroSeconds = 1000000; + TInt tickIntervalVideo = (TInt)KMccSecondInMicroSeconds; + if ( iFrameRate != 0 ) + { + // Set interval for video + tickIntervalVideo = (TInt)( KMccSecondInMicroSeconds / iFrameRate ); + + // Increase tick rate 22% + const TReal KMccTickModifier = 0.78; + tickIntervalVideo =(TInt) (tickIntervalVideo * KMccTickModifier); + } + + __FILESOURCE_CONTROLL_INT1( "CMccFileVideo::StartTimer (timer):", + tickIntervalVideo ) + + iTimerInterval = tickIntervalVideo; + iCorrectionInterval = iTimerInterval; + + iPeriodicRunner->Start( tickIntervalVideo, TCallBack( TickVideoL, this )); + + __FILESOURCE_CONTROLL( "CMccFileVideo::StartTimerL, exit" ) + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::TickVideoL +// ----------------------------------------------------------------------------- +// +TInt CMccFileVideo::TickVideoL( TAny* aObject ) + { + __FILESOURCE_CONTROLL( "CMccFileVideo::TickVideoL" ) + + static_cast(aObject)->ReadFrameL(); + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::ReadFrameL +// ----------------------------------------------------------------------------- +// +void CMccFileVideo::ReadFrameL() + { + __FILESOURCE_CONTROLL( "CMccFileVideo::ReadFrameL" ) + + + MP4Err retval = MP4ParseIsFrameAvailable( iMP4Handle, iType ); + + if ( retval != MP4_OK ) + { + __FILESOURCE_CONTROLL_INT1( "CMccFileVideo::ReadFrameL, no video", retval ) + + SetPaused( ETrue ); + TRAPD( positionErr, SetPositionL( 0 ) ); + iSource.PauseVideoL( positionErr ); + return; + } + + // Read the size of next video frame + __FILESOURCE_CONTROLL( "CMccFileVideo::ReadFrameL, reading frame" ) + + if ( MP4ParseNextFrameSize( iMP4Handle, iType, + ( mp4_u32* ) &iBufferSize ) != MP4_OK ) + { + __FILESOURCE_CONTROLL( "CMccFileVideo::ReadFrameL, \ +MP4ParseNextFrameSize not Ok" ) + return; + } + + TInt aVCNALUsBufferLen = 0; + if ( IsAVC() && iIsFirstFrame ) + { + delete iAVCNALUsBuffer; + iAVCNALUsBuffer = NULL; + + ExtractH264ParameterSetNALUsL( &iAVCNALUsBuffer ); + if( iAVCNALUsBuffer ) + { + aVCNALUsBufferLen += iAVCNALUsBuffer->Length(); + } + iIsFirstFrame = EFalse; + } + + // Allocate a buffer big enough for the next video frame + __FILESOURCE_CONTROLL_INT1( "CMccFileVideo::ReadFrameL, reading frame, size", iBufferSize ) + HBufC8* mediaBuffer = HBufC8::NewLC( iBufferSize + aVCNALUsBufferLen ); + TPtr8 mediaSinkBuffer = mediaBuffer->Des(); + mediaSinkBuffer.SetLength( iBufferSize + aVCNALUsBufferLen ); + TUint8* ptrMediaBuffer = CONST_CAST(TUint8*, mediaBuffer->Ptr()); + + TBool keyFrame = EFalse; + + TUint32 oldPosition = iPosition; + if ( MP4ParseReadVideoFrame( iMP4Handle, + ( mp4_u8* ) ptrMediaBuffer, ( mp4_u32 ) iBufferSize, + ( mp4_u32* ) &iFrameSize, ( mp4_u32* ) &iPosition, + ( mp4_bool* ) &keyFrame, NULL ) != MP4_OK ) + { + __FILESOURCE_CONTROLL( "CMccFileVideo::ReadFrameL, reading frame, NOT OK" ) + CleanupStack::PopAndDestroy( mediaBuffer ); + } + else + { + __FILESOURCE_CONTROLL_INT1( "CMccFileVideo::ReadFrameL, reading frame, OK, position", + iPosition ) + + DoTimingCorrection( iPosition ); + + // Update read interval (how much position changes on each frame read) + iReadInterval = iPosition - oldPosition; + + if ( IsAVC() ) + { + MarkWithNALUDelimiters( iBufferSize, mediaSinkBuffer ); + } + + TTimeIntervalMicroSeconds timeStamp = GetTimeStamp(); + + CMccFrameItem* item = new (ELeave) CMccFrameItem(); + item->iFrame = mediaBuffer; + CleanupStack::Pop( mediaBuffer ); + CleanupStack::PushL( item ); + item->iTimeStamp = timeStamp; + item->iKeyFrame = keyFrame; + iFrames.AppendL( item ); + CleanupStack::Pop( item ); + + WriteBufferL(); + } + + __FILESOURCE_CONTROLL( "CMccFileVideo::ReadFrameL, Time Check 2" ) + + __FILESOURCE_CONTROLL( "CMccFileVideo::ReadFrameL, \ +writing buffer to sink complete" ) + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::MarkWithNALUDelimiters() +// ----------------------------------------------------------------------------- +// +void CMccFileVideo::MarkWithNALUDelimiters( + const TInt aAccessUnitSize, TDes8& aBuffer ) + { + __FILESOURCE_CONTROLL( "CMccFileVideo::MarkWithNALUDelimiters" ) + + TUint8* ptrByte = NULL; + TInt indx = 0; + TUint32 size; + const TUint32 size_field_len = 4; + + do + { + ptrByte = const_cast ( aBuffer.Mid( indx ).Ptr() ); // get starting point of NALU size + size = BigEndian::Get32(ptrByte); // get NALU size + aBuffer[indx+0] = 0x00; + aBuffer[indx+1] = 0x00; + aBuffer[indx+2] = 0x00; + aBuffer[indx+3] = 0x01; + indx = indx + size + size_field_len; + + }while(indx < aAccessUnitSize); + + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::ExtractH264ParameterSetNALUsL() +// ----------------------------------------------------------------------------- +// +void CMccFileVideo::ExtractH264ParameterSetNALUsL( + HBufC8** aBufferOut, + TBool aSdpFormat ) + { + __FILESOURCE_CONTROLL( "CMccFileVideo::ExtractH264ParameterSetNALUsL" ) + + *aBufferOut = NULL; + + HBufC8* buffer = NULL; + HBufC8* bufferOut = NULL; + TUint32 decspecinfosize; + TUint8* ptrByte; + + // Query for size, buffer too small error is ok as we give zero length buffer + MP4Err retval = MP4ParseReadVideoDecoderSpecificInfo( + iMP4Handle, ( mp4_u8* ) NULL, 0, ( mp4_u32* ) &decspecinfosize ); + + if ( retval != MP4_OK && retval != MP4_BUFFER_TOO_SMALL ) + { + User::Leave(KErrGeneral); + } + + buffer = HBufC8::NewLC(decspecinfosize); + ptrByte = const_cast ( buffer->Des().Ptr() ); + + if( MP4ParseReadVideoDecoderSpecificInfo( iMP4Handle, + ( mp4_u8* ) ptrByte, + ( mp4_u32 ) buffer->Des().MaxSize(), + ( mp4_u32* ) &decspecinfosize + ) != MP4_OK ) + { + User::Leave(KErrGeneral); + } + else + { + buffer->Des().SetLength( decspecinfosize ); + } + + const TUint two = 2; + bufferOut = HBufC8::NewLC( buffer->Size() * two ); + TPtr8 ptrBufferOut = bufferOut->Des(); + TPtr8 ptrBuffer = buffer->Des(); + + // find NALUs in decoder info and put in buffer by marking with delimiters + + TUint8 tmpByte; + TUint16 size16; + TInt indx = 5; // skip other info in the beginning + TInt numNALUs = 0; + TInt lp_indx; + + // get number of seq. parameter set NALUs + tmpByte = ptrBuffer[indx++]; + numNALUs = tmpByte & 0x1F; // get rid of reserved '111' bits + + // extract seq. parameter set NALUs + + for ( lp_indx = 0; lp_indx < numNALUs; lp_indx++ ) + { + size16 = BigEndian::Get16( ptrBuffer.Mid( indx ).Ptr() ); + indx += 2; + + if ( aSdpFormat ) + { + // TBD: take possibility of multiple nalus into account + HBufC8* base64Encoded = + MccConversionUtility::Base64EncodeLC( ptrBuffer.Mid( indx, size16 ) ); + ptrBufferOut.Append( *base64Encoded ); + CleanupStack::PopAndDestroy( base64Encoded ); + } + else + { + // insert delimiter + tmpByte = 0x00; + ptrBufferOut.Append( &tmpByte, 1 ); + ptrBufferOut.Append( &tmpByte, 1 ); + ptrBufferOut.Append( &tmpByte, 1 ); + tmpByte = 0x01; + ptrBufferOut.Append( &tmpByte, 1 ); + + ptrBufferOut.Append( ptrBuffer.Mid( indx, size16 ) ); + } + + indx += size16; + } + + // get number of pic. parameter set NALUs + tmpByte = ptrBuffer[indx++]; + numNALUs = tmpByte; + + // extract pic. parameter set NALUs + + for ( lp_indx = 0; lp_indx < numNALUs; lp_indx++ ) + { + size16 = BigEndian::Get16( ptrBuffer.Mid( indx ).Ptr() ); + indx += 2; + + if ( aSdpFormat ) + { + // TBD: take possibility of multiple nalus into account + _LIT8( KMccFileSinkNaluSeparator, "," ); + ptrBufferOut.Append( KMccFileSinkNaluSeparator ); + HBufC8* base64Encoded = + MccConversionUtility::Base64EncodeLC( ptrBuffer.Mid( indx, size16 ) ); + ptrBufferOut.Append( *base64Encoded ); + CleanupStack::PopAndDestroy( base64Encoded ); + } + else + { + // insert delimiter + tmpByte = 0x00; + ptrBufferOut.Append( &tmpByte, 1 ); + ptrBufferOut.Append( &tmpByte, 1 ); + ptrBufferOut.Append( &tmpByte, 1 ); + tmpByte = 0x01; + ptrBufferOut.Append( &tmpByte, 1 ); + + ptrBufferOut.Append( ptrBuffer.Mid( indx, size16 ) ); + } + + indx += size16; + } + + CleanupStack::Pop( bufferOut ); + CleanupStack::PopAndDestroy( buffer ); + + *aBufferOut = bufferOut; + + __FILESOURCE_CONTROLL( "CMccFileVideo::ExtractH264ParameterSetNALUsL, exit" ) + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::InsertParameterSetNALUs +// ----------------------------------------------------------------------------- +// +TBool CMccFileVideo::InsertParameterSetNALUs() + { + TBool insertParameterSets( iInsertParameterSets ); + if ( insertParameterSets ) + { + iInsertParameterSets = EFalse; + iInsertParameterSetsTime.HomeTime(); + } + else + { + TTime currentTime; + currentTime.HomeTime(); + if ( currentTime.MicroSecondsFrom( iInsertParameterSetsTime ).Int64() > + KMccFileSourceParameterSetsIntervalInMicroSeconds ) + { + // Enough time elapsed from previous parameter set NALUs, + // insert them at next round. + iInsertParameterSets = ETrue; + } + } + return insertParameterSets; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::GetTimeStamp +// ----------------------------------------------------------------------------- +// +TTimeIntervalMicroSeconds CMccFileVideo::GetTimeStamp() + { + TInt64 position = iPosition + iPositionModifier; + TUint32 position2 = ( position < 0 ) ? 0 : (TUint32)position; + TTimeIntervalMicroSeconds timeStamp = + TTimeIntervalMicroSeconds ( (TInt64) position2 * KMccMicroToMilliConst ); + + __FILESOURCE_CONTROLL_INT1( "CMccFileVideo::GetTimeStamp timestamp (without clock frequency):", + timeStamp.Int64() ) + return timeStamp; + } + +// ----------------------------------------------------------------------------- +// CMccFileVideo::WriteBufferL +// If AVC NALUS buffer exists, it is passed always before keyframe and it uses +// timestamp of that frame. It is also passed always at beginning even if first +// frame wouldn't be keyframe. +// ----------------------------------------------------------------------------- +// +void CMccFileVideo::WriteBufferL() + { + __FILESOURCE_CONTROLL("CMccFileVideo::WriteBufferL") + + if ( iFrames.Count() && iConsumer && iConsumerBuffer ) + { + TBool removeCurrentFrame( ETrue ); + + CMMFDataBuffer* buf = static_cast(iConsumerBuffer); + + CMccFrameItem* item = iFrames[0]; + HBufC8* videoFrame = item->iFrame; + + if ( iAVCNALUsBuffer && InsertParameterSetNALUs() ) + { + videoFrame = iAVCNALUsBuffer; + removeCurrentFrame = EFalse; + } + + if ( buf->Data().MaxLength() >= videoFrame->Des().Length() ) + { + buf->Data().Delete( 0, buf->BufferSize() ); + + buf->Data().Append( *videoFrame ); + + iConsumerBuffer->SetTimeToPlay(item->iTimeStamp); + iConsumerBuffer->SetLastBuffer(EFalse); + iConsumerBuffer->SetFrameNumber( iSequenceNum ); + + if ( removeCurrentFrame ) + { + if ( iMccResources && item->iKeyFrame ) + { + iMccResources->StoreKeyFrameInfoL( iEndpointId, *iConsumerBuffer ); + } + + delete item; + iFrames.Remove(0); + } + + videoFrame = NULL; + + iConsumer->BufferFilledL( iConsumerBuffer ); + iConsumer = NULL; + iConsumerBuffer = NULL; + iSequenceNum++; + __FILESOURCE_CONTROLL("CMccFileVideo::WriteBufferL, done") + } + else + { + __FILESOURCE_CONTROLL("CMccFileVideo::WriteBufferL, drop frame") + delete iFrames[0]; + iFrames.Remove(0); + videoFrame = NULL; + } + } + else + { + __FILESOURCE_CONTROLL("CMccFileVideo::WriteBufferL, writing ignored") + } + } + +