OI_STATUS OI_CODEC_SBC_SkipFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, const OI_BYTE **frameData, OI_UINT32 *frameBytes) { OI_STATUS status; OI_UINT framelen; OI_UINT headerlen; OI_UINT8 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; }
/******************************************************************************* ** ** Function btc_a2dp_sink_handle_inc_media ** ** Description ** ** Returns void ** *******************************************************************************/ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg) { UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1); int count; UINT32 pcmBytes, availPcmBytes; OI_INT16 *pcmDataPointer = pcmData; /*Will be overwritten on next packet receipt*/ OI_STATUS status; int num_sbc_frames = p_msg->num_frames_to_be_processed; UINT32 sbc_frame_len = p_msg->len - 1; availPcmBytes = 2 * sizeof(pcmData); if (btc_av_get_peer_sep() == AVDT_TSEP_SNK || (btc_aa_snk_cb.rx_flush)) { APPL_TRACE_DEBUG(" State Changed happened in this tick "); return; } // ignore data if no one is listening if (!btc_a2dp_control_get_datachnl_stat()) { return; } APPL_TRACE_DEBUG("Number of sbc frames %d, frame_len %d\n", num_sbc_frames, sbc_frame_len); for (count = 0; count < num_sbc_frames && sbc_frame_len != 0; count ++) { pcmBytes = availPcmBytes; status = OI_CODEC_SBC_DecodeFrame(&context, (const OI_BYTE **)&sbc_start_frame, (OI_UINT32 *)&sbc_frame_len, (OI_INT16 *)pcmDataPointer, (OI_UINT32 *)&pcmBytes); if (!OI_SUCCESS(status)) { APPL_TRACE_ERROR("Decoding failure: %d\n", status); break; } availPcmBytes -= pcmBytes; pcmDataPointer += pcmBytes / 2; p_msg->offset += (p_msg->len - 1) - sbc_frame_len; p_msg->len = sbc_frame_len + 1; } btc_a2d_data_cb_to_app((uint8_t *)pcmData, (2 * sizeof(pcmData) - availPcmBytes)); }
PRIVATE OI_STATUS internal_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_UINT8 bitpool, const OI_BYTE **frameData, OI_UINT32 *frameBytes, OI_INT16 *pcmData, OI_UINT32 *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; }
INLINE OI_STATUS internal_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_UINT32 *decoderData, OI_UINT32 decoderDataBytes, OI_BYTE maxChannels, OI_BYTE pcmStride, OI_BOOL enhanced) { OI_UINT i; OI_STATUS status; for (i = 0; i < sizeof(*context); i++) { ((char *)context)[i] = 0; } #ifdef SBC_ENHANCED context->enhancedEnabled = enhanced ? TRUE : FALSE; #else context->enhancedEnabled = FALSE; if (enhanced){ return OI_STATUS_INVALID_PARAMETERS; } #endif status = OI_CODEC_SBC_Alloc(&context->common, decoderData, decoderDataBytes, maxChannels, pcmStride); if (!OI_SUCCESS(status)) { return status; } context->common.codecInfo = OI_Codec_Copyright; context->common.maxBitneed = 0; context->limitFrameFormat = FALSE; OI_SBC_ExpandFrameFields(&context->common.frameInfo); /*PLATFORM_DECODER_RESET(context);*/ return OI_OK; }
OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, const OI_BYTE **frameData, OI_UINT32 *frameBytes, OI_INT16 *pcmData, OI_UINT32 *pcmBytes) { OI_STATUS status; OI_UINT framelen; OI_UINT8 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; } #ifdef OI_DEBUG /* * 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; } #endif /* * 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; }
/******************************************************************************* ** ** Function btc_a2dp_sink_handle_decoder_reset ** ** Description ** ** Returns void ** *******************************************************************************/ static void btc_a2dp_sink_handle_decoder_reset(tBTC_MEDIA_SINK_CFG_UPDATE *p_msg) { tBTC_MEDIA_SINK_CFG_UPDATE *p_buf = p_msg; tA2D_STATUS a2d_status; tA2D_SBC_CIE sbc_cie; OI_STATUS status; UINT32 freq_multiple = 48 * 20; /* frequency multiple for 20ms of data , initialize with 48K*/ UINT32 num_blocks = 16; UINT32 num_subbands = 8; APPL_TRACE_EVENT("%s p_codec_info[%x:%x:%x:%x:%x:%x]\n", __FUNCTION__, p_buf->codec_info[1], p_buf->codec_info[2], p_buf->codec_info[3], p_buf->codec_info[4], p_buf->codec_info[5], p_buf->codec_info[6]); a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_buf->codec_info, FALSE); if (a2d_status != A2D_SUCCESS) { APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status); return; } btc_aa_snk_cb.sample_rate = btc_a2dp_sink_get_track_frequency(sbc_cie.samp_freq); btc_aa_snk_cb.channel_count = btc_a2dp_sink_get_track_channel_count(sbc_cie.ch_mode); btc_aa_snk_cb.rx_flush = FALSE; APPL_TRACE_EVENT("Reset to sink role"); status = OI_CODEC_SBC_DecoderReset(&context, contextData, sizeof(contextData), 2, 2, FALSE); if (!OI_SUCCESS(status)) { APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status); } btc_a2dp_control_set_datachnl_stat(TRUE); switch (sbc_cie.samp_freq) { case A2D_SBC_IE_SAMP_FREQ_16: APPL_TRACE_DEBUG("\tsamp_freq:%d (16000)\n", sbc_cie.samp_freq); freq_multiple = 16 * 20; break; case A2D_SBC_IE_SAMP_FREQ_32: APPL_TRACE_DEBUG("\tsamp_freq:%d (32000)\n", sbc_cie.samp_freq); freq_multiple = 32 * 20; break; case A2D_SBC_IE_SAMP_FREQ_44: APPL_TRACE_DEBUG("\tsamp_freq:%d (44100)\n", sbc_cie.samp_freq); freq_multiple = 441 * 2; break; case A2D_SBC_IE_SAMP_FREQ_48: APPL_TRACE_DEBUG("\tsamp_freq:%d (48000)\n", sbc_cie.samp_freq); freq_multiple = 48 * 20; break; default: APPL_TRACE_DEBUG(" Unknown Frequency "); break; } switch (sbc_cie.ch_mode) { case A2D_SBC_IE_CH_MD_MONO: APPL_TRACE_DEBUG("\tch_mode:%d (Mono)\n", sbc_cie.ch_mode); break; case A2D_SBC_IE_CH_MD_DUAL: APPL_TRACE_DEBUG("\tch_mode:%d (DUAL)\n", sbc_cie.ch_mode); break; case A2D_SBC_IE_CH_MD_STEREO: APPL_TRACE_DEBUG("\tch_mode:%d (STEREO)\n", sbc_cie.ch_mode); break; case A2D_SBC_IE_CH_MD_JOINT: APPL_TRACE_DEBUG("\tch_mode:%d (JOINT)\n", sbc_cie.ch_mode); break; default: APPL_TRACE_DEBUG(" Unknown Mode "); break; } switch (sbc_cie.block_len) { case A2D_SBC_IE_BLOCKS_4: APPL_TRACE_DEBUG("\tblock_len:%d (4)\n", sbc_cie.block_len); num_blocks = 4; break; case A2D_SBC_IE_BLOCKS_8: APPL_TRACE_DEBUG("\tblock_len:%d (8)\n", sbc_cie.block_len); num_blocks = 8; break; case A2D_SBC_IE_BLOCKS_12: APPL_TRACE_DEBUG("\tblock_len:%d (12)\n", sbc_cie.block_len); num_blocks = 12; break; case A2D_SBC_IE_BLOCKS_16: APPL_TRACE_DEBUG("\tblock_len:%d (16)\n", sbc_cie.block_len); num_blocks = 16; break; default: APPL_TRACE_DEBUG(" Unknown BlockLen "); break; } switch (sbc_cie.num_subbands) { case A2D_SBC_IE_SUBBAND_4: APPL_TRACE_DEBUG("\tnum_subbands:%d (4)\n", sbc_cie.num_subbands); num_subbands = 4; break; case A2D_SBC_IE_SUBBAND_8: APPL_TRACE_DEBUG("\tnum_subbands:%d (8)\n", sbc_cie.num_subbands); num_subbands = 8; break; default: APPL_TRACE_DEBUG(" Unknown SubBands "); break; } switch (sbc_cie.alloc_mthd) { case A2D_SBC_IE_ALLOC_MD_S: APPL_TRACE_DEBUG("\talloc_mthd:%d (SNR)\n", sbc_cie.alloc_mthd); break; case A2D_SBC_IE_ALLOC_MD_L: APPL_TRACE_DEBUG("\talloc_mthd:%d (Loudness)\n", sbc_cie.alloc_mthd); break; default: APPL_TRACE_DEBUG(" Unknown Allocation Method"); break; } APPL_TRACE_EVENT("\tBit pool Min:%d Max:%d\n", sbc_cie.min_bitpool, sbc_cie.max_bitpool); int frames_to_process = ((freq_multiple) / (num_blocks * num_subbands)) + 1; APPL_TRACE_EVENT(" Frames to be processed in 20 ms %d\n", frames_to_process); }