blob: 7bd1bec38a0f4641a2e2a2fbcc534e3388ca2328 [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 2008 PacketVideo
*
* 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.
* -------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------
; INCLUDES
----------------------------------------------------------------------------*/
#include "amr_dec.h"
#include "frame.h"
#include "cnst.h"
#include "d_homing.h"
#define ETS_INPUT_FRAME_SIZE 500
//Compressed audio formats
#define PV_AMR_IETF 0
#define PV_AMR_IF2 1
#define PV_AMR_ETS 2
#define PV_AMR_IETF_COMBINED 3
//WB modes
#define PV_AMRWB_IETF_PAYLOAD 4
#define PV_AMRWB_IETF 5
// decoder returns -1 if there is an error in decoding a frame
#define PV_GSMAMR_DECODE_STATUS_ERR -1
// Currently, decoder returns a value >=0 if there is no error.(should be 0)
#define PV_GSMAMR_DECODE_STATUS_OK 0
// Find frame size for each frame type
static const OMX_S32 WBIETFFrameSize[16] =
{
18 // AMR-WB 6.60 Kbps
, 24 // AMR-WB 8.85 Kbps
, 33 // AMR-WB 12.65 Kbps
, 37 // AMR-WB 14.25 Kbps
, 41 // AMR-WB 15.85 Kbps
, 47 // AMR-WB 18.25 Kbps
, 51 // AMR-WB 19.85 Kbps
, 59 // AMR-WB 23.05 Kbps
, 61 // AMR-WB 23.85 Kbps
, 6 // AMR-WB SID
, 1
, 1
, 1
, 1 // WBAMR Frame No Data
, 1 // WBAMR Frame No Data
};
//////////////////////////////////////////////////////////////////////////////////
static const OMX_S32 IETFFrameSize[16] =
{
13 // AMR 4.75 Kbps
, 14 // AMR 5.15 Kbps
, 16 // AMR 5.90 Kbps
, 18 // AMR 6.70 Kbps
, 20 // AMR 7.40 Kbps
, 21 // AMR 7.95 Kbps
, 27 // AMR 10.2 Kbps
, 32 // AMR 12.2 Kbps
, 6 // GsmAmr comfort noise
, 7 // Gsm-Efr comfort noise
, 6 // IS-641 comfort noise
, 6 // Pdc-Efr comfort noise
, 1 // future use; 0 length but set to 1 to skip the frame type byte
, 1 // future use; 0 length but set to 1 to skip the frame type byte
, 1 // future use; 0 length but set to 1 to skip the frame type byte
, 1 // AMR Frame No Data
};
OmxAmrDecoder::OmxAmrDecoder()
{
iOmxInputFormat = PV_AMR_ETS;
iBytesProcessed = 0;
iAmrInitFlag = 0;
iNarrowBandFlag = OMX_TRUE;
//Output frame size in NB would be double the L_FRAME, due to char* output buffer in case of openmax instead of short* in console app
iOutputFrameSize = L_FRAME * 2;
iCodecExternals = NULL;
iAudioAmrDecoder = NULL;
}
/* Decoder Initialization function */
OMX_BOOL OmxAmrDecoder::AmrDecInit(OMX_AUDIO_AMRFRAMEFORMATTYPE aInFormat, OMX_AUDIO_AMRBANDMODETYPE aInMode)
{
OMX_S32 Status = 0;
iAmrInitFlag = 0;
if ((aInMode >= OMX_AUDIO_AMRBandModeNB0) && (aInMode <= OMX_AUDIO_AMRBandModeNB7))
{
iAudioAmrDecoder = CDecoder_AMR_NB::NewL();
if (!iAudioAmrDecoder)
{
return OMX_FALSE;
}
iNarrowBandFlag = OMX_TRUE;
iOutputFrameSize = L_FRAME * 2;
}
else if ((aInMode >= OMX_AUDIO_AMRBandModeWB0) && (aInMode <= OMX_AUDIO_AMRBandModeWB8))
{
iAudioAmrDecoder = CDecoder_AMR_WB::NewL();
if (!iAudioAmrDecoder)
{
return OMX_FALSE;
}
iNarrowBandFlag = OMX_FALSE;
iOutputFrameSize = L_FRAME * 4;
}
else
{
return OMX_FALSE;
}
if (!iCodecExternals)
{
iCodecExternals = OSCL_NEW(tPVAmrDecoderExternal, ());
if (!iCodecExternals)
{
return OMX_FALSE;
}
}
//initialize all fields to 0
oscl_memset(iCodecExternals, 0, sizeof(tPVAmrDecoderExternal));
iCodecExternals->quality = 1; // assume its always good data
//Extracting the input format information
if (OMX_AUDIO_AMRFrameFormatConformance == aInFormat)
{
iOmxInputFormat = PV_AMR_ETS;
iCodecExternals->input_format = ETS;
}
else if (OMX_AUDIO_AMRFrameFormatIF2 == aInFormat)
{
iOmxInputFormat = PV_AMR_IF2;
iCodecExternals->input_format = IF2;
}
else if (OMX_AUDIO_AMRFrameFormatRTPPayload == aInFormat)
{
if (OMX_TRUE == iNarrowBandFlag)
{
iOmxInputFormat = PV_AMR_IETF_COMBINED;
}
else
{
iOmxInputFormat = PV_AMRWB_IETF_PAYLOAD;
}
iCodecExternals->input_format = MIME_IETF;
}
else if (OMX_AUDIO_AMRFrameFormatFSF == aInFormat)
{
if (OMX_TRUE == iNarrowBandFlag)
{
iOmxInputFormat = PV_AMR_IETF;
}
else
{
iOmxInputFormat = PV_AMRWB_IETF;
}
iCodecExternals->input_format = MIME_IETF;
}
Status = iAudioAmrDecoder->StartL(iCodecExternals, false, false);
if (Status)
{
return OMX_FALSE;
}
return OMX_TRUE;
}
/* Decoder De-Initialization function */
void OmxAmrDecoder::AmrDecDeinit()
{
/* This function call is platform-specific */
if (iAudioAmrDecoder)
{
iAudioAmrDecoder->TerminateDecoderL();
OSCL_DELETE(iAudioAmrDecoder);
iAudioAmrDecoder = NULL;
if (iCodecExternals)
{
OSCL_DELETE(iCodecExternals);
iCodecExternals = NULL;
}
}
}
void OmxAmrDecoder::ResetDecoder()
{
if (iAudioAmrDecoder)
{
iAudioAmrDecoder->ResetDecoderL();
}
}
/* Find the start point & size of TOC table in case of IETF_Combined format */
void OmxAmrDecoder::GetStartPointsForIETFCombinedMode
(OMX_U8* aPtrIn, OMX_U32 aLength, OMX_U8* &aTocPtr, OMX_S32* aNumOfBytes)
{
OMX_U8 Fbit = 0x80;
OMX_U32 FrameCnt = 0;
/* Count number of frames */
aTocPtr = aPtrIn;
while ((*(aTocPtr + FrameCnt) & Fbit) && (FrameCnt < aLength))
{
FrameCnt++;
}
FrameCnt++;
*aNumOfBytes = FrameCnt;
}
/* Decode function for all the input formats */
OMX_BOOL OmxAmrDecoder::AmrDecodeFrame(OMX_S16* aOutputBuffer,
OMX_U32* aOutputLength, OMX_U8** aInBuffer,
OMX_U32* aInBufSize, OMX_S32* aIsFirstBuffer,
OMX_BOOL* aResizeFlag)
{
/* Initialize decoder homing flags */
static OMX_S16 DecHomingFlag = 0; /* Decoder currently not homed */
static OMX_S16 DecHomingFlagOld = 1; /* Decoder was previously homed */
OMX_BOOL Status = OMX_TRUE;
*aResizeFlag = OMX_FALSE;
OMX_S32 ByteOffset, ii;
TXFrameType TxFrame;
/* 3GPP Frame Type Buffer */
Frame_Type_3GPP FrameType3gpp;
OMX_U8* pTocPtr;
/* Takes care of extra bytes above the decoded ones
* e.g. toc length for ietf_combined, frame header length &
* one frame type byte for ietf format.
*/
OMX_S32 FrameBytesProcessed = 0, FrameLength;
/* Reset speech_bits buffer pointer */
OMX_U8* pSpeechBits = *aInBuffer;
//ETS mode requires a 16-bit pointer
OMX_S16* pEtsSpeechBits = (OMX_S16*) * aInBuffer;
if ((PV_AMR_IETF_COMBINED == iOmxInputFormat) || (PV_AMR_IETF == iOmxInputFormat)
|| (PV_AMRWB_IETF_PAYLOAD == iOmxInputFormat) || (PV_AMRWB_IETF == iOmxInputFormat))
{
if ((PV_AMR_IETF_COMBINED == iOmxInputFormat) || (PV_AMRWB_IETF_PAYLOAD == iOmxInputFormat))
{
if (0 == iBytesProcessed)
{
pTocPtr = NULL;
GetStartPointsForIETFCombinedMode(pSpeechBits, *aInBufSize,
pTocPtr, &iBytesProcessed);
pSpeechBits += iBytesProcessed;
FrameBytesProcessed = iBytesProcessed;
oscl_memcpy(iTocTable, pTocPtr, iBytesProcessed);
iBytesProcessed--;
}
else
{
iBytesProcessed--;
FrameBytesProcessed = 0;
}
FrameType3gpp = GetFrameTypeLength(iTocTable, &FrameLength);
}
else //iOmxInputFormat == PV_AMR_IETF or (PV_AMRWB_IETF == iOmxInputFormat)
{
if (0 == iAmrInitFlag)
{
if ('#' == pSpeechBits[0])
{
pSpeechBits += 6;
FrameBytesProcessed = 6;
}
iAmrInitFlag = 1;
}
FrameType3gpp = GetFrameTypeLength(pSpeechBits, &FrameLength);
}
/* Set up pointer to the start of frame to be decoded */
iCodecExternals->frame_type = (uint32)FrameType3gpp;
iCodecExternals->mode = (uint32)FrameType3gpp;
iCodecExternals->pInputBuffer = (uint8*) pSpeechBits;
iCodecExternals->pOutputBuffer = (int16*) aOutputBuffer;
ByteOffset = iAudioAmrDecoder->ExecuteL(iCodecExternals);
if (PV_GSMAMR_DECODE_STATUS_ERR == ByteOffset)
{
Status = OMX_FALSE;
printf("DEECODER RETURNED FALSE \n");
}
if ((FrameLength + FrameBytesProcessed) <= (OMX_S32) *aInBufSize)
{
*aInBufSize -= (FrameLength + FrameBytesProcessed);
*aInBuffer += (FrameLength + FrameBytesProcessed);
*aOutputLength = iOutputFrameSize;
}
else
{
*aInBufSize = 0;
*aOutputLength = 0;
}
}
else if (PV_AMR_IF2 == iOmxInputFormat)
{
FrameType3gpp = (Frame_Type_3GPP)(pSpeechBits[0] & 0xF);
/* Set up pointer to the start of frame to be decoded */
iCodecExternals->frame_type = (uint32)FrameType3gpp;
iCodecExternals->mode = (uint32)FrameType3gpp;
iCodecExternals->pInputBuffer = (uint8*) pSpeechBits;
iCodecExternals->pOutputBuffer = (int16*) aOutputBuffer;
ByteOffset = iAudioAmrDecoder->ExecuteL(iCodecExternals);
if (PV_GSMAMR_DECODE_STATUS_ERR == ByteOffset)
{
Status = OMX_FALSE;
}
//Frame type is a part of ByteOffset.
if (ByteOffset <= (OMX_S32) *aInBufSize)
{
*aInBufSize -= ByteOffset;
*aInBuffer += ByteOffset;
*aOutputLength = iOutputFrameSize;
}
/* EOS has arrived & the data is less than a full frame,
* discard that data so that EOS callback can be send.
* This is done to pass BufferFlagTest conformance */
else
{
*aInBufSize = 0;
*aOutputLength = 0;
}
}
else if (PV_AMR_ETS == iOmxInputFormat)
{
FrameType3gpp = (enum Frame_Type_3GPP) pSpeechBits[(1 + MAX_SERIAL_SIZE) * 2];
/* Get TX frame type */
TxFrame = (TXFrameType)pEtsSpeechBits[0];
/* Convert TX frame type to RX frame type */
switch (TxFrame)
{
case TX_SPEECH_GOOD:
pEtsSpeechBits[0] = RX_SPEECH_GOOD;
break;
case TX_SPEECH_DEGRADED:
pEtsSpeechBits[0] = RX_SPEECH_DEGRADED;
break;
case TX_SPEECH_BAD:
pEtsSpeechBits[0] = RX_SPEECH_BAD;
break;
case TX_SID_FIRST:
pEtsSpeechBits[0] = RX_SID_FIRST;
break;
case TX_SID_UPDATE:
pEtsSpeechBits[0] = RX_SID_UPDATE;
break;
case TX_SID_BAD:
pEtsSpeechBits[0] = RX_SID_BAD;
break;
case TX_ONSET:
pEtsSpeechBits[0] = RX_ONSET;
break;
case TX_NO_DATA:
pEtsSpeechBits[0] = RX_NO_DATA;
FrameType3gpp = (enum Frame_Type_3GPP) iCodecExternals->mode;
break;
default:
break;
}
/* if homed: check if this frame is another homing frame */
if (1 == DecHomingFlagOld)
{
/* only check until end of first subframe */
DecHomingFlag = decoder_homing_frame_test_first(
(OMX_S16*) & pEtsSpeechBits[1],
(enum Mode) FrameType3gpp);
}
/* produce encoder homing frame if homed & input=decoder homing frame */
if ((0 != DecHomingFlag) && (0 != DecHomingFlagOld))
{
for (ii = 0; ii < L_FRAME; ii++)
{
aOutputBuffer[ii] = EHF_MASK;
}
}
else
{
/* Set up pointer to the start of frame to be decoded */
iCodecExternals->frame_type = (uint32)FrameType3gpp;
iCodecExternals->mode = (uint32)FrameType3gpp;
iCodecExternals->pInputBuffer = (uint8*) pEtsSpeechBits;
iCodecExternals->pOutputBuffer = (int16*) aOutputBuffer;
ByteOffset = iAudioAmrDecoder->ExecuteL(iCodecExternals);
if (PV_GSMAMR_DECODE_STATUS_ERR == ByteOffset)
{
Status = OMX_FALSE;
}
}
/* if not homed: check whether current frame is a homing frame */
if (0 == DecHomingFlagOld)
{
/* check whole frame */
DecHomingFlag = decoder_homing_frame_test(
(OMX_S16*) & pEtsSpeechBits[1],
(enum Mode) FrameType3gpp);
}
/* reset decoder if current frame is a homing frame */
if (0 != DecHomingFlag)
{
iAudioAmrDecoder->ResetDecoderL();
}
DecHomingFlagOld = DecHomingFlag;
//Input buffer requirement per frame is constant at ETS_INPUT_FRAME_SIZE
*aInBufSize -= ETS_INPUT_FRAME_SIZE;
*aInBuffer += ETS_INPUT_FRAME_SIZE;
*aOutputLength = iOutputFrameSize;
}
(*aIsFirstBuffer)++;
//After decoding the first frame, modify all the input & output port settings
if (1 == *aIsFirstBuffer)
{
//Set the Resize flag to send the port settings changed callback
*aResizeFlag = OMX_TRUE;
}
return Status;
}
/* Decode function for all the input formats */
OMX_BOOL OmxAmrDecoder::AmrDecodeSilenceFrame(OMX_S16* aOutputBuffer,
OMX_U32* aOutputLength)
{
OMX_BOOL Status = OMX_TRUE;
OMX_S32 ByteOffset;
OMX_U8 FrameType = 15; // silence frame
iCodecExternals->mode = (uint32) FrameType;
iCodecExternals->frame_type = (uint32) FrameType;
iCodecExternals->pInputBuffer = (uint8*) & FrameType;
iCodecExternals->pOutputBuffer = (int16*) aOutputBuffer;
ByteOffset = iAudioAmrDecoder->ExecuteL(iCodecExternals);
if (PV_GSMAMR_DECODE_STATUS_ERR == ByteOffset)
{
Status = OMX_FALSE;
}
else
{
*aOutputLength = iOutputFrameSize;
}
return Status;
}
/* Get Frame type for format == PVMF_AMR_IETF or PVMF_AMR_IETF_COMBINED and the WB counterparts*/
Frame_Type_3GPP OmxAmrDecoder::GetFrameTypeLength(OMX_U8* aFrame, OMX_S32* aFrameLength)
{
Frame_Type_3GPP FrameType3gpp;
OMX_S32 ii;
FrameType3gpp = (Frame_Type_3GPP)((aFrame[0] >> 3) & 0x0F);
//Narrow Band AMR
if (OMX_TRUE == iNarrowBandFlag)
{
*aFrameLength = IETFFrameSize[FrameType3gpp];
}
else
{
*aFrameLength = WBIETFFrameSize[FrameType3gpp];
}
if (PV_AMR_IETF_COMBINED == iOmxInputFormat || PV_AMRWB_IETF_PAYLOAD == iOmxInputFormat)
{
aFrame++;
(*aFrameLength)--;
}
else
{
for (ii = 0; ii < *aFrameLength; ii++)
{
aFrame[ii] = aFrame[ii+1];
}
}
return (FrameType3gpp);
}