| /* |
| * 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; |
| } |