/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** ****************************************************************************** * @file M4VSS3GPP_EditAudio.c * @brief Video Studio Service 3GPP edit API implementation. * @note ****************************************************************************** */ /****************/ /*** Includes ***/ /****************/ #include "NXPSW_CompilerSwitches.h" /** * Our header */ #include "M4VSS3GPP_API.h" #include "M4VSS3GPP_InternalTypes.h" #include "M4VSS3GPP_InternalFunctions.h" #include "M4VSS3GPP_InternalConfig.h" #include "M4VSS3GPP_ErrorCodes.h" /** * OSAL headers */ #include "M4OSA_Memory.h" /**< OSAL memory management */ #include "M4OSA_Debug.h" /**< OSAL debug management */ #define PWR_FXP_FRACT_MAX (32768) /************************************************************************/ /* Static local functions */ /************************************************************************/ static M4OSA_ERR M4VSS3GPP_intCheckAudioMode( M4VSS3GPP_InternalEditContext *pC ); static M4OSA_Void M4VSS3GPP_intCheckAudioEffects( M4VSS3GPP_InternalEditContext *pC, M4OSA_UInt8 uiClipNumber ); static M4OSA_ERR M4VSS3GPP_intApplyAudioEffect( M4VSS3GPP_InternalEditContext *pC, M4OSA_UInt8 uiClip1orClip2, M4OSA_Int16 *pPCMdata, M4OSA_UInt32 uiPCMsize ); static M4OSA_ERR M4VSS3GPP_intAudioTransition( M4VSS3GPP_InternalEditContext *pC, M4OSA_Int16 *pPCMdata1, M4OSA_Int16 *pPCMdata2, M4OSA_UInt32 uiPCMsize ); /** ****************************************************************************** * M4OSA_ERR M4VSS3GPP_intEditJumpMP3() * @brief One step of jumping processing for the MP3 clip. * @note On one step, the jump of several AU is done * @param pC (IN/OUT) Internal edit context ****************************************************************************** */ M4OSA_ERR M4VSS3GPP_intEditJumpMP3( M4VSS3GPP_InternalEditContext *pC ) { M4OSA_ERR err; M4VSS3GPP_ClipContext *pClip = pC->pC1; /**< shortcut */ M4OSA_Int32 JumpCts; JumpCts = pClip->iActualAudioBeginCut; err = M4VSS3GPP_intClipJumpAudioAt(pClip, &JumpCts); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intOpenClip: M4VSS3GPP_intClipJumpAudioAt(A) returns 0x%x!", err); return err; } if( JumpCts >= pClip->iActualAudioBeginCut ) { pC->State = M4VSS3GPP_kEditState_MP3; /** * Update clip offset with the audio begin cut */ pClip->iAoffset = -JumpCts; /** * The audio is currently in reading mode */ pClip->Astatus = M4VSS3GPP_kClipStatus_READ; } return M4NO_ERROR; } /** ****************************************************************************** * M4OSA_ERR M4VSS3GPP_intEditStepMP3() * @brief One step of audio processing for the MP3 clip * @param pC (IN/OUT) Internal edit context ****************************************************************************** */ M4OSA_ERR M4VSS3GPP_intEditStepMP3( M4VSS3GPP_InternalEditContext *pC ) { M4OSA_ERR err; M4VSS3GPP_ClipContext *pClip = pC->pC1; /**< shortcut */ /** * Copy the input AU to the output AU */ err = pC->pOsaFileWritPtr->writeData(pC->ewc.p3gpWriterContext, pClip->pAudioFramePtr, (M4OSA_UInt32)pClip->uiAudioFrameSize); /** * Read the next audio frame */ err = M4VSS3GPP_intClipReadNextAudioFrame(pClip); if( M4OSA_ERR_IS_ERROR(err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepMP3: READ_WRITE:\ M4VSS3GPP_intClipReadNextAudioFrame returns 0x%x!", err); return err; } else { /** * Update current time (to=tc+T) */ pC->ewc.dATo = ( pClip->iAudioFrameCts + pClip->iAoffset) / pClip->scale_audio; if( (M4OSA_Int32)(pClip->iAudioFrameCts / pClip->scale_audio + 0.5) >= pClip->iEndTime ) { M4READER_Buffer mp3tagBuffer; /** * The duration is better respected if the first AU and last AU are both above the cut time */ err = pC->pOsaFileWritPtr->writeData(pC->ewc.p3gpWriterContext, pClip->pAudioFramePtr, (M4OSA_UInt32)pClip->uiAudioFrameSize); /* The ID3v1 tag is always at the end of the mp3 file so the end of the cutting process is waited */ /* before writing the metadata in the output file*/ /* Retrieve the data of the ID3v1 Tag */ err = pClip->ShellAPI.m_pReader->m_pFctGetOption( pClip->pReaderContext, M4READER_kOptionID_Mp3Id3v1Tag, (M4OSA_DataOption) &mp3tagBuffer); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepMP3: M4MP3R_getOption returns 0x%x", err); return err; } /* Write the data of the ID3v1 Tag in the output file */ if( 0 != mp3tagBuffer.m_uiBufferSize ) { err = pC->pOsaFileWritPtr->writeData(pC->ewc.p3gpWriterContext, (M4OSA_MemAddr8)mp3tagBuffer.m_pData, mp3tagBuffer.m_uiBufferSize); /** * Free before the error checking anyway */ free(mp3tagBuffer.m_pData); /** * Error checking */ if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepMP3:\ pOsaFileWritPtr->writeData(ID3v1Tag) returns 0x%x", err); return err; } mp3tagBuffer.m_uiBufferSize = 0; mp3tagBuffer.m_pData = M4OSA_NULL; } /* The End Cut has been reached */ err = M4VSS3GPP_intReachedEndOfAudio(pC); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepMP3 : M4VSS3GPP_intReachedEndOfAudio returns 0x%x", err); return err; } } if( ( M4WAR_NO_MORE_AU == err) && (M4OSA_FALSE == pC->bSupportSilence) ) /**< Reached end of clip */ { err = M4VSS3GPP_intReachedEndOfAudio( pC); /**< Clip done, do the next one */ if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepMP3: READ_WRITE:\ M4VSS3GPP_intReachedEndOfAudio returns 0x%x", err); return err; } } } /** * Return with no error */ M4OSA_TRACE3_0("M4VSS3GPP_intEditStepMP3: returning M4NO_ERROR"); return M4NO_ERROR; } /** ****************************************************************************** * M4OSA_ERR M4VSS3GPP_intEditStepAudio() * @brief One step of audio processing * @param pC (IN/OUT) Internal edit context ****************************************************************************** */ M4OSA_ERR M4VSS3GPP_intEditStepAudio( M4VSS3GPP_InternalEditContext *pC ) { M4OSA_ERR err; int32_t auTimeStamp = -1; M4ENCODER_AudioBuffer pEncInBuffer; /**< Encoder input buffer for api */ M4ENCODER_AudioBuffer pEncOutBuffer; /**< Encoder output buffer for api */ M4OSA_Time frameTimeDelta; /**< Duration of the encoded (then written) data */ M4OSA_Bool bStopAudio; /** * Check if we reached end cut */ if( ( pC->ewc.dATo - pC->pC1->iAoffset / pC->pC1->scale_audio + 0.5) >= pC->pC1->iEndTime ) { /** * Audio is done for this clip */ err = M4VSS3GPP_intReachedEndOfAudio(pC); /* RC: to know when a file has been processed */ if( M4NO_ERROR != err && err != M4VSS3GPP_WAR_SWITCH_CLIP ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: M4VSS3GPP_intReachedEndOfAudio returns 0x%x", err); } return err; } /** * Check Audio Mode, depending on the current output CTS */ err = M4VSS3GPP_intCheckAudioMode( pC); /**< This function change the pC->Astate variable! */ if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: M4VSS3GPP_intCheckAudioMode returns 0x%x!", err); return err; } M4OSA_TRACE2_3(" AUDIO step : dATo = %f state = %d offset = %ld", pC->ewc.dATo, pC->Astate, pC->pC1->iAoffset); bStopAudio = M4OSA_FALSE; switch( pC->Astate ) { /* _________________ */ /*| |*/ /*| READ_WRITE MODE |*/ /*|_________________|*/ case M4VSS3GPP_kEditAudioState_READ_WRITE: { M4OSA_TRACE3_0("M4VSS3GPP_intEditStepAudio READ_WRITE"); /** * Get the output AU to write into */ err = pC->ShellAPI.pWriterDataFcts->pStartAU( pC->ewc.p3gpWriterContext, M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio:\ READ_WRITE: pWriterDataFcts->pStartAU returns 0x%x!", err); return err; } /** * Compute output audio CTS */ pC->ewc.WriterAudioAU.CTS = pC->pC1->iAudioFrameCts + pC->pC1->iAoffset; /** * BZZZ bug fix (read-write case): * Replace the first AMR AU of the stream with a silence AU. * It removes annoying "BZZZ" audio glitch. * It is not needed if there is a begin cut. * It is not needed for the first clip. * Because of another bugfix (2005-03-24), the first AU written may be * the second one which CTS is 20. Hence the cts<21 test. * (the BZZZ effect occurs even with the second AU!) */ if( ( M4OSA_FALSE == pC->pC1->bFirstAuWritten) && (0 != pC->uiCurrentClip) && (pC->pC1->iAudioFrameCts < (pC->ewc.iSilenceFrameDuration + 1)) ) { /** * Copy a silence AU to the output */ pC->ewc.WriterAudioAU.size = pC->ewc.uiSilenceFrameSize; memcpy((void *)pC->ewc.WriterAudioAU.dataAddress, (void *)pC->ewc.pSilenceFrameData, pC->ewc.uiSilenceFrameSize); M4OSA_TRACE2_0("A #### silence AU"); } else if( (M4OSA_UInt32)pC->pC1->uiAudioFrameSize < pC->ewc.uiAudioMaxAuSize ) { /** * Copy the input AU to the output AU */ pC->ewc.WriterAudioAU.size = (M4OSA_UInt32)pC->pC1->uiAudioFrameSize; memcpy((void *)pC->ewc.WriterAudioAU.dataAddress, (void *)pC->pC1->pAudioFramePtr, pC->ewc.WriterAudioAU.size); } else { M4OSA_TRACE1_2( "M4VSS3GPP_intEditStepAudio: READ_WRITE: AU size greater than MaxAuSize \ (%d>%d)! returning M4VSS3GPP_ERR_INPUT_AUDIO_AU_TOO_LARGE", pC->pC1->uiAudioFrameSize, pC->ewc.uiAudioMaxAuSize); return M4VSS3GPP_ERR_INPUT_AUDIO_AU_TOO_LARGE; } /** * This boolean is only used to fix the BZZ bug... */ pC->pC1->bFirstAuWritten = M4OSA_TRUE; M4OSA_TRACE2_2("B ---- write : cts = %ld [ 0x%x ]", (M4OSA_Int32)(pC->ewc.WriterAudioAU.CTS / pC->ewc.scale_audio), pC->ewc.WriterAudioAU.size); /** * Write the AU */ err = pC->ShellAPI.pWriterDataFcts->pProcessAU( pC->ewc.p3gpWriterContext, M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU); if( M4NO_ERROR != err ) { /*11/12/2008 CR 3283 MMS use case for VideoArtist the warning M4WAR_WRITER_STOP_REQ is returned when the targeted output file size is reached The editing is then finished, the warning M4VSS3GPP_WAR_EDITING_DONE is returned*/ if( M4WAR_WRITER_STOP_REQ == err ) { M4OSA_TRACE1_0( "M4VSS3GPP_intEditStepAudio: File was cut to avoid oversize"); return M4VSS3GPP_WAR_EDITING_DONE; } else { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio:\ READ_WRITE: pWriterDataFcts->pProcessAU returns 0x%x!", err); return err; } } /** * Audio is now in read mode (there may be a "if(status!=READ)" here, but it is removed for optimization) */ pC->pC1->Astatus = M4VSS3GPP_kClipStatus_READ; /** * Read the next audio frame */ err = M4VSS3GPP_intClipReadNextAudioFrame(pC->pC1); M4OSA_TRACE2_3("C .... read : cts = %.0f + %.0f [ 0x%x ]", pC->pC1->iAudioFrameCts / pC->pC1->scale_audio, pC->pC1->iAoffset / pC->pC1->scale_audio, pC->pC1->uiAudioFrameSize); if( M4OSA_ERR_IS_ERROR(err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: READ_WRITE:\ M4VSS3GPP_intClipReadNextAudioFrame returns 0x%x!", err); return err; } else { /** * Update current time (to=tc+T) */ pC->ewc.dATo = ( pC->pC1->iAudioFrameCts + pC->pC1->iAoffset) / pC->pC1->scale_audio; if( ( M4WAR_NO_MORE_AU == err) && (M4OSA_FALSE == pC->bSupportSilence) ) { /** * If output is other than AMR or AAC (i.e. EVRC,we can't write silence into it) * So we simply end here.*/ bStopAudio = M4OSA_TRUE; } } } break; /* ____________________ */ /*| |*/ /*| DECODE_ENCODE MODE |*/ /*|____________________|*/ case M4VSS3GPP_kEditAudioState_DECODE_ENCODE: { M4OSA_TRACE3_0("M4VSS3GPP_intEditStepAudio DECODE_ENCODE"); /** * Get the output AU to write into */ err = pC->ShellAPI.pWriterDataFcts->pStartAU( pC->ewc.p3gpWriterContext, M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: DECODE_ENCODE:\ pWriterDataFcts->pStartAU returns 0x%x!", err); return err; } /** * If we were reading the clip, we must jump a few AU backward to decode/encode (without writing result) from that point. */ if( M4VSS3GPP_kClipStatus_READ == pC->pC1->Astatus ) { M4OSA_Int32 iTargetCts, iCurrentCts; if( 0 != pC->pC1-> iAudioFrameCts ) /**<don't try to pre-decode if clip is at its beginning. */ { /** * Jump a few AUs backward */ iCurrentCts = pC->pC1->iAudioFrameCts; iTargetCts = iCurrentCts - M4VSS3GPP_NB_AU_PREFETCH * pC->ewc.iSilenceFrameDuration; if( iTargetCts < 0 ) { iTargetCts = 0; /**< Sanity check */ } err = M4VSS3GPP_intClipJumpAudioAt(pC->pC1, &iTargetCts); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: DECODE_ENCODE-prefetch:\ M4VSS3GPP_intClipJumpAudioAt returns 0x%x!", err); return err; } err = M4VSS3GPP_intClipReadNextAudioFrame( pC->pC1); /**< read AU where we jumped */ M4OSA_TRACE2_3("D .... read : cts = %.0f + %.0f [ 0x%x ]", pC->pC1->iAudioFrameCts / pC->pC1->scale_audio, pC->pC1->iAoffset / pC->pC1->scale_audio, pC->pC1->uiAudioFrameSize); if( M4OSA_ERR_IS_ERROR(err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: DECODE_ENCODE-prefetch:\ M4VSS3GPP_intClipReadNextAudioFrame(a) returns 0x%x!", err); return err; } /** * Decode/encode up to the wanted position */ while( pC->pC1->iAudioFrameCts < iCurrentCts ) { err = M4VSS3GPP_intClipDecodeCurrentAudioFrame(pC->pC1); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: DECODE_ENCODE-prefetch: \ M4VSS3GPP_intClipDecodeCurrentAudioFrame returns 0x%x!", err); return err; } /* [Mono] or [Stereo interleaved] : all is in one buffer */ pEncInBuffer.pTableBuffer[0] = pC->pC1->AudioDecBufferOut.m_dataAddress; pEncInBuffer.pTableBufferSize[0] = pC->pC1->AudioDecBufferOut.m_bufferSize; pEncInBuffer.pTableBuffer[1] = M4OSA_NULL; pEncInBuffer.pTableBufferSize[1] = 0; /* Time in ms from data size, because it is PCM16 samples */ frameTimeDelta = pEncInBuffer.pTableBufferSize[0] / sizeof(short) / pC->ewc.uiNbChannels; /** * Prepare output buffer */ pEncOutBuffer.pTableBuffer[0] = (M4OSA_MemAddr8)pC->ewc.WriterAudioAU.dataAddress; pEncOutBuffer.pTableBufferSize[0] = 0; M4OSA_TRACE2_0("E **** pre-encode"); #ifdef M4VSS_SUPPORT_OMX_CODECS /*OMX Audio decoder used. * OMX Audio dec shell does internal buffering and hence does not return a PCM buffer for every decodeStep call.* * So PCM buffer sizes might be 0. In this case donot call encode Step*/ if( 0 != pEncInBuffer.pTableBufferSize[0] ) { #endif /** * Encode the PCM audio */ err = pC->ShellAPI.pAudioEncoderGlobalFcts->pFctStep( pC->ewc.pAudioEncCtxt, &pEncInBuffer, &pEncOutBuffer); if( ( M4NO_ERROR != err) && (M4WAR_NO_MORE_AU != err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio():\ pAudioEncoderGlobalFcts->pFctStep returns 0x%x", err); return err; } #ifdef M4VSS_SUPPORT_OMX_CODECS } //if(0 != pEncInBuffer.pTableBufferSize[0]) #endif pC->pC1->pAudioFramePtr = M4OSA_NULL; // Get timestamp of last read AU pC->pC1->ShellAPI.m_pAudioDecoder->m_pFctGetOptionAudioDec( pC->pC1->pAudioDecCtxt, M4AD_kOptionID_AuCTS, (M4OSA_DataOption) &auTimeStamp); if (auTimeStamp == -1) { M4OSA_TRACE1_0("M4VSS3GPP_intEditStepAudio: \ invalid audio timestamp returned"); return M4WAR_INVALID_TIME; } pC->pC1->iAudioFrameCts = auTimeStamp; } } /** * Audio is now OK for decoding */ pC->pC1->Astatus = M4VSS3GPP_kClipStatus_DECODE; } /** * Decode the input audio */ err = M4VSS3GPP_intClipDecodeCurrentAudioFrame(pC->pC1); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: DECODE_ENCODE:\ M4VSS3GPP_intClipDecodeCurrentAudioFrame returns 0x%x", err); return err; } pC->pC1->pAudioFramePtr = M4OSA_NULL; // Get timestamp of last read AU pC->pC1->ShellAPI.m_pAudioDecoder->m_pFctGetOptionAudioDec( pC->pC1->pAudioDecCtxt, M4AD_kOptionID_AuCTS, (M4OSA_DataOption) &auTimeStamp); if (auTimeStamp == -1) { M4OSA_TRACE1_0("M4VSS3GPP_intEditStepAudio: invalid audio \ timestamp returned"); return M4WAR_INVALID_TIME; } pC->pC1->iAudioFrameCts = auTimeStamp; /** * Apply the effect */ if( pC->iClip1ActiveEffect >= 0 ) { err = M4VSS3GPP_intApplyAudioEffect(pC, 1, (M4OSA_Int16 *)pC->pC1->AudioDecBufferOut.m_dataAddress, pC->pC1->AudioDecBufferOut.m_bufferSize); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: DECODE_ENCODE:\ M4VSS3GPP_intEndAudioEffect returns 0x%x", err); return err; } } /** * Compute output audio CTS */ pC->ewc.WriterAudioAU.CTS = pC->pC1->iAudioFrameCts + pC->pC1->iAoffset; /* May happen with corrupted input files (which have stts entries not multiple of SilenceFrameDuration) */ if( pC->ewc.WriterAudioAU.CTS < 0 ) { pC->ewc.WriterAudioAU.CTS = 0; } /** * BZZZ bug fix (decode-encode case): * (Yes, the Bzz bug may also occur when we re-encode. It doesn't * occur at the decode before the encode, but at the playback!) * Replace the first AMR AU of the encoded stream with a silence AU. * It removes annoying "BZZZ" audio glitch. * It is not needed if there is a begin cut. * It is not needed for the first clip. * Because of another bugfix (2005-03-24), the first AU written may be * the second one which CTS is 20. Hence the cts<21 test. * (the BZZZ effect occurs even with the second AU!) */ if( ( M4OSA_FALSE == pC->pC1->bFirstAuWritten) && (0 != pC->uiCurrentClip) && (pC->pC1->iAudioFrameCts < (pC->ewc.iSilenceFrameDuration + 1)) ) { /** * Copy a silence AMR AU to the output */ pC->ewc.WriterAudioAU.size = pC->ewc.uiSilenceFrameSize; memcpy((void *)pC->ewc.WriterAudioAU.dataAddress, (void *)pC->ewc.pSilenceFrameData, pC->ewc.uiSilenceFrameSize); M4OSA_TRACE2_0("G #### silence AU"); } else { /** * Encode the filtered PCM audio directly into the output AU */ /* [Mono] or [Stereo interleaved] : all is in one buffer */ pEncInBuffer.pTableBuffer[0] = pC->pC1->AudioDecBufferOut.m_dataAddress; pEncInBuffer.pTableBufferSize[0] = pC->pC1->AudioDecBufferOut.m_bufferSize; pEncInBuffer.pTableBuffer[1] = M4OSA_NULL; pEncInBuffer.pTableBufferSize[1] = 0; /* Time in ms from data size, because it is PCM16 samples */ frameTimeDelta = pEncInBuffer.pTableBufferSize[0] / sizeof(short) / pC->ewc.uiNbChannels; /** * Prepare output buffer */ pEncOutBuffer.pTableBuffer[0] = (M4OSA_MemAddr8)pC->ewc.WriterAudioAU.dataAddress; pEncOutBuffer.pTableBufferSize[0] = 0; M4OSA_TRACE2_0("H ++++ encode AU"); #ifdef M4VSS_SUPPORT_OMX_CODECS /*OMX Audio decoder used. * OMX Audio dec shell does internal buffering and hence does not return a PCM buffer for every decodeStep call.* * So PCM buffer sizes might be 0. In this case donot call encode Step*/ if( 0 != pEncInBuffer.pTableBufferSize[0] ) { #endif /** * Encode the PCM audio */ err = pC->ShellAPI.pAudioEncoderGlobalFcts->pFctStep( pC->ewc.pAudioEncCtxt, &pEncInBuffer, &pEncOutBuffer); if( ( M4NO_ERROR != err) && (M4WAR_NO_MORE_AU != err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio():\ pAudioEncoderGlobalFcts->pFctStep returns 0x%x", err); return err; } #ifdef M4VSS_SUPPORT_OMX_CODECS } #endif /** * Set AU size */ pC->ewc.WriterAudioAU.size = pEncOutBuffer.pTableBufferSize[ 0]; /**< Get the size of encoded data */ } /** * This boolean is only used to fix the BZZ bug... */ pC->pC1->bFirstAuWritten = M4OSA_TRUE; M4OSA_TRACE2_2("I ---- write : cts = %ld [ 0x%x ]", (M4OSA_Int32)(pC->ewc.WriterAudioAU.CTS / pC->ewc.scale_audio), pC->ewc.WriterAudioAU.size); /** * Write the AU */ err = pC->ShellAPI.pWriterDataFcts->pProcessAU( pC->ewc.p3gpWriterContext, M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU); if( M4NO_ERROR != err ) { /*11/12/2008 CR 3283 MMS use case for VideoArtist the warning M4WAR_WRITER_STOP_REQ is returned when the targeted output file size is reached The editing is then finished, the warning M4VSS3GPP_WAR_EDITING_DONE is returned*/ if( M4WAR_WRITER_STOP_REQ == err ) { M4OSA_TRACE1_0( "M4VSS3GPP_intEditStepAudio: File was cut to avoid oversize"); return M4VSS3GPP_WAR_EDITING_DONE; } else { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: DECODE_ENCODE:\ pWriterDataFcts->pProcessAU returns 0x%x!", err); return err; } } /** * Read the next audio frame */ err = M4VSS3GPP_intClipReadNextAudioFrame(pC->pC1); M4OSA_TRACE2_3("J .... read : cts = %.0f + %.0f [ 0x%x ]", pC->pC1->iAudioFrameCts / pC->pC1->scale_audio, pC->pC1->iAoffset / pC->pC1->scale_audio, pC->pC1->uiAudioFrameSize); if( M4OSA_ERR_IS_ERROR(err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: DECODE_ENCODE:\ M4VSS3GPP_intClipReadNextAudioFrame returns 0x%x!", err); return err; } else { /** * Update current time (to=tc+T) */ pC->ewc.dATo = ( pC->pC1->iAudioFrameCts + pC->pC1->iAoffset) / pC->pC1->scale_audio; if( ( M4WAR_NO_MORE_AU == err) && (M4OSA_FALSE == pC->bSupportSilence) ) { /** * If output is other than AMR or AAC (i.e. EVRC,we can't write silence into it) * So we simply end here.*/ bStopAudio = M4OSA_TRUE; } } } break; /* _________________ */ /*| |*/ /*| TRANSITION MODE |*/ /*|_________________|*/ case M4VSS3GPP_kEditAudioState_TRANSITION: { M4OSA_TRACE3_0("M4VSS3GPP_intEditStepAudio TRANSITION"); /** * Get the output AU to write into */ err = pC->ShellAPI.pWriterDataFcts->pStartAU( pC->ewc.p3gpWriterContext, M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION:\ pWriterDataFcts->pStartAU returns 0x%x!", err); return err; } /** * If we were reading the clip, we must jump a few AU backward to decode/encode (without writing result) from that point. */ if( M4VSS3GPP_kClipStatus_READ == pC->pC1->Astatus ) { M4OSA_Int32 iTargetCts, iCurrentCts; if( 0 != pC->pC1-> iAudioFrameCts ) /**<don't try to pre-decode if clip is at its beginning.*/ { /** * Jump a few AUs backward */ iCurrentCts = pC->pC1->iAudioFrameCts; iTargetCts = iCurrentCts - M4VSS3GPP_NB_AU_PREFETCH * pC->ewc.iSilenceFrameDuration; if( iTargetCts < 0 ) { iTargetCts = 0; /**< Sanity check */ } err = M4VSS3GPP_intClipJumpAudioAt(pC->pC1, &iTargetCts); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION-prefetch:\ M4VSS3GPP_intClipJumpAudioAt returns 0x%x!", err); return err; } err = M4VSS3GPP_intClipReadNextAudioFrame( pC->pC1); /**< read AU where we jumped */ M4OSA_TRACE2_3("K .... read : cts = %.0f + %.0f [ 0x%x ]", pC->pC1->iAudioFrameCts / pC->pC1->scale_audio, pC->pC1->iAoffset / pC->pC1->scale_audio, pC->pC1->uiAudioFrameSize); if( M4OSA_ERR_IS_ERROR(err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION-prefetch:\ M4VSS3GPP_intClipReadNextAudioFrame(a) returns 0x%x!", err); return err; } /** * Decode/encode up to the wanted position */ while( pC->pC1->iAudioFrameCts < iCurrentCts ) { err = M4VSS3GPP_intClipDecodeCurrentAudioFrame(pC->pC1); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION-prefetch:\ M4VSS3GPP_intClipDecodeCurrentAudioFrame returns 0x%x!", err); return err; } /* [Mono] or [Stereo interleaved] : all is in one buffer */ pEncInBuffer.pTableBuffer[0] = pC->pC1->AudioDecBufferOut.m_dataAddress; pEncInBuffer.pTableBufferSize[0] = pC->pC1->AudioDecBufferOut.m_bufferSize; pEncInBuffer.pTableBuffer[1] = M4OSA_NULL; pEncInBuffer.pTableBufferSize[1] = 0; /* Time in ms from data size, because it is PCM16 samples */ frameTimeDelta = pEncInBuffer.pTableBufferSize[0] / sizeof(short) / pC->ewc.uiNbChannels; /** * Prepare output buffer */ pEncOutBuffer.pTableBuffer[0] = (M4OSA_MemAddr8)pC->ewc.WriterAudioAU.dataAddress; pEncOutBuffer.pTableBufferSize[0] = 0; M4OSA_TRACE2_0("L **** pre-encode"); #ifdef M4VSS_SUPPORT_OMX_CODECS /*OMX Audio decoder used. * OMX Audio dec shell does internal buffering and hence does not return a PCM buffer for every decodeStep call.* * So PCM buffer sizes might be 0. In this case donot call encode Step*/ if( 0 != pEncInBuffer.pTableBufferSize[0] ) { #endif /** * Encode the PCM audio */ err = pC->ShellAPI.pAudioEncoderGlobalFcts->pFctStep( pC->ewc.pAudioEncCtxt, &pEncInBuffer, &pEncOutBuffer); if( ( M4NO_ERROR != err) && (M4WAR_NO_MORE_AU != err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio():\ pAudioEncoderGlobalFcts->pFctStep returns 0x%x", err); return err; } #ifdef M4VSS_SUPPORT_OMX_CODECS } #endif err = M4VSS3GPP_intClipReadNextAudioFrame(pC->pC1); M4OSA_TRACE2_3( "M .... read : cts = %.0f + %.0f [ 0x%x ]", pC->pC1->iAudioFrameCts / pC->pC1->scale_audio, pC->pC1->iAoffset / pC->pC1->scale_audio, pC->pC1->uiAudioFrameSize); if( M4OSA_ERR_IS_ERROR(err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION-prefetch:\ M4VSS3GPP_intClipReadNextAudioFrame(b) returns 0x%x!", err); return err; } } } /** * Audio is now OK for decoding */ pC->pC1->Astatus = M4VSS3GPP_kClipStatus_DECODE; } /** * Decode the first input audio */ err = M4VSS3GPP_intClipDecodeCurrentAudioFrame(pC->pC1); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION:\ M4VSS3GPP_intClipDecodeCurrentAudioFrame(C1) returns 0x%x", err); return err; } /** * Decode the second input audio */ err = M4VSS3GPP_intClipDecodeCurrentAudioFrame(pC->pC2); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION:\ M4VSS3GPP_intClipDecodeCurrentAudioFrame(C2) returns 0x%x", err); return err; } /** * Check both clips decoded the same amount of PCM samples */ if( pC->pC1->AudioDecBufferOut.m_bufferSize != pC->pC2->AudioDecBufferOut.m_bufferSize ) { M4OSA_TRACE1_2( "ERR : AudioTransition: both clips AU must have the same decoded\ PCM size! pc1 size=0x%x, pC2 size = 0x%x", pC->pC1->AudioDecBufferOut.m_bufferSize, pC->pC2->AudioDecBufferOut.m_bufferSize); #ifdef M4VSS_SUPPORT_OMX_CODECS /*OMX Audio decoder used. * OMX Audio dec shell does internal buffering and hence does not return a PCM buffer for every decodeStep call.* * So PCM buffer sizes might be 0 or different for clip1 and clip2. * So no need to return error in this case */ M4OSA_TRACE1_2( "M4VSS3GPP_intEditStepAudio: , pc1 AudBuff size=0x%x,\ pC2 AudBuff size = 0x%x", pC->pC1->AudioDecBufferOut.m_bufferSize, pC->pC2->AudioDecBufferOut.m_bufferSize); #else return M4VSS3GPP_ERR_AUDIO_DECODED_PCM_SIZE_ISSUE; #endif // M4VSS_SUPPORT_OMX_CODECS } /** * Apply the audio effect on clip1 */ if( pC->iClip1ActiveEffect >= 0 ) { err = M4VSS3GPP_intApplyAudioEffect(pC, 1, (M4OSA_Int16 *)pC->pC1->AudioDecBufferOut.m_dataAddress, pC->pC1->AudioDecBufferOut.m_bufferSize); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION:\ M4VSS3GPP_intApplyAudioEffect(C1) returns 0x%x", err); return err; } } /** * Apply the audio effect on clip2 */ if( pC->iClip2ActiveEffect >= 0 ) { err = M4VSS3GPP_intApplyAudioEffect(pC, 2, (M4OSA_Int16 *)pC->pC2->AudioDecBufferOut.m_dataAddress, pC->pC2->AudioDecBufferOut.m_bufferSize); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION:\ M4VSS3GPP_intApplyAudioEffect(C2) returns 0x%x", err); return err; } } /** * Apply the transition effect */ err = M4VSS3GPP_intAudioTransition(pC, (M4OSA_Int16 *)pC->pC1->AudioDecBufferOut.m_dataAddress, (M4OSA_Int16 *)pC->pC2->AudioDecBufferOut.m_dataAddress, pC->pC1->AudioDecBufferOut.m_bufferSize); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION:\ M4VSS3GPP_intAudioTransition returns 0x%x", err); return err; } /* [Mono] or [Stereo interleaved] : all is in one buffer */ pEncInBuffer.pTableBuffer[0] = pC->pC1->AudioDecBufferOut.m_dataAddress; pEncInBuffer.pTableBufferSize[0] = pC->pC1->AudioDecBufferOut.m_bufferSize; pEncInBuffer.pTableBuffer[1] = M4OSA_NULL; pEncInBuffer.pTableBufferSize[1] = 0; /* Time in ms from data size, because it is PCM16 samples */ frameTimeDelta = pEncInBuffer.pTableBufferSize[0] / sizeof(short) / pC->ewc.uiNbChannels; /** * Prepare output buffer */ pEncOutBuffer.pTableBuffer[0] = (M4OSA_MemAddr8)pC->ewc.WriterAudioAU.dataAddress; pEncOutBuffer.pTableBufferSize[0] = 0; M4OSA_TRACE2_0("N **** blend AUs"); #ifdef M4VSS_SUPPORT_OMX_CODECS /*OMX Audio decoder used. * OMX Audio dec shell does internal buffering and hence does not return a PCM buffer for every decodeStep call.* * So PCM buffer sizes might be 0. In this case donot call encode Step*/ if( 0 != pEncInBuffer.pTableBufferSize[0] ) { #endif /** * Encode the PCM audio */ err = pC->ShellAPI.pAudioEncoderGlobalFcts->pFctStep( pC->ewc.pAudioEncCtxt, &pEncInBuffer, &pEncOutBuffer); if( ( M4NO_ERROR != err) && (M4WAR_NO_MORE_AU != err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio():\ pAudioEncoderGlobalFcts->pFctStep returns 0x%x", err); return err; } #ifdef M4VSS_SUPPORT_OMX_CODECS } #endif /** * Set AU cts and size */ pC->ewc.WriterAudioAU.size = pEncOutBuffer.pTableBufferSize[ 0]; /**< Get the size of encoded data */ pC->ewc.WriterAudioAU.CTS += frameTimeDelta; M4OSA_TRACE2_2("O ---- write : cts = %ld [ 0x%x ]", (M4OSA_Int32)(pC->ewc.WriterAudioAU.CTS / pC->ewc.scale_audio), pC->ewc.WriterAudioAU.size); /** * Write the AU */ err = pC->ShellAPI.pWriterDataFcts->pProcessAU( pC->ewc.p3gpWriterContext, M4VSS3GPP_WRITER_AUDIO_STREAM_ID, &pC->ewc.WriterAudioAU); if( M4NO_ERROR != err ) { /*11/12/2008 CR 3283 MMS use case for VideoArtist the warning M4WAR_WRITER_STOP_REQ is returned when the targeted output file size is reached The editing is then finished,the warning M4VSS3GPP_WAR_EDITING_DONE is returned*/ if( M4WAR_WRITER_STOP_REQ == err ) { M4OSA_TRACE1_0( "M4VSS3GPP_intEditStepAudio: File was cut to avoid oversize"); return M4VSS3GPP_WAR_EDITING_DONE; } else { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION:\ pWriterDataFcts->pProcessAU returns 0x%x!", err); return err; } } /** * Read the next audio frame */ err = M4VSS3GPP_intClipReadNextAudioFrame(pC->pC1); M4OSA_TRACE2_3("P .... read : cts = %.0f + %.0f [ 0x%x ]", pC->pC1->iAudioFrameCts / pC->pC1->scale_audio, pC->pC1->iAoffset / pC->pC1->scale_audio, pC->pC1->uiAudioFrameSize); if( M4OSA_ERR_IS_ERROR(err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION:\ M4VSS3GPP_intClipReadNextAudioFrame(C1) returns 0x%x!", err); return err; } else { M4OSA_ERR secondaryError; /** * Update current time (to=tc+T) */ pC->ewc.dATo = ( pC->pC1->iAudioFrameCts + pC->pC1->iAoffset) / pC->pC1->scale_audio; /** * Read the next audio frame in the second clip */ secondaryError = M4VSS3GPP_intClipReadNextAudioFrame(pC->pC2); M4OSA_TRACE2_3("Q .... read : cts = %.0f + %.0f [ 0x%x ]", pC->pC2->iAudioFrameCts / pC->pC2->scale_audio, pC->pC2->iAoffset / pC->pC2->scale_audio, pC->pC2->uiAudioFrameSize); if( M4OSA_ERR_IS_ERROR(secondaryError) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: TRANSITION:\ M4VSS3GPP_intClipReadNextAudioFrame(C2) returns 0x%x!", secondaryError); return err; } if( ( ( M4WAR_NO_MORE_AU == err) || (M4WAR_NO_MORE_AU == secondaryError)) && (M4OSA_FALSE == pC->bSupportSilence) ) { /** * If output is other than AMR or AAC (i.e. EVRC,we can't write silence into it) * So we simply end here.*/ bStopAudio = M4OSA_TRUE; } } } break; /* ____________ */ /*| |*/ /*| ERROR CASE |*/ /*|____________|*/ default: M4OSA_TRACE3_1( "M4VSS3GPP_intEditStepAudio: invalid internal state (0x%x), \ returning M4VSS3GPP_ERR_INTERNAL_STATE", pC->Astate); return M4VSS3GPP_ERR_INTERNAL_STATE; } /** * Check if we are forced to stop audio */ if( M4OSA_TRUE == bStopAudio ) { /** * Audio is done for this clip */ err = M4VSS3GPP_intReachedEndOfAudio(pC); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intEditStepAudio: M4VSS3GPP_intReachedEndOfAudio returns 0x%x", err); return err; } } /** * Return with no error */ M4OSA_TRACE3_0("M4VSS3GPP_intEditStepAudio: returning M4NO_ERROR"); return M4NO_ERROR; } /** ****************************************************************************** * M4OSA_ERR M4VSS3GPP_intCheckAudioMode() * @brief Check which audio process mode we must use, depending on the output CTS. * @param pC (IN/OUT) Internal edit context ****************************************************************************** */ static M4OSA_ERR M4VSS3GPP_intCheckAudioMode( M4VSS3GPP_InternalEditContext *pC ) { M4OSA_ERR err; const M4OSA_Int32 TD = pC->pTransitionList[pC-> uiCurrentClip].uiTransitionDuration; /**< Transition duration */ const M4VSS3GPP_EditAudioState previousAstate = pC->Astate; /** * Check if Clip1 is on its begin cut, or in its begin effect or end effect zone */ M4VSS3GPP_intCheckAudioEffects(pC, 1); /** * Check if we are in the transition with next clip */ if( ( TD > 0) && ((M4OSA_Int32)(pC->ewc.dATo - pC->pC1->iAoffset / pC->pC1->scale_audio + 0.5) >= (pC->pC1->iEndTime - TD)) ) { /** * We are in a transition */ pC->Astate = M4VSS3GPP_kEditAudioState_TRANSITION; pC->bTransitionEffect = M4OSA_TRUE; /** * Do we enter the transition section ? */ if( M4VSS3GPP_kEditAudioState_TRANSITION != previousAstate ) { /** * Open second clip for transition, if not yet opened */ if( M4OSA_NULL == pC->pC2 ) { err = M4VSS3GPP_intOpenClip(pC, &pC->pC2, &pC->pClipList[pC->uiCurrentClip + 1]); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intCheckAudioMode: M4VSS3GPP_intOpenClip() returns 0x%x!", err); return err; } /** * In case of short transition and bad luck (...), there may be no video AU * in the transition. In that case, the second clip has not been opened. * So we must update the video offset here. */ // Decorrelate input and output encoding timestamp to handle encoder prefetch /**< Add current video output CTS to the clip offset */ pC->pC2->iVoffset += (M4OSA_UInt32)pC->ewc.dInputVidCts; } /** * Add current audio output CTS to the clip offset * (video offset has already been set when doing the video transition) */ pC->pC2->iAoffset += (M4OSA_UInt32)(pC->ewc.dATo * pC->ewc.scale_audio + 0.5); /** * 2005-03-24: BugFix for audio-video synchro: * There may be a portion of the duration of an audio AU of desynchro at each assembly. * It leads to an audible desynchro when there are a lot of clips assembled. * This bug fix allows to resynch the audio track when the delta is higher * than one audio AU duration. * We Step one AU in the second clip and we change the audio offset accordingly. */ if( ( pC->pC2->iAoffset - (M4OSA_Int32)(pC->pC2->iVoffset *pC->pC2->scale_audio + 0.5)) > pC->ewc.iSilenceFrameDuration ) { /** * Advance one AMR frame */ err = M4VSS3GPP_intClipReadNextAudioFrame(pC->pC2); M4OSA_TRACE2_3("Z .... read : cts = %.0f + %.0f [ 0x%x ]", pC->pC2->iAudioFrameCts / pC->pC2->scale_audio, pC->pC2->iAoffset / pC->pC2->scale_audio, pC->pC2->uiAudioFrameSize); if( M4OSA_ERR_IS_ERROR(err) ) { M4OSA_TRACE1_1( "M4VSS3GPP_intCheckAudioMode:\ M4VSS3GPP_intClipReadNextAudioFrame returns 0x%x!", err); return err; } /** * Update audio offset accordingly*/ pC->pC2->iAoffset -= pC->ewc.iSilenceFrameDuration; } } /** * Check begin and end effects for clip2 */ M4VSS3GPP_intCheckAudioEffects(pC, 2); } else { /** * We are not in a transition */ pC->bTransitionEffect = M4OSA_FALSE; /** * Check if current mode is Read/Write or Decode/Encode */ if( pC->iClip1ActiveEffect >= 0 ) { pC->Astate = M4VSS3GPP_kEditAudioState_DECODE_ENCODE; } else { pC->Astate = M4VSS3GPP_kEditAudioState_READ_WRITE; } } /** * Check if we create/destroy an encoder */ if( ( M4VSS3GPP_kEditAudioState_READ_WRITE == previousAstate) && /**< read mode */ (M4VSS3GPP_kEditAudioState_READ_WRITE != pC->Astate) ) /**< encode mode */ { M4OSA_UInt32 uiAudioBitrate; /* Compute max bitrate depending on input files bitrates and transitions */ if( pC->Astate == M4VSS3GPP_kEditAudioState_TRANSITION ) { /* Max of the two blended files */ if( pC->pC1->pSettings->ClipProperties.uiAudioBitrate > pC->pC2->pSettings->ClipProperties.uiAudioBitrate ) uiAudioBitrate = pC->pC1->pSettings->ClipProperties.uiAudioBitrate; else uiAudioBitrate = pC->pC2->pSettings->ClipProperties.uiAudioBitrate; } else { /* Same as input file */ uiAudioBitrate = pC->pC1->pSettings->ClipProperties.uiAudioBitrate; } /** * Create the encoder */ err = M4VSS3GPP_intCreateAudioEncoder(&pC->ewc, &pC->ShellAPI, uiAudioBitrate); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intCheckAudioMode: M4VSS3GPP_intResetAudioEncoder() returns 0x%x!", err); return err; } } /** * Return with no error */ M4OSA_TRACE3_0("M4VSS3GPP_intCheckAudioMode(): returning M4NO_ERROR"); return M4NO_ERROR; } /** ****************************************************************************** * M4OSA_Void M4VSS3GPP_intCheckAudioEffects() * @brief Check which audio effect must be applied at the current time ****************************************************************************** */ static M4OSA_Void M4VSS3GPP_intCheckAudioEffects( M4VSS3GPP_InternalEditContext *pC, M4OSA_UInt8 uiClipNumber ) { M4OSA_UInt8 uiClipIndex; M4OSA_UInt8 uiFxIndex; M4VSS3GPP_ClipContext *pClip; M4VSS3GPP_EffectSettings *pFx; M4OSA_Int32 BC, EC; M4OSA_Int8 *piClipActiveEffect; M4OSA_Int32 t; if( 1 == uiClipNumber ) { uiClipIndex = pC->uiCurrentClip; pClip = pC->pC1; piClipActiveEffect = &(pC->iClip1ActiveEffect); } else /**< (2 == uiClipNumber) */ { uiClipIndex = pC->uiCurrentClip + 1; pClip = pC->pC2; piClipActiveEffect = &(pC->iClip2ActiveEffect); } /** * Shortcuts for code readability */ BC = pClip->iActualAudioBeginCut; EC = pClip->iEndTime; /** Change the absolut time to clip related time RC t = (M4OSA_Int32)(pC->ewc.dATo - pClip->iAoffset/pClip->scale_audio + 0.5); < rounding */; t = (M4OSA_Int32)(pC->ewc.dATo/*- pClip->iAoffset/pClip->scale_audio*/ + 0.5); /**< rounding */ ; /** * Default: no effect active */ *piClipActiveEffect = -1; /** * Check the three effects */ // RC for (uiFxIndex=0; uiFxIndex<pC->pClipList[uiClipIndex].nbEffects; uiFxIndex++) for ( uiFxIndex = 0; uiFxIndex < pC->nbEffects; uiFxIndex++ ) { /** Shortcut, reverse order because of priority between effects ( EndEffect always clean ) */ pFx = &(pC->pEffectsList[pC->nbEffects - 1 - uiFxIndex]); if( M4VSS3GPP_kAudioEffectType_None != pFx->AudioEffectType ) { /** * Check if there is actually a video effect */ if( ( t >= (M4OSA_Int32)(/*BC +*/pFx->uiStartTime)) && /**< Are we after the start time of the effect? */ (t < (M4OSA_Int32)(/*BC +*/pFx->uiStartTime + pFx-> uiDuration)) ) /**< Are we into the effect duration? */ { /** * Set the active effect */ *piClipActiveEffect = pC->nbEffects - 1 - uiFxIndex; /** * The first effect has the highest priority, then the second one, then the thirs one. * Hence, as soon as we found an active effect, we can get out of this loop */ uiFxIndex = pC->nbEffects; /** get out of the for loop */ } /** * Bugfix: The duration of the end effect has been set according to the announced clip duration. * If the announced duration is smaller than the real one, the end effect won't be applied at * the very end of the clip. To solve this issue we force the end effect. */ } } return; } /** ****************************************************************************** * M4OSA_ERR M4VSS3GPP_intApplyAudioEffect() * @brief Apply audio effect to pPCMdata * @param pC (IN/OUT) Internal edit context * @param uiClip1orClip2 (IN/OUT) 1 for first clip, 2 for second clip * @param pPCMdata (IN/OUT) Input and Output PCM audio data * @param uiPCMsize (IN) Size of pPCMdata * @return M4NO_ERROR: No error ****************************************************************************** */ static M4OSA_ERR M4VSS3GPP_intApplyAudioEffect( M4VSS3GPP_InternalEditContext *pC, M4OSA_UInt8 uiClip1orClip2, M4OSA_Int16 *pPCMdata, M4OSA_UInt32 uiPCMsize ) { M4VSS3GPP_ClipContext *pClip; M4VSS3GPP_ClipSettings *pClipSettings; M4VSS3GPP_EffectSettings *pFx; M4OSA_Int32 i32sample; /**< we will cast each Int16 sample into this Int32 variable */ M4OSA_Int32 iPos; M4OSA_Int32 iDur; M4OSA_DEBUG_IF2(( 1 != uiClip1orClip2) && (2 != uiClip1orClip2), M4ERR_PARAMETER, "M4VSS3GPP_intBeginAudioEffect: uiClip1orClip2 invalid"); if( 1 == uiClip1orClip2 ) { pClip = pC->pC1; pClipSettings = &(pC->pClipList[pC-> uiCurrentClip]); /**< Get a shortcut to the clip settings */ // RC pFx = &(pClipSettings->Effects[pC->iClip1ActiveEffect]);/**< Get a shortcut // to the active effect */ pFx = &(pC-> pEffectsList[pC-> iClip1ActiveEffect]); /**< Get a shortcut to the active effect */ M4OSA_DEBUG_IF2(( pC->iClip1ActiveEffect < 0) || (pC->iClip1ActiveEffect > 2), M4ERR_PARAMETER, "M4VSS3GPP_intApplyAudioEffect: iClip1ActiveEffect invalid"); } else /**< if (2==uiClip1orClip2) */ { pClip = pC->pC2; pClipSettings = &(pC->pClipList[pC->uiCurrentClip + 1]); /**< Get a shortcut to the clip settings */ // RC pFx = &(pClipSettings->Effects[pC->iClip2ActiveEffect]);/**< Get a shortcut // to the active effect */ pFx = &(pC-> pEffectsList[pC-> iClip2ActiveEffect]); /**< Get a shortcut to the active effect */ M4OSA_DEBUG_IF2(( pC->iClip2ActiveEffect < 0) || (pC->iClip2ActiveEffect > 2), M4ERR_PARAMETER, "M4VSS3GPP_intApplyAudioEffect: iClip2ActiveEffect invalid"); } iDur = (M4OSA_Int32)pFx->uiDuration; /** * Compute how far from the beginning of the effect we are, in clip-base time. * It is done with integers because the offset and begin cut have been rounded already. */ iPos = (M4OSA_Int32)(pC->ewc.dATo + 0.5 - pClip->iAoffset / pClip->scale_audio) - pClip->iActualAudioBeginCut - pFx->uiStartTime; /** * Sanity check */ if( iPos > iDur ) { iPos = iDur; } else if( iPos < 0 ) { iPos = 0; } /** * At this point, iPos is the effect progress, in a 0 to iDur base */ switch( pFx->AudioEffectType ) { case M4VSS3GPP_kAudioEffectType_FadeIn: /** * Original samples are signed 16bits. * We convert it to signed 32bits and multiply it by iPos. * So we must assure that iPos is not higher that 16bits max. * iPos max value is iDur, so we test iDur. */ while( iDur > PWR_FXP_FRACT_MAX ) { iDur >>= 2; /**< divide by 2 would be more logical (instead of 4), but we have enough dynamic..) */ iPos >>= 2; /**< idem */ } /** * From buffer size (bytes) to number of sample (int16): divide by two */ uiPCMsize >>= 1; /** * Loop on samples */ while( uiPCMsize-- > 0 ) /**< decrementing to optimize */ { i32sample = *pPCMdata; i32sample *= iPos; i32sample /= iDur; *pPCMdata++ = (M4OSA_Int16)i32sample; } break; case M4VSS3GPP_kAudioEffectType_FadeOut: /** * switch from 0->Dur to Dur->0 in order to do fadeOUT instead of fadeIN */ iPos = iDur - iPos; /** * Original samples are signed 16bits. * We convert it to signed 32bits and multiply it by iPos. * So we must assure that iPos is not higher that 16bits max. * iPos max value is iDur, so we test iDur. */ while( iDur > PWR_FXP_FRACT_MAX ) { iDur >>= 2; /**< divide by 2 would be more logical (instead of 4), but we have enough dynamic..) */ iPos >>= 2; /**< idem */ } /** * From buffer size (bytes) to number of sample (int16): divide by two */ uiPCMsize >>= 1; /** * Loop on samples, apply the fade factor on each */ while( uiPCMsize-- > 0 ) /**< decrementing counter to optimize */ { i32sample = *pPCMdata; i32sample *= iPos; i32sample /= iDur; *pPCMdata++ = (M4OSA_Int16)i32sample; } break; default: M4OSA_TRACE1_1( "M4VSS3GPP_intApplyAudioEffect: unknown audio effect type (0x%x),\ returning M4VSS3GPP_ERR_INVALID_AUDIO_EFFECT_TYPE", pFx->AudioEffectType); return M4VSS3GPP_ERR_INVALID_AUDIO_EFFECT_TYPE; } /** * Return */ M4OSA_TRACE3_0("M4VSS3GPP_intApplyAudioEffect: returning M4NO_ERROR"); return M4NO_ERROR; } /** ****************************************************************************** * M4OSA_ERR M4VSS3GPP_intAudioTransition() * @brief Apply transition effect to two PCM buffer * @note The result of the transition is put in the first buffer. * I know it's not beautiful, but it fits my current needs, and it's efficient! * So why bother with a third output buffer? * @param pC (IN/OUT) Internal edit context * @param pPCMdata1 (IN/OUT) First input and Output PCM audio data * @param pPCMdata2 (IN) Second input PCM audio data * @param uiPCMsize (IN) Size of both PCM buffers * @return M4NO_ERROR: No error ****************************************************************************** */ static M4OSA_ERR M4VSS3GPP_intAudioTransition( M4VSS3GPP_InternalEditContext *pC, M4OSA_Int16 *pPCMdata1, M4OSA_Int16 *pPCMdata2, M4OSA_UInt32 uiPCMsize ) { M4OSA_Int32 i32sample1, i32sample2; /**< we will cast each Int16 sample into this Int32 variable */ M4OSA_Int32 iPos1, iPos2; M4OSA_Int32 iDur = (M4OSA_Int32)pC-> pTransitionList[pC->uiCurrentClip].uiTransitionDuration; /** * Compute how far from the end cut we are, in clip-base time. * It is done with integers because the offset and begin cut have been rounded already. */ iPos1 = pC->pC1->iEndTime - (M4OSA_Int32)(pC->ewc.dATo + 0.5 - pC->pC1->iAoffset / pC->pC1->scale_audio); /** * Sanity check */ if( iPos1 > iDur ) { iPos1 = iDur; } else if( iPos1 < 0 ) { iPos1 = 0; } /** * Position of second clip in the transition */ iPos2 = iDur - iPos1; /** * At this point, iPos2 is the transition progress, in a 0 to iDur base. * iPos1 is the transition progress, in a iDUr to 0 base. */ switch( pC->pTransitionList[pC->uiCurrentClip].AudioTransitionType ) { case M4VSS3GPP_kAudioTransitionType_CrossFade: /** * Original samples are signed 16bits. * We convert it to signed 32bits and multiply it by iPos. * So we must assure that iPos is not higher that 16bits max. * iPos max value is iDur, so we test iDur. */ while( iDur > PWR_FXP_FRACT_MAX ) { iDur >>= 2; /**< divide by 2 would be more logical (instead of 4), but we have enough dynamic..) */ iPos1 >>= 2; /**< idem */ iPos2 >>= 2; /**< idem */ } /** * From buffer size (bytes) to number of sample (int16): divide by two */ uiPCMsize >>= 1; /** * Loop on samples, apply the fade factor on each */ while( uiPCMsize-- > 0 ) /**< decrementing counter to optimize */ { i32sample1 = *pPCMdata1; /**< Get clip1 sample */ i32sample1 *= iPos1; /**< multiply by fade numerator */ i32sample1 /= iDur; /**< divide by fade denominator */ i32sample2 = *pPCMdata2; /**< Get clip2 sample */ i32sample2 *= iPos2; /**< multiply by fade numerator */ i32sample2 /= iDur; /**< divide by fade denominator */ *pPCMdata1++ = (M4OSA_Int16)(i32sample1 + i32sample2); /**< mix the two samples */ pPCMdata2++; /**< don't forget to increment the second buffer */ } break; case M4VSS3GPP_kAudioTransitionType_None: /** * This is a stupid-non optimized version of the None transition... * We copy the PCM frames */ if( iPos1 < (iDur >> 1) ) /**< second half of transition */ { /** * Copy the input PCM to the output buffer */ memcpy((void *)pPCMdata1, (void *)pPCMdata2, uiPCMsize); } /** * the output must be put in the first buffer. * For the first half of the non-transition it's already the case! * So we have nothing to do here... */ break; default: M4OSA_TRACE1_1( "M4VSS3GPP_intAudioTransition: unknown transition type (0x%x),\ returning M4VSS3GPP_ERR_INVALID_AUDIO_TRANSITION_TYPE", pC->pTransitionList[pC->uiCurrentClip].AudioTransitionType); return M4VSS3GPP_ERR_INVALID_AUDIO_TRANSITION_TYPE; } /** * Return */ M4OSA_TRACE3_0("M4VSS3GPP_intAudioTransition: returning M4NO_ERROR"); return M4NO_ERROR; } /** ****************************************************************************** * M4OSA_ERR M4VSS3GPP_intCreateAudioEncoder() * @brief Reset the audio encoder (Create it if needed) * @note ****************************************************************************** */ M4OSA_ERR M4VSS3GPP_intCreateAudioEncoder( M4VSS3GPP_EncodeWriteContext *pC_ewc, M4VSS3GPP_MediaAndCodecCtxt *pC_ShellAPI, M4OSA_UInt32 uiAudioBitrate ) { M4OSA_ERR err; /** * If an encoder already exist, we destroy it */ if( M4OSA_NULL != pC_ewc->pAudioEncCtxt ) { err = pC_ShellAPI->pAudioEncoderGlobalFcts->pFctClose( pC_ewc->pAudioEncCtxt); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intResetAudioEncoder: pAudioEncoderGlobalFcts->pFctClose returns 0x%x", err); /**< don't return, we still have stuff to free */ } err = pC_ShellAPI->pAudioEncoderGlobalFcts->pFctCleanUp( pC_ewc->pAudioEncCtxt); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intResetAudioEncoder:\ pAudioEncoderGlobalFcts->pFctCleanUp returns 0x%x", err); /**< don't return, we still have stuff to free */ } pC_ewc->pAudioEncCtxt = M4OSA_NULL; } /** * Creates a new encoder */ switch( pC_ewc->AudioStreamType ) { //EVRC // case M4SYS_kEVRC: // // err = M4VSS3GPP_setCurrentAudioEncoder(&pC->ShellAPI, // pC_ewc->AudioStreamType); // M4ERR_CHECK_RETURN(err); // // pC_ewc->AudioEncParams.Format = M4ENCODER_kEVRC; // pC_ewc->AudioEncParams.Frequency = M4ENCODER_k8000Hz; // pC_ewc->AudioEncParams.ChannelNum = M4ENCODER_kMono; // pC_ewc->AudioEncParams.Bitrate = M4VSS3GPP_EVRC_DEFAULT_BITRATE; // break; case M4SYS_kAMR: err = M4VSS3GPP_setCurrentAudioEncoder(pC_ShellAPI, pC_ewc->AudioStreamType); M4ERR_CHECK_RETURN(err); pC_ewc->AudioEncParams.Format = M4ENCODER_kAMRNB; pC_ewc->AudioEncParams.Frequency = M4ENCODER_k8000Hz; pC_ewc->AudioEncParams.ChannelNum = M4ENCODER_kMono; pC_ewc->AudioEncParams.Bitrate = M4VSS3GPP_AMR_DEFAULT_BITRATE; pC_ewc->AudioEncParams.SpecifParam.AmrSID = M4ENCODER_kAmrNoSID; break; case M4SYS_kAAC: err = M4VSS3GPP_setCurrentAudioEncoder(pC_ShellAPI, pC_ewc->AudioStreamType); M4ERR_CHECK_RETURN(err); pC_ewc->AudioEncParams.Format = M4ENCODER_kAAC; switch( pC_ewc->uiSamplingFrequency ) { case 8000: pC_ewc->AudioEncParams.Frequency = M4ENCODER_k8000Hz; break; case 16000: pC_ewc->AudioEncParams.Frequency = M4ENCODER_k16000Hz; break; case 22050: pC_ewc->AudioEncParams.Frequency = M4ENCODER_k22050Hz; break; case 24000: pC_ewc->AudioEncParams.Frequency = M4ENCODER_k24000Hz; break; case 32000: pC_ewc->AudioEncParams.Frequency = M4ENCODER_k32000Hz; break; case 44100: pC_ewc->AudioEncParams.Frequency = M4ENCODER_k44100Hz; break; case 48000: pC_ewc->AudioEncParams.Frequency = M4ENCODER_k48000Hz; break; default: M4OSA_TRACE1_1( "M4VSS3GPP_intCreateAudioEncoder: invalid input AAC sampling frequency\ (%d Hz), returning M4VSS3GPP_ERR_AUDIO_DECODER_INIT_FAILED", pC_ewc->uiSamplingFrequency); return M4VSS3GPP_ERR_AUDIO_DECODER_INIT_FAILED; } pC_ewc->AudioEncParams.ChannelNum = (pC_ewc->uiNbChannels == 1) ? M4ENCODER_kMono : M4ENCODER_kStereo; pC_ewc->AudioEncParams.SpecifParam.AacParam.Regulation = M4ENCODER_kAacRegulNone; //M4ENCODER_kAacBitReservoir /* unused */ pC_ewc->AudioEncParams.SpecifParam.AacParam.bIS = M4OSA_FALSE; pC_ewc->AudioEncParams.SpecifParam.AacParam.bMS = M4OSA_FALSE; pC_ewc->AudioEncParams.SpecifParam.AacParam.bPNS = M4OSA_FALSE; pC_ewc->AudioEncParams.SpecifParam.AacParam.bTNS = M4OSA_FALSE; /* TODO change into highspeed asap */ pC_ewc->AudioEncParams.SpecifParam.AacParam.bHighSpeed = M4OSA_FALSE; /* Quantify value (ceil one) */ if( uiAudioBitrate <= 16000 ) pC_ewc->AudioEncParams.Bitrate = 16000; else if( uiAudioBitrate <= 24000 ) pC_ewc->AudioEncParams.Bitrate = 24000; else if( uiAudioBitrate <= 32000 ) pC_ewc->AudioEncParams.Bitrate = 32000; else if( uiAudioBitrate <= 48000 ) pC_ewc->AudioEncParams.Bitrate = 48000; else if( uiAudioBitrate <= 64000 ) pC_ewc->AudioEncParams.Bitrate = 64000; else pC_ewc->AudioEncParams.Bitrate = 96000; /* Special requirement of our encoder */ if( ( pC_ewc->uiNbChannels == 2) && (pC_ewc->AudioEncParams.Bitrate < 32000) ) pC_ewc->AudioEncParams.Bitrate = 32000; break; default: M4OSA_TRACE1_1( "M4VSS3GPP_intResetAudioEncoder: Undefined output audio format (%d),\ returning M4VSS3GPP_ERR_EDITING_UNSUPPORTED_AUDIO_FORMAT", pC_ewc->AudioStreamType); return M4VSS3GPP_ERR_EDITING_UNSUPPORTED_AUDIO_FORMAT; } /* Initialise the audio encoder */ #ifdef M4VSS_SUPPORT_OMX_CODECS M4OSA_TRACE3_1( "M4VSS3GPP_intResetAudioEncoder:\ pAudioEncoderGlobalFcts->pFctInit called with userdata 0x%x", pC_ShellAPI->pCurrentAudioEncoderUserData); err = pC_ShellAPI->pAudioEncoderGlobalFcts->pFctInit(&pC_ewc->pAudioEncCtxt, pC_ShellAPI->pCurrentAudioEncoderUserData); #else err = pC_ShellAPI->pAudioEncoderGlobalFcts->pFctInit(&pC_ewc->pAudioEncCtxt, M4OSA_NULL /* no HW encoder */); #endif if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intResetAudioEncoder: pAudioEncoderGlobalFcts->pFctInit returns 0x%x", err); return err; } /* Open the audio encoder */ err = pC_ShellAPI->pAudioEncoderGlobalFcts->pFctOpen(pC_ewc->pAudioEncCtxt, &pC_ewc->AudioEncParams, &pC_ewc->pAudioEncDSI, M4OSA_NULL /* no grabbing */); if( M4NO_ERROR != err ) { M4OSA_TRACE1_1( "M4VSS3GPP_intResetAudioEncoder: pAudioEncoderGlobalFcts->pFctOpen returns 0x%x", err); return err; } /** * Return with no error */ M4OSA_TRACE3_0("M4VSS3GPP_intResetAudioEncoder: returning M4NO_ERROR"); return M4NO_ERROR; }