756 lines
24 KiB
C++
756 lines
24 KiB
C++
/***************************************************************************
|
|
*
|
|
* Copyright 2015-2019 BES.
|
|
* All rights reserved. All unpublished rights reserved.
|
|
*
|
|
* No part of this work may be used or reproduced in any form or by any
|
|
* means, or stored in a database or retrieval system, without prior written
|
|
* permission of BES.
|
|
*
|
|
* Use of this work is governed by a license granted by BES.
|
|
* This work contains confidential and proprietary information of
|
|
* BES. which is protected by copyright, trade secret,
|
|
* trademark and other intellectual property rights.
|
|
*
|
|
****************************************************************************/
|
|
// Standard C Included Files
|
|
#include "a2dp_decoder_internal.h"
|
|
#include "app_audio.h"
|
|
#include "cmsis.h"
|
|
#include "hal_location.h"
|
|
#include "heap_api.h"
|
|
#include "plat_types.h"
|
|
#include <string.h>
|
|
#if defined(A2DP_SCALABLE_ON)
|
|
#include "heap_api.h"
|
|
#include "ssc.h"
|
|
|
|
#define SCALABLE_MTU_LIMITER (32)
|
|
#define SCALABLE_MEMPOOL_SIZE 1024
|
|
|
|
#define SCALABLE_OUTPUT_FRAME_SAMPLES (SCALABLE_FRAME_SIZE)
|
|
|
|
typedef void *HANDLE_DECODER;
|
|
static A2DP_AUDIO_CONTEXT_T *a2dp_audio_context_p = NULL;
|
|
static A2DP_AUDIO_OUTPUT_CONFIG_T output_config;
|
|
|
|
static unsigned char *scalable_decoder_place = NULL;
|
|
static unsigned char *scalable_decoder_temp_buf = NULL;
|
|
static short ss_pcm_buff[SCALABLE_FRAME_SIZE * 4];
|
|
static int scalable_uhq_flag __attribute__((unused)) = 0;
|
|
|
|
static HANDLE_DECODER scalableDec_handle = NULL;
|
|
|
|
static A2DP_AUDIO_DECODER_LASTFRAME_INFO_T lastframe_info;
|
|
|
|
typedef struct {
|
|
uint16_t sequenceNumber;
|
|
uint32_t timestamp;
|
|
uint8_t *buffer;
|
|
int buffer_len;
|
|
} a2dp_audio_scalable_decoder_frame_t;
|
|
|
|
static void ss_to_24bit_buf(int32_t *out, int32_t *in, int size) {
|
|
for (int i = 0; i < size; i++) {
|
|
out[i] = in[i];
|
|
}
|
|
}
|
|
|
|
static void a2dp_audio_scalable_decoder_init(void) {
|
|
if (scalableDec_handle == NULL) {
|
|
scalableDec_handle = scalable_decoder_place;
|
|
|
|
ssc_decoder_init(scalableDec_handle, output_config.num_channels,
|
|
output_config.sample_rate);
|
|
}
|
|
}
|
|
|
|
static int scalableDecoder_Close(HANDLE_DECODER handle) {
|
|
if (handle) {
|
|
a2dp_audio_heap_free(handle);
|
|
a2dp_audio_heap_free(scalable_decoder_temp_buf);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void a2dp_audio_scalable_decoder_deinit(void) {
|
|
if (scalableDec_handle) {
|
|
scalableDecoder_Close(scalableDec_handle);
|
|
scalableDec_handle = NULL;
|
|
}
|
|
}
|
|
|
|
static void a2dp_audio_scalable_decoder_reinit(void) {
|
|
if (scalableDec_handle) {
|
|
a2dp_audio_scalable_decoder_deinit();
|
|
}
|
|
a2dp_audio_scalable_decoder_init();
|
|
}
|
|
|
|
static bool
|
|
is_valid_frame(a2dp_audio_scalable_decoder_frame_t *decoder_frame_p) {
|
|
int hw_tmp, len, bitrate_bps, frame_len, frame_size;
|
|
|
|
int sampling_rate = 44100;
|
|
unsigned char *input_buf = decoder_frame_p->buffer;
|
|
if (decoder_frame_p->buffer_len < SCALABLE_HEAD_SIZE) {
|
|
TRACE_A2DP_DECODER_E(
|
|
"invalid scalable a2dp frame, length < SCALABLE_HEAD_SIZE !!!!!!!");
|
|
return false;
|
|
}
|
|
|
|
scalable_uhq_flag = 0;
|
|
|
|
switch ((input_buf[3] & 0xf7)) {
|
|
case 0xF0:
|
|
bitrate_bps = 88000;
|
|
break;
|
|
case 0xF1:
|
|
bitrate_bps = 96000;
|
|
break;
|
|
case 0xF2:
|
|
bitrate_bps = 128000;
|
|
break;
|
|
case 0xF3:
|
|
bitrate_bps = 192000;
|
|
break;
|
|
case 0xF4:
|
|
bitrate_bps = 229000;
|
|
break;
|
|
case 0xF5:
|
|
scalable_uhq_flag = 1;
|
|
bitrate_bps = 328000;
|
|
sampling_rate = 96000;
|
|
break;
|
|
default:
|
|
bitrate_bps = 192000;
|
|
break;
|
|
}
|
|
|
|
frame_size = SCALABLE_FRAME_SIZE;
|
|
|
|
len = bitrate_bps * frame_size / sampling_rate / 8;
|
|
if (scalable_uhq_flag == 0) {
|
|
hw_tmp = (len * 3) >> 7;
|
|
len = hw_tmp + len;
|
|
len = len + ((len & 1) ^ 1);
|
|
} else {
|
|
len = 369; // 744/2-4+1
|
|
}
|
|
TRACE_A2DP_DECODER_D(
|
|
"scalable a2dp frame, length:%d bitrate:%d sampling_rate:%d",
|
|
decoder_frame_p->buffer_len, bitrate_bps, sampling_rate);
|
|
frame_len = SCALABLE_HEAD_SIZE + len - 1;
|
|
|
|
if (decoder_frame_p->buffer_len < frame_len) {
|
|
TRACE_A2DP_DECODER_E(
|
|
"invalid scalable a2dp frame, length:%d < %d !!!!!!!",
|
|
decoder_frame_p->buffer_len, frame_len);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
struct A2DP_CP_scalable_IN_FRM_INFO_T {
|
|
uint16_t sequenceNumber;
|
|
uint32_t timestamp;
|
|
};
|
|
|
|
struct A2DP_CP_scalable_OUT_FRM_INFO_T {
|
|
struct A2DP_CP_scalable_IN_FRM_INFO_T in_info;
|
|
uint16_t frame_samples;
|
|
uint16_t decoded_frames;
|
|
uint16_t frame_idx;
|
|
uint16_t pcm_len;
|
|
};
|
|
|
|
static bool cp_codec_reset;
|
|
extern uint32_t app_bt_stream_get_dma_buffer_samples(void);
|
|
|
|
TEXT_SSC_LOC static int a2dp_cp_scalable_mcu_decode(uint8_t *buffer,
|
|
uint32_t buffer_bytes) {
|
|
a2dp_audio_scalable_decoder_frame_t *decoder_frame_p = NULL;
|
|
list_node_t *node = NULL;
|
|
list_t *list = a2dp_audio_context_p->audio_datapath.input_raw_packet_list;
|
|
int ret, dec_ret;
|
|
struct A2DP_CP_scalable_IN_FRM_INFO_T in_info;
|
|
struct A2DP_CP_scalable_OUT_FRM_INFO_T *p_out_info;
|
|
uint8_t *out;
|
|
uint32_t out_len;
|
|
uint32_t out_frame_len;
|
|
uint32_t cp_buffer_frames_max = 0;
|
|
|
|
cp_buffer_frames_max = app_bt_stream_get_dma_buffer_samples() / 2;
|
|
if (cp_buffer_frames_max % (lastframe_info.frame_samples)) {
|
|
cp_buffer_frames_max =
|
|
cp_buffer_frames_max / (lastframe_info.frame_samples) + 1;
|
|
} else {
|
|
cp_buffer_frames_max =
|
|
cp_buffer_frames_max / (lastframe_info.frame_samples);
|
|
}
|
|
|
|
out_frame_len = sizeof(*p_out_info) + buffer_bytes;
|
|
ret = a2dp_cp_decoder_init(out_frame_len, cp_buffer_frames_max * 2);
|
|
|
|
ASSERT(ret == 0, "%s: a2dp_cp_decoder_init() failed: ret=%d", __func__, ret);
|
|
|
|
while ((node = a2dp_audio_list_begin(list)) != NULL) {
|
|
decoder_frame_p =
|
|
(a2dp_audio_scalable_decoder_frame_t *)a2dp_audio_list_node(node);
|
|
|
|
if (false == is_valid_frame(decoder_frame_p)) {
|
|
return A2DP_DECODER_DECODE_ERROR;
|
|
}
|
|
|
|
in_info.sequenceNumber = decoder_frame_p->sequenceNumber;
|
|
in_info.timestamp = decoder_frame_p->timestamp;
|
|
|
|
ret =
|
|
a2dp_cp_put_in_frame(&in_info, sizeof(in_info), decoder_frame_p->buffer,
|
|
decoder_frame_p->buffer_len);
|
|
if (ret) {
|
|
break;
|
|
}
|
|
|
|
a2dp_audio_list_remove(list, decoder_frame_p);
|
|
}
|
|
|
|
ret = a2dp_cp_get_full_out_frame((void **)&out, &out_len);
|
|
if (ret) {
|
|
return A2DP_DECODER_DECODE_ERROR;
|
|
}
|
|
if (out_len == 0) {
|
|
memset(buffer, 0, buffer_bytes);
|
|
a2dp_cp_consume_full_out_frame();
|
|
return A2DP_DECODER_NO_ERROR;
|
|
}
|
|
ASSERT(out_len == out_frame_len, "%s: Bad out len %u (should be %u)",
|
|
__func__, out_len, out_frame_len);
|
|
|
|
p_out_info = (struct A2DP_CP_scalable_OUT_FRM_INFO_T *)out;
|
|
if (p_out_info->pcm_len) {
|
|
lastframe_info.sequenceNumber = p_out_info->in_info.sequenceNumber;
|
|
lastframe_info.timestamp = p_out_info->in_info.timestamp;
|
|
lastframe_info.curSubSequenceNumber = 0;
|
|
lastframe_info.totalSubSequenceNumber = 0;
|
|
lastframe_info.frame_samples = p_out_info->frame_samples;
|
|
lastframe_info.decoded_frames += p_out_info->decoded_frames;
|
|
lastframe_info.undecode_frames =
|
|
a2dp_audio_list_length(list) +
|
|
a2dp_cp_get_in_frame_cnt_by_index(p_out_info->frame_idx) - 1;
|
|
a2dp_audio_decoder_internal_lastframe_info_set(&lastframe_info);
|
|
}
|
|
|
|
if (p_out_info->pcm_len == buffer_bytes) {
|
|
memcpy(buffer, p_out_info + 1, p_out_info->pcm_len);
|
|
dec_ret = A2DP_DECODER_NO_ERROR;
|
|
} else {
|
|
dec_ret = A2DP_DECODER_DECODE_ERROR;
|
|
}
|
|
|
|
ret = a2dp_cp_consume_full_out_frame();
|
|
ASSERT(ret == 0, "%s: a2dp_cp_consume_full_out_frame() failed: ret=%d",
|
|
__func__, ret);
|
|
|
|
return dec_ret;
|
|
}
|
|
|
|
TEXT_SSC_LOC int a2dp_cp_scalable_cp_decode(void) {
|
|
int ret;
|
|
enum CP_EMPTY_OUT_FRM_T out_frm_st;
|
|
uint8_t *out;
|
|
uint32_t out_len;
|
|
uint8_t *dec_start;
|
|
uint32_t dec_len;
|
|
struct A2DP_CP_scalable_IN_FRM_INFO_T *p_in_info;
|
|
struct A2DP_CP_scalable_OUT_FRM_INFO_T *p_out_info;
|
|
uint8_t *in_buf;
|
|
uint32_t in_len;
|
|
uint32_t dec_sum;
|
|
int error, output_samples = 0, output_byte = 0;
|
|
|
|
if (cp_codec_reset) {
|
|
cp_codec_reset = false;
|
|
a2dp_audio_scalable_decoder_init();
|
|
}
|
|
|
|
out_frm_st = a2dp_cp_get_emtpy_out_frame((void **)&out, &out_len);
|
|
|
|
if (out_frm_st != CP_EMPTY_OUT_FRM_OK &&
|
|
out_frm_st != CP_EMPTY_OUT_FRM_WORKING) {
|
|
return 1;
|
|
}
|
|
|
|
ASSERT(out_len > sizeof(*p_out_info), "%s: Bad out_len %u (should > %u)",
|
|
__func__, out_len, sizeof(*p_out_info));
|
|
|
|
p_out_info = (struct A2DP_CP_scalable_OUT_FRM_INFO_T *)out;
|
|
if (out_frm_st == CP_EMPTY_OUT_FRM_OK) {
|
|
p_out_info->pcm_len = 0;
|
|
p_out_info->decoded_frames = 0;
|
|
}
|
|
|
|
ASSERT(out_len > sizeof(*p_out_info) + p_out_info->pcm_len,
|
|
"%s: Bad out_len %u (should > %u + %u)", __func__, out_len,
|
|
sizeof(*p_out_info), p_out_info->pcm_len);
|
|
|
|
dec_start = (uint8_t *)(p_out_info + 1) + p_out_info->pcm_len;
|
|
dec_len = out_len - (dec_start - (uint8_t *)out);
|
|
if (!scalableDec_handle) {
|
|
TRACE(0, "scalable_decode not ready");
|
|
return 3;
|
|
}
|
|
|
|
dec_sum = 0;
|
|
error = 0;
|
|
|
|
while (dec_sum < dec_len && error == 0) {
|
|
ret = a2dp_cp_get_in_frame((void **)&in_buf, &in_len);
|
|
|
|
if (ret) {
|
|
break;
|
|
}
|
|
|
|
ASSERT(in_len > sizeof(*p_in_info), "%s: Bad in_len %u (should > %u)",
|
|
__func__, in_len, sizeof(*p_in_info));
|
|
p_in_info = (struct A2DP_CP_scalable_IN_FRM_INFO_T *)in_buf;
|
|
in_buf += sizeof(*p_in_info);
|
|
in_len -= sizeof(*p_in_info);
|
|
/* decode one SSC frame */
|
|
output_samples =
|
|
ssc_decode(scalableDec_handle, (const unsigned char *)in_buf,
|
|
ss_pcm_buff, SCALABLE_FRAME_SIZE, 0, 2);
|
|
if (0 == output_samples) {
|
|
a2dp_audio_scalable_decoder_reinit();
|
|
TRACE(0, "scalable_decode reinin codec \n");
|
|
error = A2DP_DECODER_DECODE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
ss_to_24bit_buf((int32_t *)(dec_start + dec_sum), (int32_t *)ss_pcm_buff,
|
|
output_samples);
|
|
output_byte = output_samples * 4;
|
|
dec_sum += output_byte;
|
|
exit:
|
|
memcpy(&p_out_info->in_info, ss_pcm_buff, sizeof(*p_in_info));
|
|
p_out_info->decoded_frames++;
|
|
p_out_info->frame_samples = SCALABLE_OUTPUT_FRAME_SAMPLES;
|
|
p_out_info->frame_idx = a2dp_cp_get_in_frame_index();
|
|
|
|
ret = a2dp_cp_consume_in_frame();
|
|
ASSERT(ret == 0, "%s: a2dp_cp_consume_in_frame() failed: ret=%d", __func__,
|
|
ret);
|
|
}
|
|
|
|
p_out_info->pcm_len += dec_sum;
|
|
|
|
if (error || out_len <= sizeof(*p_out_info) + p_out_info->pcm_len) {
|
|
ret = a2dp_cp_consume_emtpy_out_frame();
|
|
ASSERT(ret == 0, "%s: a2dp_cp_consume_emtpy_out_frame() failed: ret=%d",
|
|
__func__, ret);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
static int a2dp_audio_scalable_init(A2DP_AUDIO_OUTPUT_CONFIG_T *config,
|
|
void *context) {
|
|
TRACE_A2DP_DECODER_D("%s", __func__);
|
|
|
|
TRACE(0, "\n\nA2DP SSC-LC INIT\n");
|
|
|
|
a2dp_audio_context_p = (A2DP_AUDIO_CONTEXT_T *)context;
|
|
|
|
memcpy(&output_config, config, sizeof(A2DP_AUDIO_OUTPUT_CONFIG_T));
|
|
|
|
memset(&lastframe_info, 0, sizeof(A2DP_AUDIO_DECODER_LASTFRAME_INFO_T));
|
|
lastframe_info.stream_info = output_config;
|
|
lastframe_info.frame_samples = SCALABLE_FRAME_SIZE;
|
|
lastframe_info.list_samples = SCALABLE_FRAME_SIZE;
|
|
a2dp_audio_decoder_internal_lastframe_info_set(&lastframe_info);
|
|
|
|
ASSERT(a2dp_audio_context_p->dest_packet_mut < SCALABLE_MTU_LIMITER,
|
|
"%s MTU OVERFLOW:%u/%u", __func__,
|
|
a2dp_audio_context_p->dest_packet_mut, SCALABLE_MTU_LIMITER);
|
|
|
|
int decoder_size;
|
|
|
|
decoder_size = ssc_decoder_get_size(
|
|
output_config.num_channels); // todo: get size with codec capability
|
|
|
|
TRACE(0, "decoder size %d", decoder_size);
|
|
|
|
scalable_decoder_place =
|
|
(unsigned char *)a2dp_audio_heap_malloc(decoder_size);
|
|
ASSERT_A2DP_DECODER(scalable_decoder_place,
|
|
"no memory resource for scalable_decoder_place");
|
|
|
|
scalable_decoder_temp_buf =
|
|
(unsigned char *)a2dp_audio_heap_malloc(SCALABLE_FRAME_SIZE * 16);
|
|
ASSERT_A2DP_DECODER(scalable_decoder_temp_buf,
|
|
"no memory resource for scalable_decoder_temp_buf");
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
int ret;
|
|
cp_codec_reset = true;
|
|
ret = a2dp_cp_init(a2dp_cp_scalable_cp_decode, CP_PROC_DELAY_1_FRAME);
|
|
ASSERT(ret == 0, "%s: a2dp_cp_init() failed: ret=%d", __func__, ret);
|
|
#else
|
|
a2dp_audio_scalable_decoder_init();
|
|
#endif
|
|
|
|
return A2DP_DECODER_NO_ERROR;
|
|
}
|
|
|
|
static int a2dp_audio_scalable_deinit(void) {
|
|
#ifdef A2DP_CP_ACCEL
|
|
a2dp_cp_deinit();
|
|
#endif
|
|
|
|
a2dp_audio_scalable_decoder_deinit();
|
|
|
|
TRACE(0, "\n\nA2DP SCALABLE DEINIT\n");
|
|
|
|
return A2DP_DECODER_NO_ERROR;
|
|
}
|
|
|
|
int a2dp_audio_scalable_mcu_decode_frame(uint8_t *buffer,
|
|
uint32_t buffer_bytes) {
|
|
list_t *list = a2dp_audio_context_p->audio_datapath.input_raw_packet_list;
|
|
list_node_t *node = NULL;
|
|
a2dp_audio_scalable_decoder_frame_t *decoder_frame_p = NULL;
|
|
int ret = A2DP_DECODER_NO_ERROR;
|
|
|
|
bool cache_underflow = false;
|
|
int output_byte = 0, output_samples = 0;
|
|
uint8_t *output = buffer;
|
|
// TRACE(1,"bbd %d",buffer_bytes );
|
|
if (buffer_bytes < (SCALABLE_FRAME_SIZE * output_config.num_channels *
|
|
output_config.bits_depth / 8)) {
|
|
TRACE(1, "scalable_decode pcm_len = %d \n", buffer_bytes);
|
|
return A2DP_DECODER_NO_ERROR;
|
|
}
|
|
if (!scalableDec_handle) {
|
|
TRACE(0, "scalable_decode not ready");
|
|
return A2DP_DECODER_NO_ERROR;
|
|
}
|
|
|
|
while (output < buffer + buffer_bytes) {
|
|
node = a2dp_audio_list_begin(list);
|
|
if (!node) {
|
|
TRACE(0, "scalable_decode cache underflow");
|
|
cache_underflow = true;
|
|
goto exit;
|
|
} else {
|
|
decoder_frame_p =
|
|
(a2dp_audio_scalable_decoder_frame_t *)a2dp_audio_list_node(node);
|
|
|
|
if (false == is_valid_frame(decoder_frame_p)) {
|
|
TRACE_A2DP_DECODER_E("%s %d invalid a2dp frame", __func__, __LINE__);
|
|
ret = A2DP_DECODER_DECODE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
/* decode one SSC frame */
|
|
output_samples = ssc_decode(scalableDec_handle, decoder_frame_p->buffer,
|
|
ss_pcm_buff, SCALABLE_FRAME_SIZE, 0, 2);
|
|
|
|
TRACE_A2DP_DECODER_D("scalable_decode seq:%d len:%d output_samples:%d",
|
|
decoder_frame_p->sequenceNumber,
|
|
decoder_frame_p->buffer_len, output_samples);
|
|
|
|
if (0 == output_samples) {
|
|
TRACE(0, "scalable_decode failed !!!!!!");
|
|
// if failed reopen it again
|
|
a2dp_audio_scalable_decoder_reinit();
|
|
TRACE(0, "scalable_decode reinin codec \n");
|
|
ret = A2DP_DECODER_DECODE_ERROR;
|
|
goto exit;
|
|
}
|
|
ss_to_24bit_buf((int32_t *)output, (int32_t *)ss_pcm_buff,
|
|
output_samples * 2);
|
|
output_byte = output_samples * 8;
|
|
|
|
output += output_byte;
|
|
ASSERT(SCALABLE_FRAME_SIZE == output_samples,
|
|
"scalable_decode output mismatch samples:%d", output_samples);
|
|
lastframe_info.sequenceNumber = decoder_frame_p->sequenceNumber;
|
|
lastframe_info.timestamp = decoder_frame_p->timestamp;
|
|
lastframe_info.curSubSequenceNumber = 0;
|
|
lastframe_info.totalSubSequenceNumber = 0;
|
|
lastframe_info.frame_samples = SCALABLE_FRAME_SIZE;
|
|
lastframe_info.decoded_frames++;
|
|
lastframe_info.undecode_frames = a2dp_audio_list_length(list) - 1;
|
|
a2dp_audio_decoder_internal_lastframe_info_set(&lastframe_info);
|
|
a2dp_audio_list_remove(list, decoder_frame_p);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (cache_underflow) {
|
|
lastframe_info.undecode_frames = 0;
|
|
a2dp_audio_decoder_internal_lastframe_info_set(&lastframe_info);
|
|
ret = A2DP_DECODER_CACHE_UNDERFLOW_ERROR;
|
|
}
|
|
// TRACE(0,"abd");
|
|
return ret;
|
|
}
|
|
|
|
static int a2dp_audio_scalable_decode_frame(uint8_t *buffer,
|
|
uint32_t buffer_bytes) {
|
|
#ifdef A2DP_CP_ACCEL
|
|
return a2dp_cp_scalable_mcu_decode(buffer, buffer_bytes);
|
|
#else
|
|
return a2dp_audio_scalable_mcu_decode_frame(buffer, buffer_bytes);
|
|
#endif
|
|
}
|
|
|
|
static int a2dp_audio_scalable_preparse_packet(btif_media_header_t *header,
|
|
uint8_t *buffer,
|
|
uint32_t buffer_bytes) {
|
|
lastframe_info.sequenceNumber = header->sequenceNumber;
|
|
lastframe_info.timestamp = header->timestamp;
|
|
lastframe_info.curSubSequenceNumber = 0;
|
|
lastframe_info.totalSubSequenceNumber = 0;
|
|
lastframe_info.frame_samples = SCALABLE_FRAME_SIZE;
|
|
lastframe_info.list_samples = SCALABLE_FRAME_SIZE;
|
|
lastframe_info.decoded_frames = 0;
|
|
lastframe_info.undecode_frames = 0;
|
|
a2dp_audio_decoder_internal_lastframe_info_set(&lastframe_info);
|
|
|
|
TRACE(3, "%s seq:%d timestamp:%08x", __func__, header->sequenceNumber,
|
|
header->timestamp);
|
|
|
|
return A2DP_DECODER_NO_ERROR;
|
|
}
|
|
|
|
static void *a2dp_audio_scalable_frame_malloc(uint32_t packet_len) {
|
|
a2dp_audio_scalable_decoder_frame_t *decoder_frame_p = NULL;
|
|
uint8_t *buffer = NULL;
|
|
|
|
buffer = (uint8_t *)a2dp_audio_heap_malloc(SCALABLE_READBUF_SIZE);
|
|
decoder_frame_p =
|
|
(a2dp_audio_scalable_decoder_frame_t *)a2dp_audio_heap_malloc(
|
|
sizeof(a2dp_audio_scalable_decoder_frame_t));
|
|
decoder_frame_p->buffer = buffer;
|
|
decoder_frame_p->buffer_len = packet_len;
|
|
return (void *)decoder_frame_p;
|
|
}
|
|
|
|
static void a2dp_audio_scalable_free(void *packet) {
|
|
a2dp_audio_scalable_decoder_frame_t *decoder_frame_p =
|
|
(a2dp_audio_scalable_decoder_frame_t *)packet;
|
|
a2dp_audio_heap_free(decoder_frame_p->buffer);
|
|
a2dp_audio_heap_free(decoder_frame_p);
|
|
}
|
|
|
|
static int a2dp_audio_scalable_store_packet(btif_media_header_t *header,
|
|
uint8_t *buffer,
|
|
uint32_t buffer_bytes) {
|
|
list_t *list = a2dp_audio_context_p->audio_datapath.input_raw_packet_list;
|
|
int nRet = A2DP_DECODER_NO_ERROR;
|
|
if (a2dp_audio_list_length(list) < SCALABLE_MTU_LIMITER) {
|
|
a2dp_audio_scalable_decoder_frame_t *decoder_frame_p =
|
|
(a2dp_audio_scalable_decoder_frame_t *)a2dp_audio_scalable_frame_malloc(
|
|
buffer_bytes);
|
|
TRACE_A2DP_DECODER_D("%s seq:%d len:%d", __func__, header->sequenceNumber,
|
|
buffer_bytes);
|
|
decoder_frame_p->sequenceNumber = header->sequenceNumber;
|
|
decoder_frame_p->timestamp = header->timestamp;
|
|
memcpy(decoder_frame_p->buffer, buffer, buffer_bytes);
|
|
decoder_frame_p->buffer_len = buffer_bytes;
|
|
a2dp_audio_list_append(list, decoder_frame_p);
|
|
nRet = A2DP_DECODER_NO_ERROR;
|
|
} else {
|
|
TRACE(2, "%s list full current len:%d", __func__,
|
|
a2dp_audio_list_length(list));
|
|
nRet = A2DP_DECODER_MTU_LIMTER_ERROR;
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
static int a2dp_audio_scalable_discards_packet(uint32_t packets) {
|
|
int nRet = A2DP_DECODER_MEMORY_ERROR;
|
|
list_t *list = a2dp_audio_context_p->audio_datapath.input_raw_packet_list;
|
|
list_node_t *node = NULL;
|
|
a2dp_audio_scalable_decoder_frame_t *decoder_frame_p = NULL;
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
a2dp_cp_reset_frame();
|
|
#endif
|
|
|
|
if (packets <= a2dp_audio_list_length(list)) {
|
|
for (uint8_t i = 0; i < packets; i++) {
|
|
if ((node = a2dp_audio_list_begin(list)) != NULL) {
|
|
decoder_frame_p =
|
|
(a2dp_audio_scalable_decoder_frame_t *)a2dp_audio_list_node(node);
|
|
a2dp_audio_list_remove(list, decoder_frame_p);
|
|
}
|
|
}
|
|
nRet = A2DP_DECODER_NO_ERROR;
|
|
}
|
|
|
|
TRACE(3, "%s packets:%d nRet:%d", __func__, packets, nRet);
|
|
return nRet;
|
|
}
|
|
|
|
static int a2dp_audio_scalable_headframe_info_get(
|
|
A2DP_AUDIO_HEADFRAME_INFO_T *headframe_info) {
|
|
list_t *list = a2dp_audio_context_p->audio_datapath.input_raw_packet_list;
|
|
list_node_t *node = NULL;
|
|
a2dp_audio_scalable_decoder_frame_t *decoder_frame_p = NULL;
|
|
|
|
if (a2dp_audio_list_length(list)) {
|
|
if ((node = a2dp_audio_list_begin(list)) != NULL) {
|
|
decoder_frame_p =
|
|
(a2dp_audio_scalable_decoder_frame_t *)a2dp_audio_list_node(node);
|
|
headframe_info->sequenceNumber = decoder_frame_p->sequenceNumber;
|
|
headframe_info->timestamp = decoder_frame_p->timestamp;
|
|
headframe_info->curSubSequenceNumber = 0;
|
|
headframe_info->totalSubSequenceNumber = 0;
|
|
}
|
|
} else {
|
|
memset(headframe_info, 0, sizeof(A2DP_AUDIO_HEADFRAME_INFO_T));
|
|
}
|
|
|
|
return A2DP_DECODER_NO_ERROR;
|
|
}
|
|
|
|
static int a2dp_audio_scalable_info_get(void *info) {
|
|
return A2DP_DECODER_NO_ERROR;
|
|
}
|
|
|
|
static int
|
|
a2dp_audio_scalable_synchronize_packet(A2DP_AUDIO_SYNCFRAME_INFO_T *sync_info,
|
|
uint32_t mask) {
|
|
int nRet = A2DP_DECODER_SYNC_ERROR;
|
|
list_t *list = a2dp_audio_context_p->audio_datapath.input_raw_packet_list;
|
|
list_node_t *node = NULL;
|
|
int list_len;
|
|
a2dp_audio_scalable_decoder_frame_t *decoder_frame_p = NULL;
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
a2dp_cp_reset_frame();
|
|
#endif
|
|
|
|
list_len = a2dp_audio_list_length(list);
|
|
|
|
for (uint16_t i = 0; i < list_len; i++) {
|
|
if ((node = a2dp_audio_list_begin(list)) != NULL) {
|
|
decoder_frame_p =
|
|
(a2dp_audio_scalable_decoder_frame_t *)a2dp_audio_list_node(node);
|
|
if (A2DP_AUDIO_SYNCFRAME_CHK(decoder_frame_p->sequenceNumber ==
|
|
sync_info->sequenceNumber,
|
|
A2DP_AUDIO_SYNCFRAME_MASK_SEQ, mask) &&
|
|
A2DP_AUDIO_SYNCFRAME_CHK(decoder_frame_p->timestamp ==
|
|
sync_info->timestamp,
|
|
A2DP_AUDIO_SYNCFRAME_MASK_TIMESTAMP, mask)) {
|
|
nRet = A2DP_DECODER_NO_ERROR;
|
|
break;
|
|
}
|
|
a2dp_audio_list_remove(list, decoder_frame_p);
|
|
}
|
|
}
|
|
|
|
node = a2dp_audio_list_begin(list);
|
|
if (node) {
|
|
decoder_frame_p =
|
|
(a2dp_audio_scalable_decoder_frame_t *)a2dp_audio_list_node(node);
|
|
TRACE(4, "%s nRet:%d SEQ:%d timestamp:%d", __func__, nRet,
|
|
decoder_frame_p->sequenceNumber, decoder_frame_p->timestamp);
|
|
} else {
|
|
TRACE(2, "%s nRet:%d", __func__, nRet);
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
static int
|
|
a2dp_audio_scalable_synchronize_dest_packet_mut(uint16_t packet_mut) {
|
|
list_node_t *node = NULL;
|
|
uint32_t list_len = 0;
|
|
list_t *list = a2dp_audio_context_p->audio_datapath.input_raw_packet_list;
|
|
a2dp_audio_scalable_decoder_frame_t *decoder_frame_p = NULL;
|
|
|
|
list_len = a2dp_audio_list_length(list);
|
|
if (list_len > packet_mut) {
|
|
do {
|
|
node = a2dp_audio_list_begin(list);
|
|
if (node) {
|
|
decoder_frame_p =
|
|
(a2dp_audio_scalable_decoder_frame_t *)a2dp_audio_list_node(node);
|
|
a2dp_audio_list_remove(list, decoder_frame_p);
|
|
}
|
|
} while (a2dp_audio_list_length(list) > packet_mut);
|
|
}
|
|
|
|
TRACE(2, "%s list:%d", __func__, a2dp_audio_list_length(list));
|
|
|
|
return A2DP_DECODER_NO_ERROR;
|
|
}
|
|
|
|
int a2dp_audio_scalable_convert_list_to_samples(uint32_t *samples) {
|
|
uint32_t list_len = 0;
|
|
list_t *list = a2dp_audio_context_p->audio_datapath.input_raw_packet_list;
|
|
|
|
list_len = a2dp_audio_list_length(list);
|
|
*samples = SCALABLE_FRAME_SIZE * list_len;
|
|
|
|
TRACE(3, "%s list:%d samples:%d", __func__, list_len, *samples);
|
|
|
|
return A2DP_DECODER_NO_ERROR;
|
|
}
|
|
|
|
int a2dp_audio_scalable_discards_samples(uint32_t samples) {
|
|
int nRet = A2DP_DECODER_SYNC_ERROR;
|
|
list_t *list = a2dp_audio_context_p->audio_datapath.input_raw_packet_list;
|
|
a2dp_audio_scalable_decoder_frame_t *scalable_decoder_frame = NULL;
|
|
list_node_t *node = NULL;
|
|
int need_remove_list = 0;
|
|
uint32_t list_samples = 0;
|
|
ASSERT(!(samples % SCALABLE_FRAME_SIZE), "%s samples err:%d", __func__,
|
|
samples);
|
|
|
|
a2dp_audio_scalable_convert_list_to_samples(&list_samples);
|
|
if (list_samples >= samples) {
|
|
need_remove_list = samples / SCALABLE_FRAME_SIZE;
|
|
for (int i = 0; i < need_remove_list; i++) {
|
|
node = a2dp_audio_list_begin(list);
|
|
if (node) {
|
|
scalable_decoder_frame =
|
|
(a2dp_audio_scalable_decoder_frame_t *)a2dp_audio_list_node(node);
|
|
a2dp_audio_list_remove(list, scalable_decoder_frame);
|
|
}
|
|
}
|
|
nRet = A2DP_DECODER_NO_ERROR;
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
A2DP_AUDIO_DECODER_T a2dp_audio_scalable_decoder_config = {
|
|
{44100, 2, 16},
|
|
1,
|
|
a2dp_audio_scalable_init,
|
|
a2dp_audio_scalable_deinit,
|
|
a2dp_audio_scalable_decode_frame,
|
|
a2dp_audio_scalable_preparse_packet,
|
|
a2dp_audio_scalable_store_packet,
|
|
a2dp_audio_scalable_discards_packet,
|
|
a2dp_audio_scalable_synchronize_packet,
|
|
a2dp_audio_scalable_synchronize_dest_packet_mut,
|
|
a2dp_audio_scalable_convert_list_to_samples,
|
|
a2dp_audio_scalable_discards_samples,
|
|
a2dp_audio_scalable_headframe_info_get,
|
|
a2dp_audio_scalable_info_get,
|
|
a2dp_audio_scalable_free,
|
|
};
|
|
#else
|
|
A2DP_AUDIO_DECODER_T a2dp_audio_scalable_decoder_config = {
|
|
0,
|
|
};
|
|
#endif
|