/****************************************************************************** * * Copyright 2014 The Android Open Source Project * Copyright 2006 Open Interface North America, Inc. All rights reserved. * * 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. * ******************************************************************************/ /******************************************************************************* $Revision: #1 $ ******************************************************************************/ /** @file @ingroup codec_internal */ /**@addtogroup codec_internal */ /**@{*/ #include "oi_bitstream.h" #include "oi_codec_sbc_private.h" #define SPECIALIZE_READ_SAMPLES_JOINT #if __has_attribute(fallthrough) #define __fallthrough __attribute__((__fallthrough__)) #else #define __fallthrough #endif /** * Scans through a buffer looking for a codec syncword. If the decoder has been * set for enhanced operation using OI_CODEC_SBC_DecoderReset(), it will search * for both a standard and an enhanced syncword. */ PRIVATE OI_STATUS FindSyncword(OI_CODEC_SBC_DECODER_CONTEXT* context, const OI_BYTE** frameData, uint32_t* frameBytes) { #ifdef SBC_ENHANCED OI_BYTE search1 = OI_SBC_SYNCWORD; OI_BYTE search2 = OI_SBC_ENHANCED_SYNCWORD; #endif // SBC_ENHANCED if (*frameBytes == 0) { return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; } #ifdef SBC_ENHANCED if (context->limitFrameFormat && context->enhancedEnabled) { /* If the context is restricted, only search for specified SYNCWORD */ search1 = search2; } else if (context->enhancedEnabled == FALSE) { /* If enhanced is not enabled, only search for classic SBC SYNCWORD*/ search2 = search1; } while (*frameBytes && (**frameData != search1) && (**frameData != search2)) { (*frameBytes)--; (*frameData)++; } if (*frameBytes) { /* Syncword found, *frameData points to it, and *frameBytes correctly * reflects the number of bytes available to read, including the * syncword. */ context->common.frameInfo.enhanced = (**frameData == OI_SBC_ENHANCED_SYNCWORD); return OI_OK; } else { /* No syncword was found anywhere in the provided input data. * *frameData points past the end of the original input, and * *frameBytes is 0. */ return OI_CODEC_SBC_NO_SYNCWORD; } #else // SBC_ENHANCED while (*frameBytes && (**frameData != OI_SBC_SYNCWORD)) { (*frameBytes)--; (*frameData)++; } if (*frameBytes) { /* Syncword found, *frameData points to it, and *frameBytes correctly * reflects the number of bytes available to read, including the * syncword. */ context->common.frameInfo.enhanced = FALSE; return OI_OK; } else { /* No syncword was found anywhere in the provided input data. * *frameData points past the end of the original input, and * *frameBytes is 0. */ return OI_CODEC_SBC_NO_SYNCWORD; } #endif // SBC_ENHANCED } static OI_STATUS DecodeBody(OI_CODEC_SBC_DECODER_CONTEXT* context, const OI_BYTE* bodyData, int16_t* pcmData, uint32_t* pcmBytes, OI_BOOL allowPartial) { OI_BITSTREAM bs; OI_UINT frameSamples = context->common.frameInfo.nrof_blocks * context->common.frameInfo.nrof_subbands; OI_UINT decode_block_count; /* * Based on the header data, make sure that there is enough room to write the * output samples. */ if (*pcmBytes < (sizeof(int16_t) * frameSamples * context->common.pcmStride) && !allowPartial) { /* If we're not allowing partial decodes, we need room for the entire * codec frame */ TRACE(("-OI_CODEC_SBC_Decode: OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA")); return OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA; } else if (*pcmBytes < sizeof(int16_t) * context->common.frameInfo.nrof_subbands * context->common.pcmStride) { /* Even if we're allowing partials, we can still only decode on a frame * boundary */ return OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA; } if (context->bufferedBlocks == 0) { TRACE(("Reading scalefactors")); OI_SBC_ReadScalefactors(&context->common, bodyData, &bs); TRACE(("Computing bit allocation")); OI_SBC_ComputeBitAllocation(&context->common); TRACE(("Reading samples")); if (context->common.frameInfo.mode == SBC_JOINT_STEREO) { OI_SBC_ReadSamplesJoint(context, &bs); } else { OI_SBC_ReadSamples(context, &bs); } context->bufferedBlocks = context->common.frameInfo.nrof_blocks; } if (allowPartial) { decode_block_count = *pcmBytes / sizeof(int16_t) / context->common.pcmStride / context->common.frameInfo.nrof_subbands; if (decode_block_count > context->bufferedBlocks) { decode_block_count = context->bufferedBlocks; } } else { decode_block_count = context->common.frameInfo.nrof_blocks; } TRACE(("Synthesizing frame")); { OI_UINT start_block = context->common.frameInfo.nrof_blocks - context->bufferedBlocks; OI_SBC_SynthFrame(context, pcmData, start_block, decode_block_count); } OI_ASSERT(context->bufferedBlocks >= decode_block_count); context->bufferedBlocks -= decode_block_count; frameSamples = decode_block_count * context->common.frameInfo.nrof_subbands; /* * When decoding mono into a stride-2 array, copy pcm data to second channel */ if (context->common.frameInfo.nrof_channels == 1 && context->common.pcmStride == 2) { OI_UINT i; for (i = 0; i < frameSamples; ++i) { pcmData[2 * i + 1] = pcmData[2 * i]; } } /* * Return number of pcm bytes generated by the decode operation. */ *pcmBytes = frameSamples * sizeof(int16_t) * context->common.pcmStride; if (context->bufferedBlocks > 0) { return OI_CODEC_SBC_PARTIAL_DECODE; } else { return OI_OK; } } PRIVATE OI_STATUS internal_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT* context, uint8_t bitpool, const OI_BYTE** frameData, uint32_t* frameBytes, int16_t* pcmData, uint32_t* pcmBytes) { OI_STATUS status; OI_UINT bodyLen; TRACE(("+OI_CODEC_SBC_DecodeRaw")); if (context->bufferedBlocks == 0) { /* * The bitallocator needs to know the bitpool value. */ context->common.frameInfo.bitpool = bitpool; /* * Compute the frame length and check we have enough frame data to proceed */ bodyLen = OI_CODEC_SBC_CalculateFramelen(&context->common.frameInfo) - SBC_HEADER_LEN; if (*frameBytes < bodyLen) { TRACE(("-OI_CODEC_SBC_Decode: OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA")); return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA; } } else { bodyLen = 0; } /* * Decode the SBC data. Pass TRUE to DecodeBody to allow partial decoding of * tones. */ status = DecodeBody(context, *frameData, pcmData, pcmBytes, TRUE); if (OI_SUCCESS(status) || status == OI_CODEC_SBC_PARTIAL_DECODE) { *frameData += bodyLen; *frameBytes -= bodyLen; } TRACE(("-OI_CODEC_SBC_DecodeRaw: %d", status)); return status; } OI_STATUS OI_CODEC_SBC_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT* context, uint32_t* decoderData, uint32_t decoderDataBytes, uint8_t maxChannels, uint8_t pcmStride, OI_BOOL enhanced) { return internal_DecoderReset(context, decoderData, decoderDataBytes, maxChannels, pcmStride, enhanced); } OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT* context, const OI_BYTE** frameData, uint32_t* frameBytes, int16_t* pcmData, uint32_t* pcmBytes) { OI_STATUS status; OI_UINT framelen; uint8_t crc; TRACE(("+OI_CODEC_SBC_DecodeFrame")); TRACE(("Finding syncword")); status = FindSyncword(context, frameData, frameBytes); if (!OI_SUCCESS(status)) { return status; } /* Make sure enough data remains to read the header. */ if (*frameBytes < SBC_HEADER_LEN) { TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA")); return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; } TRACE(("Reading Header")); OI_SBC_ReadHeader(&context->common, *frameData); /* * Some implementations load the decoder into RAM and use overlays for 4 vs 8 * subbands. We need * to ensure that the SBC parameters for this frame are compatible with the * restrictions imposed * by the loaded overlays. */ if (context->limitFrameFormat && (context->common.frameInfo.subbands != context->restrictSubbands)) { ERROR(("SBC parameters incompatible with loaded overlay")); return OI_STATUS_INVALID_PARAMETERS; } if (context->common.frameInfo.nrof_channels > context->common.maxChannels) { ERROR( ("SBC parameters incompatible with number of channels specified during " "reset")); return OI_STATUS_INVALID_PARAMETERS; } if (context->common.pcmStride < 1 || context->common.pcmStride > 2) { ERROR(("PCM stride not set correctly during reset")); return OI_STATUS_INVALID_PARAMETERS; } /* * At this point a header has been read. However, it's possible that we found * a false syncword, * so the header data might be invalid. Make sure we have enough bytes to read * in the * CRC-protected header, but don't require we have the whole frame. That way, * if it turns out * that we're acting on bogus header data, we don't stall the decoding process * by waiting for * data that we don't actually need. */ framelen = OI_CODEC_SBC_CalculateFramelen(&context->common.frameInfo); if (*frameBytes < framelen) { TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA")); return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA; } TRACE(("Calculating checksum")); crc = OI_SBC_CalculateChecksum(&context->common.frameInfo, *frameData); if (crc != context->common.frameInfo.crc) { TRACE(("CRC Mismatch: calc=%02x read=%02x\n", crc, context->common.frameInfo.crc)); TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_CHECKSUM_MISMATCH")); return OI_CODEC_SBC_CHECKSUM_MISMATCH; } /* * Make sure the bitpool values are sane. */ if ((context->common.frameInfo.bitpool < SBC_MIN_BITPOOL) && !context->common.frameInfo.enhanced) { ERROR(("Bitpool too small: %d (must be >= 2)", context->common.frameInfo.bitpool)); return OI_STATUS_INVALID_PARAMETERS; } if (context->common.frameInfo.bitpool > OI_SBC_MaxBitpool(&context->common.frameInfo)) { ERROR(("Bitpool too large: %d (must be <= %ld)", context->common.frameInfo.bitpool, OI_SBC_MaxBitpool(&context->common.frameInfo))); return OI_STATUS_INVALID_PARAMETERS; } /* * Now decode the SBC data. Partial decode is not yet implemented for an SBC * stream, so pass FALSE to decode body to have it enforce the old rule that * you have to decode a whole packet at a time. */ status = DecodeBody(context, *frameData + SBC_HEADER_LEN, pcmData, pcmBytes, FALSE); if (OI_SUCCESS(status)) { *frameData += framelen; *frameBytes -= framelen; } TRACE(("-OI_CODEC_SBC_DecodeFrame: %d", status)); return status; } OI_STATUS OI_CODEC_SBC_SkipFrame(OI_CODEC_SBC_DECODER_CONTEXT* context, const OI_BYTE** frameData, uint32_t* frameBytes) { OI_STATUS status; OI_UINT framelen; OI_UINT headerlen; uint8_t crc; status = FindSyncword(context, frameData, frameBytes); if (!OI_SUCCESS(status)) { return status; } if (*frameBytes < SBC_HEADER_LEN) { return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; } OI_SBC_ReadHeader(&context->common, *frameData); framelen = OI_SBC_CalculateFrameAndHeaderlen(&context->common.frameInfo, &headerlen); if (*frameBytes < headerlen) { return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; } crc = OI_SBC_CalculateChecksum(&context->common.frameInfo, *frameData); if (crc != context->common.frameInfo.crc) { return OI_CODEC_SBC_CHECKSUM_MISMATCH; } if (*frameBytes < framelen) { return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA; } context->bufferedBlocks = 0; *frameData += framelen; *frameBytes -= framelen; return OI_OK; } uint8_t OI_CODEC_SBC_FrameCount(OI_BYTE* frameData, uint32_t frameBytes) { uint8_t mode; uint8_t blocks; uint8_t subbands; uint8_t frameCount = 0; OI_UINT frameLen; while (frameBytes) { while (frameBytes && ((frameData[0] & 0xFE) != 0x9C)) { frameData++; frameBytes--; } if (frameBytes < SBC_HEADER_LEN) { return frameCount; } /* Extract and translate required fields from Header */ subbands = mode = blocks = frameData[1]; ; mode = (mode & (BIT3 | BIT2)) >> 2; blocks = block_values[(blocks & (BIT5 | BIT4)) >> 4]; subbands = band_values[(subbands & BIT0)]; /* Inline logic to avoid corrupting context */ frameLen = blocks * frameData[2]; switch (mode) { case SBC_JOINT_STEREO: frameLen += subbands + (8 * subbands); break; case SBC_DUAL_CHANNEL: frameLen *= 2; __fallthrough; default: if (mode == SBC_MONO) { frameLen += 4 * subbands; } else { frameLen += 8 * subbands; } } frameCount++; frameLen = SBC_HEADER_LEN + (frameLen + 7) / 8; if (frameBytes > frameLen) { frameBytes -= frameLen; frameData += frameLen; } else { frameBytes = 0; } } return frameCount; } /** Read quantized subband samples from the input bitstream and expand them. */ #ifdef SPECIALIZE_READ_SAMPLES_JOINT PRIVATE void OI_SBC_ReadSamplesJoint4(OI_CODEC_SBC_DECODER_CONTEXT* context, OI_BITSTREAM* global_bs) { #define NROF_SUBBANDS 4 #include "readsamplesjoint.inc" #undef NROF_SUBBANDS } PRIVATE void OI_SBC_ReadSamplesJoint8(OI_CODEC_SBC_DECODER_CONTEXT* context, OI_BITSTREAM* global_bs) { #define NROF_SUBBANDS 8 #include "readsamplesjoint.inc" #undef NROF_SUBBANDS } typedef void (*READ_SAMPLES)(OI_CODEC_SBC_DECODER_CONTEXT* context, OI_BITSTREAM* global_bs); static const READ_SAMPLES SpecializedReadSamples[] = {OI_SBC_ReadSamplesJoint4, OI_SBC_ReadSamplesJoint8}; #endif /* SPECIALIZE_READ_SAMPLES_JOINT */ PRIVATE void OI_SBC_ReadSamplesJoint(OI_CODEC_SBC_DECODER_CONTEXT* context, OI_BITSTREAM* global_bs) { OI_CODEC_SBC_COMMON_CONTEXT* common = &context->common; OI_UINT nrof_subbands = common->frameInfo.nrof_subbands; #ifdef SPECIALIZE_READ_SAMPLES_JOINT OI_ASSERT((nrof_subbands >> 3u) <= 1u); SpecializedReadSamples[nrof_subbands >> 3](context, global_bs); #else #define NROF_SUBBANDS nrof_subbands #include "readsamplesjoint.inc" #undef NROF_SUBBANDS #endif /* SPECIALIZE_READ_SAMPLES_JOINT */ } /**@}*/