1 /*
2 * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /******************************************************************************
8 **
9 ** Name: btc_a2dp_source.c
10 **
11 ******************************************************************************/
12 #include "common/bt_target.h"
13 #include "common/bt_trace.h"
14 #include <string.h>
15 #include <stdio.h>
16 #include <stdint.h>
17 #include <unistd.h>
18 #include <time.h>
19 #include <sys/time.h>
20 #include "osi/allocator.h"
21 #include "osi/alarm.h"
22 #include "osi/thread.h"
23 #include "osi/mutex.h"
24 #include "osi/fixed_queue.h"
25 #include "stack/a2d_api.h"
26 #include "stack/a2d_sbc.h"
27 #include "bta/bta_av_api.h"
28 #include "bta/bta_av_sbc.h"
29 #include "bta/bta_av_ci.h"
30 #include "btc/btc_manage.h"
31 #include "btc/btc_common.h"
32 #include "btc_av_co.h"
33 #include "btc_a2dp.h"
34 #include "btc_a2dp_control.h"
35 #include "btc_a2dp_source.h"
36 #include "btc_av.h"
37 #include "btc/btc_util.h"
38 #include "esp_a2dp_api.h"
39 #include "sbc_encoder.h"
40 #include "osi/future.h"
41 #include <assert.h>
42
43 #if BTC_AV_SRC_INCLUDED
44
45 extern osi_thread_t *btc_thread;
46
47 /*****************************************************************************
48 ** Constants
49 *****************************************************************************/
50
51 /* BTC source command event definition */
52 enum {
53 BTC_MEDIA_TASK_INIT,
54 BTC_MEDIA_TASK_CLEAN_UP,
55 BTC_MEDIA_START_AA_TX,
56 BTC_MEDIA_STOP_AA_TX,
57 BTC_MEDIA_SBC_ENC_INIT,
58 BTC_MEDIA_SBC_ENC_UPDATE,
59 BTC_MEDIA_FLUSH_AA_TX,
60 BTC_MEDIA_AUDIO_FEEDING_INIT,
61 };
62
63 enum {
64 BTC_A2DP_SOURCE_STATE_OFF = 0,
65 BTC_A2DP_SOURCE_STATE_ON = 1,
66 BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN = 2
67 };
68
69
70 /* Media task tick in milliseconds, must be set to multiple of
71 (1000/TICKS_PER_SEC) */
72 #define BTC_MEDIA_TIME_TICK_MS (30)
73 #define A2DP_DATA_READ_POLL_MS (BTC_MEDIA_TIME_TICK_MS / 2)
74
75 #ifndef MAX_PCM_FRAME_NUM_PER_TICK
76 #define MAX_PCM_FRAME_NUM_PER_TICK 21 // 14 for 20ms
77 #endif
78
79 #define BTC_MEDIA_AA_BUF_SIZE (4096+16)
80
81 #if (BTA_AV_CO_CP_SCMS_T == TRUE)
82 #define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE + 1)
83 #else
84 #define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE)
85 #endif
86
87 #ifndef BTC_MEDIA_BITRATE_STEP
88 #define BTC_MEDIA_BITRATE_STEP 5
89 #endif
90
91 #ifndef BTC_A2DP_NON_EDR_MAX_RATE
92 #define BTC_A2DP_NON_EDR_MAX_RATE 229
93 #endif
94
95 /* Middle quality quality setting @ 44.1 khz */
96 #define DEFAULT_SBC_BITRATE 328
97
98 /*
99 * CONGESTION COMPENSATION CTRL ::
100 *
101 * Thus setting controls how many buffers we will hold in media task
102 * during temp link congestion. Together with the stack buffer queues
103 * it controls much temporary a2dp link congestion we can
104 * compensate for. It however also depends on the default run level of sinks
105 * jitterbuffers. Depending on type of sink this would vary.
106 * Ideally the (SRC) max tx buffer capacity should equal the sinks
107 * jitterbuffer runlevel including any intermediate buffers on the way
108 * towards the sinks codec.
109 */
110
111 /* fixme -- define this in pcm time instead of buffer count */
112
113 /* The typical runlevel of the tx queue size is ~1 buffer
114 but due to link flow control or thread preemption in lower
115 layers we might need to temporarily buffer up data */
116
117 /* 5 frames is equivalent to 6.89*5*2.9 ~= 100 ms @ 44.1 khz, 20 ms mediatick */
118 #define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ (5)
119 #define MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ (27) // 18 for 20ms tick
120
121 typedef struct {
122 uint32_t sig;
123 void *param;
124 } a2dp_src_task_evt_t;
125
126 typedef struct {
127 UINT16 num_frames_to_be_processed;
128 UINT16 len;
129 UINT16 offset;
130 UINT16 layer_specific;
131 } tBT_SBC_HDR;
132
133 typedef struct {
134 UINT32 aa_frame_counter;
135 INT32 aa_feed_counter;
136 INT32 aa_feed_residue;
137 UINT32 counter;
138 UINT32 bytes_per_tick; /* pcm bytes read each media task tick */
139 } tBTC_AV_MEDIA_FEEDINGS_PCM_STATE;
140
141 typedef union {
142 tBTC_AV_MEDIA_FEEDINGS_PCM_STATE pcm;
143 } tBTC_AV_MEDIA_FEEDINGS_STATE;
144
145 typedef struct {
146 UINT8 TxTranscoding;
147 BOOLEAN tx_flush; /* discards any outgoing data when true */
148 BOOLEAN is_tx_timer;
149 UINT16 TxAaMtuSize;
150 UINT32 timestamp;
151 fixed_queue_t *TxAaQ;
152 tBTC_AV_FEEDING_MODE feeding_mode;
153 tBTC_AV_MEDIA_FEEDINGS_STATE media_feeding_state;
154 tBTC_AV_MEDIA_FEEDINGS media_feeding;
155 SBC_ENC_PARAMS encoder;
156 osi_alarm_t *media_alarm;
157 } tBTC_A2DP_SOURCE_CB;
158
159 typedef struct {
160 tBTC_A2DP_SOURCE_CB btc_aa_src_cb;
161 osi_thread_t *btc_aa_src_task_hdl;
162 UINT64 last_frame_us;
163 } a2dp_source_local_param_t;
164
165 static void btc_a2dp_source_thread_init(UNUSED_ATTR void *context);
166 static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context);
167 static void btc_a2dp_source_flush_q(fixed_queue_t *p_q);
168
169 static void btc_a2dp_source_feeding_state_reset(void);
170 static void btc_a2dp_source_send_aa_frame(void);
171 static void btc_a2dp_source_aa_start_tx(void);
172 static void btc_a2dp_source_aa_stop_tx(void);
173 static void btc_a2dp_source_enc_init(BT_HDR *p_msg);
174 static void btc_a2dp_source_enc_update(BT_HDR *p_msg);
175 static void btc_a2dp_source_audio_feeding_init(BT_HDR *p_msg);
176 static void btc_a2dp_source_aa_tx_flush(void);
177 static void btc_a2dp_source_prep_2_send(UINT8 nb_frame);
178 static void btc_a2dp_source_handle_timer(UNUSED_ATTR void *context);
179 static void btc_a2dp_source_encoder_init(void);
180
181 static int btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF;
182 static esp_a2d_source_data_cb_t btc_aa_src_data_cb = NULL;
183 #if A2D_DYNAMIC_MEMORY == FALSE
184 static a2dp_source_local_param_t a2dp_source_local_param;
185 #else
186 static a2dp_source_local_param_t *a2dp_source_local_param_ptr;
187 #define a2dp_source_local_param (*a2dp_source_local_param_ptr)
188 #endif ///A2D_DYNAMIC_MEMORY == FALSE
189
btc_a2dp_src_reg_data_cb(esp_a2d_source_data_cb_t callback)190 void btc_a2dp_src_reg_data_cb(esp_a2d_source_data_cb_t callback)
191 {
192 // todo: critical section protection
193 btc_aa_src_data_cb = callback;
194 }
195
btc_aa_src_data_read(uint8_t * data,int32_t len)196 static inline uint32_t btc_aa_src_data_read(uint8_t *data, int32_t len)
197 {
198 // todo: critical section protection
199 if (btc_aa_src_data_cb) {
200 return btc_aa_src_data_cb(data, len);
201 } else {
202 return 0;
203 }
204 }
205
206 /*****************************************************************************
207 ** Misc helper functions
208 *****************************************************************************/
btc_aa_cb_to_app(esp_a2d_cb_event_t event,esp_a2d_cb_param_t * param)209 static inline void btc_aa_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
210 {
211 esp_a2d_cb_t btc_aa_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP);
212 if (btc_aa_cb) {
213 btc_aa_cb(event, param);
214 }
215 }
216
217 /*****************************************************************************
218 ** BTC ADAPTATION
219 *****************************************************************************/
220
btc_a2dp_source_is_streaming(void)221 bool btc_a2dp_source_is_streaming(void)
222 {
223 return a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == TRUE;
224 }
225
btc_a2dp_source_is_task_shutting_down(void)226 bool btc_a2dp_source_is_task_shutting_down(void)
227 {
228 return btc_a2dp_source_state == BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN;
229 }
230
btc_a2dp_source_ctrl(uint32_t sig,void * param)231 static bool btc_a2dp_source_ctrl(uint32_t sig, void *param)
232 {
233 switch (sig) {
234 case BTC_MEDIA_TASK_INIT:
235 btc_a2dp_source_thread_init(NULL);
236 break;
237 case BTC_MEDIA_TASK_CLEAN_UP:
238 btc_a2dp_source_thread_cleanup(NULL);
239 break;
240 case BTC_MEDIA_START_AA_TX:
241 btc_a2dp_source_aa_start_tx();
242 break;
243 case BTC_MEDIA_STOP_AA_TX:
244 btc_a2dp_source_aa_stop_tx();
245 break;
246 case BTC_MEDIA_SBC_ENC_INIT:
247 btc_a2dp_source_enc_init(param);
248 break;
249 case BTC_MEDIA_SBC_ENC_UPDATE:
250 btc_a2dp_source_enc_update(param);
251 break;
252 case BTC_MEDIA_AUDIO_FEEDING_INIT:
253 btc_a2dp_source_audio_feeding_init(param);
254 break;
255 case BTC_MEDIA_FLUSH_AA_TX:
256 btc_a2dp_source_aa_tx_flush();
257 break;
258 default:
259 APPL_TRACE_WARNING("media task unhandled evt: 0x%x\n", sig);
260 }
261
262 if (param != NULL) {
263 osi_free(param);
264 }
265
266 return true;
267 }
268
btc_a2dp_source_startup(void)269 bool btc_a2dp_source_startup(void)
270 {
271 if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_OFF) {
272 APPL_TRACE_ERROR("warning : media task already running");
273 return false;
274 }
275
276 #if A2D_DYNAMIC_MEMORY == TRUE
277 if ((a2dp_source_local_param_ptr = (a2dp_source_local_param_t *)osi_malloc(sizeof(a2dp_source_local_param_t))) == NULL) {
278 APPL_TRACE_ERROR("%s malloc failed!", __func__);
279 return false;
280 }
281 memset((void *)a2dp_source_local_param_ptr, 0, sizeof(a2dp_source_local_param_t));
282 #endif
283
284 APPL_TRACE_EVENT("## A2DP SOURCE START MEDIA THREAD ##");
285
286 a2dp_source_local_param.btc_aa_src_task_hdl = btc_thread;
287
288 if (btc_a2dp_source_ctrl(BTC_MEDIA_TASK_INIT, NULL) == false) {
289 goto error_exit;
290 }
291
292 APPL_TRACE_EVENT("## A2DP SOURCE MEDIA THREAD STARTED ##\n");
293
294 return true;
295
296 error_exit:;
297 APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__);
298 a2dp_source_local_param.btc_aa_src_task_hdl = NULL;
299
300 #if A2D_DYNAMIC_MEMORY == TRUE
301 osi_free(a2dp_source_local_param_ptr);
302 a2dp_source_local_param_ptr = NULL;
303 #endif
304
305 return false;
306 }
307
btc_a2dp_source_shutdown(void)308 void btc_a2dp_source_shutdown(void)
309 {
310 APPL_TRACE_EVENT("## A2DP SOURCE STOP MEDIA THREAD ##\n");
311
312 // Exit thread
313 btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN;
314 btc_a2dp_source_ctrl(BTC_MEDIA_TASK_CLEAN_UP, NULL);
315
316 a2dp_source_local_param.btc_aa_src_task_hdl = NULL;
317
318 #if A2D_DYNAMIC_MEMORY == TRUE
319 osi_free(a2dp_source_local_param_ptr);
320 a2dp_source_local_param_ptr = NULL;
321 #endif
322 }
323
324 /*****************************************************************************
325 **
326 ** Function btc_a2dp_source_on_idle
327 **
328 *******************************************************************************/
btc_a2dp_source_on_idle(void)329 void btc_a2dp_source_on_idle(void)
330 {
331 /* Make sure media task is stopped */
332 btc_a2dp_source_stop_audio_req();
333 }
334
335 /*****************************************************************************
336 **
337 ** Function btc_a2dp_source_on_stopped
338 **
339 *******************************************************************************/
btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND * p_av)340 void btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND *p_av)
341 {
342 /* allow using this api for other than suspend */
343 if (p_av != NULL) {
344 if (p_av->status != BTA_AV_SUCCESS) {
345 APPL_TRACE_EVENT("AV STOP FAILED (%d)", p_av->status);
346 if (p_av->initiator) {
347 btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
348 }
349 return;
350 }
351 }
352
353 /* ensure tx frames are immediately suspended */
354 a2dp_source_local_param.btc_aa_src_cb.tx_flush = 1;
355
356 /* request to stop media task */
357 btc_a2dp_source_tx_flush_req();
358 btc_a2dp_source_stop_audio_req();
359
360 /* once stream is fully stopped we will ack back */
361 }
362
363 /*****************************************************************************
364 **
365 ** Function btc_a2dp_source_on_suspended
366 **
367 **
368 *******************************************************************************/
369
btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND * p_av)370 void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av)
371 {
372 /* check for status failures */
373 if (p_av->status != BTA_AV_SUCCESS) {
374 if (p_av->initiator == TRUE) {
375 btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
376 }
377 }
378
379 /* once stream is fully stopped we will ack back */
380
381 /* ensure tx frames are immediately flushed */
382 a2dp_source_local_param.btc_aa_src_cb.tx_flush = 1;
383
384 /* stop timer tick */
385 btc_a2dp_source_stop_audio_req();
386 }
387
time_now_us(void)388 static UINT64 time_now_us(void)
389 {
390 #if _POSIX_TIMERS
391 struct timespec ts_now;
392 clock_gettime(CLOCK_MONOTONIC, &ts_now);
393 return ((UINT64)ts_now.tv_sec * 1000000L) + ((UINT64)ts_now.tv_nsec / 1000);
394 #else
395 struct timeval ts_now;
396 gettimeofday(&ts_now, NULL);
397 return ((UINT64)ts_now.tv_sec * 1000000L) + ((UINT64)ts_now.tv_usec);
398 #endif
399 }
400
log_tstamps_us(char * comment)401 static void log_tstamps_us(char *comment)
402 {
403 static UINT64 prev_us = 0;
404 UINT64 now_us = time_now_us();
405 APPL_TRACE_DEBUG("[%s] ts %08llu, diff : %08llu, queue sz %d", comment, now_us, now_us - prev_us,
406 fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ));
407 prev_us = now_us;
408 UNUSED(prev_us);
409 }
410
411 /* when true media task discards any tx frames */
btc_a2dp_source_set_tx_flush(BOOLEAN enable)412 void btc_a2dp_source_set_tx_flush(BOOLEAN enable)
413 {
414 APPL_TRACE_EVENT("## DROP TX %d ##", enable);
415 a2dp_source_local_param.btc_aa_src_cb.tx_flush = enable;
416 }
417
418 /*****************************************************************************
419 **
420 ** Function btc_a2dp_source_setup_codec
421 **
422 ** Description
423 **
424 ** Returns
425 **
426 *******************************************************************************/
427
btc_a2dp_source_setup_codec(void)428 void btc_a2dp_source_setup_codec(void)
429 {
430 tBTC_AV_MEDIA_FEEDINGS media_feeding;
431 tBTC_AV_STATUS status;
432
433 APPL_TRACE_EVENT("## A2DP SETUP CODEC ##\n");
434
435 osi_mutex_global_lock();
436
437 /* for now hardcode 44.1 khz 16 bit stereo PCM format */
438 media_feeding.cfg.pcm.sampling_freq = 44100;
439 media_feeding.cfg.pcm.bit_per_sample = 16;
440 media_feeding.cfg.pcm.num_channel = 2;
441 media_feeding.format = BTC_AV_CODEC_PCM;
442
443 if (bta_av_co_audio_set_codec(&media_feeding, &status)) {
444 tBTC_MEDIA_INIT_AUDIO_FEEDING mfeed;
445
446 /* Init the encoding task */
447 btc_a2dp_source_encoder_init();
448
449 /* Build the media task configuration */
450 mfeed.feeding = media_feeding;
451 mfeed.feeding_mode = BTC_AV_FEEDING_ASYNCHRONOUS;
452 /* Send message to Media task to configure transcoding */
453 btc_a2dp_source_audio_feeding_init_req(&mfeed);
454 }
455
456 osi_mutex_global_unlock();
457 }
458
459
460 /*******************************************************************************
461 **
462 ** Function btc_a2dp_source_audio_readbuf
463 **
464 ** Description This function is called by the av_co to get the next buffer to send
465 **
466 **
467 ** Returns void
468 *******************************************************************************/
btc_a2dp_source_audio_readbuf(void)469 BT_HDR *btc_a2dp_source_audio_readbuf(void)
470 {
471 if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON){
472 return NULL;
473 }
474 return fixed_queue_dequeue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, 0);
475 }
476
477 /*******************************************************************************
478 **
479 ** Function btc_a2dp_source_start_audio_req
480 **
481 ** Description
482 **
483 ** Returns TRUE is success
484 **
485 *******************************************************************************/
btc_a2dp_source_start_audio_req(void)486 BOOLEAN btc_a2dp_source_start_audio_req(void)
487 {
488 btc_a2dp_source_ctrl(BTC_MEDIA_START_AA_TX, NULL);
489 return TRUE;
490 }
491
492 /*******************************************************************************
493 **
494 ** Function btc_a2dp_source_stop_audio_req
495 **
496 ** Description
497 **
498 ** Returns TRUE is success
499 **
500 *******************************************************************************/
btc_a2dp_source_stop_audio_req(void)501 BOOLEAN btc_a2dp_source_stop_audio_req(void)
502 {
503 /*
504 * Explicitly check whether the btc_aa_src_ctrl_queue is not NULL to
505 * avoid a race condition during shutdown of the Bluetooth stack.
506 * This race condition is triggered when A2DP audio is streaming on
507 * shutdown:
508 * "btc_a2dp_on_stopped() -> btc_a2dp_source_stop_audio_req()" is called
509 * to stop the particular audio stream, and this happens right after
510 * the "cleanup() -> btc_a2dp_stop_media_task()" processing during
511 * the shutdown of the Bluetooth stack.
512 */
513 #if 0
514 if (btc_aa_src_ctrl_queue != NULL) {
515 #endif
516 btc_a2dp_source_ctrl(BTC_MEDIA_STOP_AA_TX, NULL);
517 #if 0
518 }
519 #endif
520 return TRUE;
521 }
522
523 /*******************************************************************************
524 **
525 ** Function btc_a2dp_source_enc_init_req
526 **
527 ** Description
528 **
529 ** Returns TRUE is success
530 **
531 *******************************************************************************/
btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO * p_msg)532 BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg)
533 {
534 tBTC_MEDIA_INIT_AUDIO *p_buf;
535 if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_INIT_AUDIO)))) {
536 return FALSE;
537 }
538
539 memcpy(p_buf, p_msg, sizeof(tBTC_MEDIA_INIT_AUDIO));
540
541 btc_a2dp_source_ctrl(BTC_MEDIA_SBC_ENC_INIT, p_buf);
542
543 return TRUE;
544 }
545
546 /*******************************************************************************
547 **
548 ** Function btc_a2dp_source_enc_update_req
549 **
550 ** Description
551 **
552 ** Returns TRUE is success
553 **
554 *******************************************************************************/
btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO * p_msg)555 BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg)
556 {
557 tBTC_MEDIA_UPDATE_AUDIO *p_buf;
558 if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_UPDATE_AUDIO)))) {
559 return FALSE;
560 }
561
562 memcpy(p_buf, p_msg, sizeof(tBTC_MEDIA_UPDATE_AUDIO));
563 btc_a2dp_source_ctrl(BTC_MEDIA_SBC_ENC_UPDATE, p_buf);
564 return TRUE;
565 }
566
567 /*******************************************************************************
568 **
569 ** Function btc_a2dp_source_audio_feeding_init_req
570 **
571 ** Description
572 **
573 ** Returns TRUE is success
574 **
575 *******************************************************************************/
btc_a2dp_source_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING * p_msg)576 BOOLEAN btc_a2dp_source_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_msg)
577 {
578 tBTC_MEDIA_INIT_AUDIO_FEEDING *p_buf;
579 if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_INIT_AUDIO_FEEDING)))) {
580 return FALSE;
581 }
582
583 memcpy(p_buf, p_msg, sizeof(tBTC_MEDIA_INIT_AUDIO_FEEDING));
584 btc_a2dp_source_ctrl(BTC_MEDIA_AUDIO_FEEDING_INIT, p_buf);
585 return TRUE;
586 }
587
588 /*******************************************************************************
589 **
590 ** Function btc_a2dp_source_tx_flush_req
591 **
592 ** Description
593 **
594 ** Returns TRUE is success
595 **
596 *******************************************************************************/
btc_a2dp_source_tx_flush_req(void)597 BOOLEAN btc_a2dp_source_tx_flush_req(void)
598 {
599 /*
600 * Explicitly check whether the btc_aa_src_ctrl_queue is not NULL to
601 * avoid a race condition during shutdown of the Bluetooth stack.
602 * This race condition is triggered when A2DP audio is streaming on
603 * shutdown:
604 * "btc_a2dp_on_stopped() -> btc_a2dp_source_tx_flush_req()" is called
605 * to stop the particular audio stream, and this happens right after
606 * the "cleanup() -> btc_a2dp_stop_media_task()" processing during
607 * the shutdown of the Bluetooth stack.
608 */
609 #if 0
610 if (btc_aa_src_ctrl_queue != NULL) {
611 #endif
612 btc_a2dp_source_ctrl(BTC_MEDIA_FLUSH_AA_TX, NULL);
613 #if 0
614 }
615 #endif
616
617 return TRUE;
618 }
619
620 /*****************************************************************************
621 ** BTC ADAPTATION
622 *****************************************************************************/
btc_a2dp_source_get_sbc_rate(void)623 static UINT16 btc_a2dp_source_get_sbc_rate(void)
624 {
625 UINT16 rate = DEFAULT_SBC_BITRATE;
626
627 /* restrict bitrate if a2dp link is non-edr */
628 if (!btc_av_is_peer_edr()) {
629 rate = BTC_A2DP_NON_EDR_MAX_RATE;
630 APPL_TRACE_DEBUG("non-edr a2dp sink detected, restrict rate to %d", rate);
631 }
632 return rate;
633 }
634
btc_a2dp_source_encoder_init(void)635 static void btc_a2dp_source_encoder_init(void)
636 {
637 UINT16 minmtu;
638 tBTC_MEDIA_INIT_AUDIO msg;
639 tA2D_SBC_CIE sbc_config;
640
641 /* lookup table for converting channel mode */
642 UINT16 codec_mode_tbl[5] = { SBC_JOINT_STEREO, SBC_STEREO, SBC_DUAL, 0, SBC_MONO };
643
644 /* lookup table for converting number of blocks */
645 UINT16 codec_block_tbl[5] = { 16, 12, 8, 0, 4 };
646
647 /* lookup table to convert freq */
648 UINT16 freq_block_tbl[5] = { SBC_sf48000, SBC_sf44100, SBC_sf32000, 0, SBC_sf16000 };
649
650 APPL_TRACE_DEBUG("%s", __FUNCTION__);
651
652 /* Retrieve the current SBC configuration (default if currently not used) */
653 bta_av_co_audio_get_sbc_config(&sbc_config, &minmtu);
654 msg.NumOfSubBands = (sbc_config.num_subbands == A2D_SBC_IE_SUBBAND_4) ? 4 : 8;
655 msg.NumOfBlocks = codec_block_tbl[sbc_config.block_len >> 5];
656 msg.AllocationMethod = (sbc_config.alloc_mthd == A2D_SBC_IE_ALLOC_MD_L) ? SBC_LOUDNESS : SBC_SNR;
657 msg.ChannelMode = codec_mode_tbl[sbc_config.ch_mode >> 1];
658 msg.SamplingFreq = freq_block_tbl[sbc_config.samp_freq >> 5];
659 msg.MtuSize = minmtu;
660
661 APPL_TRACE_EVENT("msg.ChannelMode %x", msg.ChannelMode);
662
663 /* Init the media task to encode SBC properly */
664 btc_a2dp_source_enc_init_req(&msg);
665 }
666
btc_a2dp_source_encoder_update(void)667 void btc_a2dp_source_encoder_update(void)
668 {
669 UINT16 minmtu;
670 tA2D_SBC_CIE sbc_config;
671 tBTC_MEDIA_UPDATE_AUDIO msg;
672 UINT8 pref_min;
673 UINT8 pref_max;
674
675 APPL_TRACE_DEBUG("%s", __FUNCTION__);
676
677 /* Retrieve the current SBC configuration (default if currently not used) */
678 bta_av_co_audio_get_sbc_config(&sbc_config, &minmtu);
679
680 APPL_TRACE_DEBUG("%s: Common min_bitpool:%d(0x%x) max_bitpool:%d(0x%x)", __FUNCTION__,
681 sbc_config.min_bitpool, sbc_config.min_bitpool,
682 sbc_config.max_bitpool, sbc_config.max_bitpool);
683
684 if (sbc_config.min_bitpool > sbc_config.max_bitpool) {
685 APPL_TRACE_ERROR("%s: ERROR min_bitpool > max_bitpool", __FUNCTION__);
686 }
687
688 /* check if remote sink has a preferred bitpool range */
689 if (bta_av_co_get_remote_bitpool_pref(&pref_min, &pref_max) == TRUE) {
690 /* adjust our preferred bitpool with the remote preference if within
691 our capable range */
692
693 if (pref_min < sbc_config.min_bitpool) {
694 pref_min = sbc_config.min_bitpool;
695 }
696
697 if (pref_max > sbc_config.max_bitpool) {
698 pref_max = sbc_config.max_bitpool;
699 }
700
701 msg.MinBitPool = pref_min;
702 msg.MaxBitPool = pref_max;
703
704 if ((pref_min != sbc_config.min_bitpool) || (pref_max != sbc_config.max_bitpool)) {
705 APPL_TRACE_EVENT("## adjusted our bitpool range to peer pref [%d:%d] ##",
706 pref_min, pref_max);
707 }
708 } else {
709 msg.MinBitPool = sbc_config.min_bitpool;
710 msg.MaxBitPool = sbc_config.max_bitpool;
711 }
712
713 msg.MinMtuSize = minmtu;
714
715 /* Update the media task to encode SBC properly */
716 btc_a2dp_source_enc_update_req(&msg);
717 }
718
719 /*******************************************************************************
720 **
721 ** Function btc_a2dp_source_enc_init
722 **
723 ** Description Initialize encoding task
724 **
725 ** Returns void
726 **
727 *******************************************************************************/
btc_a2dp_source_enc_init(BT_HDR * p_msg)728 static void btc_a2dp_source_enc_init(BT_HDR *p_msg)
729 {
730 tBTC_MEDIA_INIT_AUDIO *pInitAudio = (tBTC_MEDIA_INIT_AUDIO *) p_msg;
731
732 APPL_TRACE_DEBUG("btc_a2dp_source_enc_init");
733
734 a2dp_source_local_param.btc_aa_src_cb.timestamp = 0;
735
736 /* SBC encoder config (enforced even if not used) */
737 a2dp_source_local_param.btc_aa_src_cb.encoder.sbc_mode = SBC_MODE_STD;
738 a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode = pInitAudio->ChannelMode;
739 a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands = pInitAudio->NumOfSubBands;
740 a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks = pInitAudio->NumOfBlocks;
741 a2dp_source_local_param.btc_aa_src_cb.encoder.s16AllocationMethod = pInitAudio->AllocationMethod;
742 a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = pInitAudio->SamplingFreq;
743
744 a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate = btc_a2dp_source_get_sbc_rate();
745
746 /* Default transcoding is PCM to SBC, modified by feeding configuration */
747 a2dp_source_local_param.btc_aa_src_cb.TxTranscoding = BTC_MEDIA_TRSCD_PCM_2_SBC;
748 a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize = ((BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR))
749 < pInitAudio->MtuSize) ? (BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET
750 - sizeof(BT_HDR)) : pInitAudio->MtuSize;
751
752 APPL_TRACE_EVENT("btc_a2dp_source_enc_init mtu %d, peer mtu %d",
753 a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize, pInitAudio->MtuSize);
754 APPL_TRACE_EVENT(" ch mode %d, subnd %d, nb blk %d, alloc %d, rate %d, freq %d",
755 a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode, a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands,
756 a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks,
757 a2dp_source_local_param.btc_aa_src_cb.encoder.s16AllocationMethod, a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate,
758 a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq);
759
760 /* Reset entirely the SBC encoder */
761 SBC_Encoder_Init(&(a2dp_source_local_param.btc_aa_src_cb.encoder));
762 APPL_TRACE_DEBUG("btc_a2dp_source_enc_init bit pool %d", a2dp_source_local_param.btc_aa_src_cb.encoder.s16BitPool);
763 }
764
765
766 /*******************************************************************************
767 **
768 ** Function btc_a2dp_source_enc_update
769 **
770 ** Description Update encoding task
771 **
772 ** Returns void
773 **
774 *******************************************************************************/
775
btc_a2dp_source_enc_update(BT_HDR * p_msg)776 static void btc_a2dp_source_enc_update(BT_HDR *p_msg)
777 {
778 tBTC_MEDIA_UPDATE_AUDIO *pUpdateAudio = (tBTC_MEDIA_UPDATE_AUDIO *) p_msg;
779 SBC_ENC_PARAMS *pstrEncParams = &a2dp_source_local_param.btc_aa_src_cb.encoder;
780 UINT16 s16SamplingFreq;
781 SINT16 s16BitPool = 0;
782 SINT16 s16BitRate;
783 SINT16 s16FrameLen;
784 UINT8 protect = 0;
785
786 APPL_TRACE_DEBUG("%s : minmtu %d, maxbp %d minbp %d", __FUNCTION__,
787 pUpdateAudio->MinMtuSize, pUpdateAudio->MaxBitPool, pUpdateAudio->MinBitPool);
788
789 /* Only update the bitrate and MTU size while timer is running to make sure it has been initialized */
790 //if (a2dp_source_local_param.btc_aa_src_cb.is_tx_timer)
791 {
792 a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize = ((BTC_MEDIA_AA_BUF_SIZE -
793 BTC_MEDIA_AA_SBC_OFFSET - sizeof(BT_HDR))
794 < pUpdateAudio->MinMtuSize) ? (BTC_MEDIA_AA_BUF_SIZE - BTC_MEDIA_AA_SBC_OFFSET
795 - sizeof(BT_HDR)) : pUpdateAudio->MinMtuSize;
796 /* Set the initial target bit rate */
797 pstrEncParams->u16BitRate = btc_a2dp_source_get_sbc_rate();
798
799 if (pstrEncParams->s16SamplingFreq == SBC_sf16000) {
800 s16SamplingFreq = 16000;
801 } else if (pstrEncParams->s16SamplingFreq == SBC_sf32000) {
802 s16SamplingFreq = 32000;
803 } else if (pstrEncParams->s16SamplingFreq == SBC_sf44100) {
804 s16SamplingFreq = 44100;
805 } else {
806 s16SamplingFreq = 48000;
807 }
808
809 do {
810 if (pstrEncParams->s16NumOfBlocks == 0 || pstrEncParams->s16NumOfSubBands == 0
811 || pstrEncParams->s16NumOfChannels == 0) {
812 APPL_TRACE_ERROR("%s - Avoiding division by zero...", __FUNCTION__);
813 APPL_TRACE_ERROR("%s - block=%d, subBands=%d, channels=%d", __FUNCTION__,
814 pstrEncParams->s16NumOfBlocks, pstrEncParams->s16NumOfSubBands,
815 pstrEncParams->s16NumOfChannels);
816 break;
817 }
818
819 if ((pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) ||
820 (pstrEncParams->s16ChannelMode == SBC_STEREO) ) {
821 s16BitPool = (SINT16)( (pstrEncParams->u16BitRate *
822 pstrEncParams->s16NumOfSubBands * 1000 / s16SamplingFreq)
823 - ( (32 + (4 * pstrEncParams->s16NumOfSubBands *
824 pstrEncParams->s16NumOfChannels)
825 + ( (pstrEncParams->s16ChannelMode - 2) *
826 pstrEncParams->s16NumOfSubBands ) )
827 / pstrEncParams->s16NumOfBlocks) );
828
829 s16FrameLen = 4 + (4 * pstrEncParams->s16NumOfSubBands *
830 pstrEncParams->s16NumOfChannels) / 8
831 + ( ((pstrEncParams->s16ChannelMode - 2) *
832 pstrEncParams->s16NumOfSubBands)
833 + (pstrEncParams->s16NumOfBlocks * s16BitPool) ) / 8;
834
835 s16BitRate = (8 * s16FrameLen * s16SamplingFreq)
836 / (pstrEncParams->s16NumOfSubBands *
837 pstrEncParams->s16NumOfBlocks * 1000);
838
839 if (s16BitRate > pstrEncParams->u16BitRate) {
840 s16BitPool--;
841 }
842
843 if (pstrEncParams->s16NumOfSubBands == 8) {
844 s16BitPool = (s16BitPool > 255) ? 255 : s16BitPool;
845 } else {
846 s16BitPool = (s16BitPool > 128) ? 128 : s16BitPool;
847 }
848 } else {
849 s16BitPool = (SINT16)( ((pstrEncParams->s16NumOfSubBands *
850 pstrEncParams->u16BitRate * 1000)
851 / (s16SamplingFreq * pstrEncParams->s16NumOfChannels))
852 - ( ( (32 / pstrEncParams->s16NumOfChannels) +
853 (4 * pstrEncParams->s16NumOfSubBands) )
854 / pstrEncParams->s16NumOfBlocks ) );
855
856 pstrEncParams->s16BitPool = (s16BitPool >
857 (16 * pstrEncParams->s16NumOfSubBands))
858 ? (16 * pstrEncParams->s16NumOfSubBands) : s16BitPool;
859 }
860
861 if (s16BitPool < 0) {
862 s16BitPool = 0;
863 }
864
865 APPL_TRACE_EVENT("bitpool candidate : %d (%d kbps)",
866 s16BitPool, pstrEncParams->u16BitRate);
867
868 if (s16BitPool > pUpdateAudio->MaxBitPool) {
869 APPL_TRACE_DEBUG("%s computed bitpool too large (%d)", __FUNCTION__, s16BitPool);
870 /* Decrease bitrate */
871 a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate -= BTC_MEDIA_BITRATE_STEP;
872 /* Record that we have decreased the bitrate */
873 protect |= 1;
874 } else if (s16BitPool < pUpdateAudio->MinBitPool) {
875 APPL_TRACE_WARNING("%s computed bitpool too small (%d)", __FUNCTION__, s16BitPool);
876
877 /* Increase bitrate */
878 UINT16 previous_u16BitRate = a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate;
879 a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate += BTC_MEDIA_BITRATE_STEP;
880 /* Record that we have increased the bitrate */
881 protect |= 2;
882 /* Check over-flow */
883 if (a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate < previous_u16BitRate) {
884 protect |= 3;
885 }
886 } else {
887 break;
888 }
889 /* In case we have already increased and decreased the bitrate, just stop */
890 if (protect == 3) {
891 APPL_TRACE_ERROR("%s could not find bitpool in range", __FUNCTION__);
892 break;
893 }
894 } while (1);
895
896 /* Finally update the bitpool in the encoder structure */
897 pstrEncParams->s16BitPool = s16BitPool;
898
899 APPL_TRACE_DEBUG("%s final bit rate %d, final bit pool %d", __FUNCTION__,
900 a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate, a2dp_source_local_param.btc_aa_src_cb.encoder.s16BitPool);
901
902 /* make sure we reinitialize encoder with new settings */
903 SBC_Encoder_Init(&(a2dp_source_local_param.btc_aa_src_cb.encoder));
904 }
905 }
906
907 /*******************************************************************************
908 **
909 ** Function btc_a2dp_source_pcm2sbc_init
910 **
911 ** Description Init encoding task for PCM to SBC according to feeding
912 **
913 ** Returns void
914 **
915 *******************************************************************************/
btc_a2dp_source_pcm2sbc_init(tBTC_MEDIA_INIT_AUDIO_FEEDING * p_feeding)916 static void btc_a2dp_source_pcm2sbc_init(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feeding)
917 {
918 BOOLEAN reconfig_needed = FALSE;
919
920 APPL_TRACE_DEBUG("PCM feeding:");
921 APPL_TRACE_DEBUG("sampling_freq:%d", p_feeding->feeding.cfg.pcm.sampling_freq);
922 APPL_TRACE_DEBUG("num_channel:%d", p_feeding->feeding.cfg.pcm.num_channel);
923 APPL_TRACE_DEBUG("bit_per_sample:%d", p_feeding->feeding.cfg.pcm.bit_per_sample);
924
925 /* Check the PCM feeding sampling_freq */
926 switch (p_feeding->feeding.cfg.pcm.sampling_freq) {
927 case 8000:
928 case 12000:
929 case 16000:
930 case 24000:
931 case 32000:
932 case 48000:
933 /* For these sampling_freq the AV connection must be 48000 */
934 if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf48000) {
935 /* Reconfiguration needed at 48000 */
936 APPL_TRACE_DEBUG("SBC Reconfiguration needed at 48000");
937 a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf48000;
938 reconfig_needed = TRUE;
939 }
940 break;
941
942 case 11025:
943 case 22050:
944 case 44100:
945 /* For these sampling_freq the AV connection must be 44100 */
946 if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf44100) {
947 /* Reconfiguration needed at 44100 */
948 APPL_TRACE_DEBUG("SBC Reconfiguration needed at 44100");
949 a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf44100;
950 reconfig_needed = TRUE;
951 }
952 break;
953 default:
954 APPL_TRACE_DEBUG("Feeding PCM sampling_freq unsupported");
955 break;
956 }
957
958 /* Some AV Headsets do not support Mono => always ask for Stereo */
959 if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode == SBC_MONO) {
960 APPL_TRACE_DEBUG("SBC Reconfiguration needed in Stereo");
961 a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode = SBC_JOINT_STEREO;
962 reconfig_needed = TRUE;
963 }
964
965 if (reconfig_needed != FALSE) {
966 APPL_TRACE_DEBUG("%s :: mtu %d", __FUNCTION__, a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize);
967 APPL_TRACE_DEBUG("ch mode %d, nbsubd %d, nb %d, alloc %d, rate %d, freq %d",
968 a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode,
969 a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands, a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks,
970 a2dp_source_local_param.btc_aa_src_cb.encoder.s16AllocationMethod, a2dp_source_local_param.btc_aa_src_cb.encoder.u16BitRate,
971 a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq);
972
973 SBC_Encoder_Init(&(a2dp_source_local_param.btc_aa_src_cb.encoder));
974 } else {
975 APPL_TRACE_DEBUG("%s no SBC reconfig needed", __FUNCTION__);
976 }
977 }
978
979 /*******************************************************************************
980 **
981 ** Function btc_a2dp_source_audio_feeding_init
982 **
983 ** Description Initialize the audio path according to the feeding format
984 **
985 ** Returns void
986 **
987 *******************************************************************************/
btc_a2dp_source_audio_feeding_init(BT_HDR * p_msg)988 static void btc_a2dp_source_audio_feeding_init(BT_HDR *p_msg)
989 {
990 tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feeding = (tBTC_MEDIA_INIT_AUDIO_FEEDING *) p_msg;
991
992 APPL_TRACE_DEBUG("%s format:%d", __FUNCTION__, p_feeding->feeding.format);
993
994 /* Save Media Feeding information */
995 a2dp_source_local_param.btc_aa_src_cb.feeding_mode = p_feeding->feeding_mode;
996 a2dp_source_local_param.btc_aa_src_cb.media_feeding = p_feeding->feeding;
997
998 /* Handle different feeding formats */
999 switch (p_feeding->feeding.format) {
1000 case BTC_AV_CODEC_PCM:
1001 a2dp_source_local_param.btc_aa_src_cb.TxTranscoding = BTC_MEDIA_TRSCD_PCM_2_SBC;
1002 btc_a2dp_source_pcm2sbc_init(p_feeding);
1003 break;
1004
1005 default :
1006 APPL_TRACE_ERROR("unknown feeding format %d", p_feeding->feeding.format);
1007 break;
1008 }
1009 }
1010
1011 /*******************************************************************************
1012 **
1013 ** Function btc_a2dp_source_aa_tx_flush
1014 **
1015 ** Description
1016 **
1017 ** Returns void
1018 **
1019 *******************************************************************************/
btc_a2dp_source_aa_tx_flush(void)1020 static void btc_a2dp_source_aa_tx_flush(void)
1021 {
1022 /* Flush all enqueued music buffers (encoded) */
1023 APPL_TRACE_DEBUG("%s", __FUNCTION__);
1024
1025 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter = 0;
1026 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue = 0;
1027
1028 btc_a2dp_source_flush_q(a2dp_source_local_param.btc_aa_src_cb.TxAaQ);
1029
1030 btc_aa_src_data_read(NULL, -1);
1031 }
1032
1033 /*******************************************************************************
1034 **
1035 ** Function btc_get_num_aa_frame
1036 **
1037 ** Description
1038 **
1039 ** Returns The number of media frames in this time slice
1040 **
1041 *******************************************************************************/
btc_get_num_aa_frame(void)1042 static UINT8 btc_get_num_aa_frame(void)
1043 {
1044 UINT8 result = 0;
1045
1046 switch (a2dp_source_local_param.btc_aa_src_cb.TxTranscoding) {
1047 case BTC_MEDIA_TRSCD_PCM_2_SBC: {
1048 UINT32 pcm_bytes_per_frame = a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands *
1049 a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks *
1050 a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel *
1051 a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8;
1052
1053 UINT32 us_this_tick = BTC_MEDIA_TIME_TICK_MS * 1000;
1054 UINT64 now_us = time_now_us();
1055 if (a2dp_source_local_param.last_frame_us != 0) {
1056 #if _POSIX_TIMERS
1057 us_this_tick = (now_us - a2dp_source_local_param.last_frame_us);
1058 #else
1059 // consider the case that the number of day increases and timeofday wraps around
1060 us_this_tick = (now_us > a2dp_source_local_param.last_frame_us) ? (now_us - a2dp_source_local_param.last_frame_us) :
1061 (now_us + 86400000000ull - a2dp_source_local_param.last_frame_us);
1062 #endif
1063 }
1064 a2dp_source_local_param.last_frame_us = now_us;
1065
1066 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter +=
1067 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick *
1068 us_this_tick / (BTC_MEDIA_TIME_TICK_MS * 1000);
1069
1070 /* calculate nbr of frames pending for this media tick */
1071 result = a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter / pcm_bytes_per_frame;
1072
1073 /* limit the frames to be sent */
1074 UINT32 frm_nb_threshold = MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ);
1075 if (frm_nb_threshold > MAX_PCM_FRAME_NUM_PER_TICK) {
1076 frm_nb_threshold = MAX_PCM_FRAME_NUM_PER_TICK;
1077 }
1078
1079 if (result > frm_nb_threshold) {
1080 APPL_TRACE_EVENT("Limit frms to send from %d to %d", result, frm_nb_threshold);
1081 result = frm_nb_threshold;
1082 }
1083 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter -= result * pcm_bytes_per_frame;
1084
1085 BTC_TRACE_VERBOSE("WRITE %d FRAMES", result);
1086 }
1087 break;
1088
1089 default:
1090 APPL_TRACE_ERROR("ERROR btc_get_num_aa_frame Unsupported transcoding format 0x%x",
1091 a2dp_source_local_param.btc_aa_src_cb.TxTranscoding);
1092 result = 0;
1093 break;
1094 }
1095
1096 return (UINT8)result;
1097 }
1098
1099 /*******************************************************************************
1100 **
1101 ** Function btc_media_aa_read_feeding
1102 **
1103 ** Description
1104 **
1105 ** Returns void
1106 **
1107 *******************************************************************************/
1108
btc_media_aa_read_feeding(void)1109 BOOLEAN btc_media_aa_read_feeding(void)
1110 {
1111 UINT16 blocm_x_subband = a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands * \
1112 a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks;
1113 UINT32 read_size;
1114 UINT16 sbc_sampling = 48000;
1115 UINT32 src_samples;
1116 UINT16 bytes_needed = blocm_x_subband * a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfChannels * \
1117 a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8;
1118 static UINT16 up_sampled_buffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS
1119 * SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS * 2];
1120 static UINT16 read_buffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS
1121 * SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS];
1122 UINT32 src_size_used;
1123 UINT32 dst_size_used;
1124 BOOLEAN fract_needed;
1125 INT32 fract_max;
1126 INT32 fract_threshold;
1127 UINT32 nb_byte_read = 0;
1128
1129 /* Get the SBC sampling rate */
1130 switch (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq) {
1131 case SBC_sf48000:
1132 sbc_sampling = 48000;
1133 break;
1134 case SBC_sf44100:
1135 sbc_sampling = 44100;
1136 break;
1137 case SBC_sf32000:
1138 sbc_sampling = 32000;
1139 break;
1140 case SBC_sf16000:
1141 sbc_sampling = 16000;
1142 break;
1143 }
1144
1145 if (sbc_sampling == a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq) {
1146 read_size = bytes_needed - a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue;
1147 nb_byte_read = btc_aa_src_data_read(
1148 ((uint8_t *)a2dp_source_local_param.btc_aa_src_cb.encoder.as16PcmBuffer) +
1149 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue,
1150 read_size);
1151 if (nb_byte_read == read_size) {
1152 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue = 0;
1153 return TRUE;
1154 } else {
1155 APPL_TRACE_WARNING("### UNDERFLOW :: ONLY READ %d BYTES OUT OF %d ###",
1156 nb_byte_read, read_size);
1157 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue += nb_byte_read;
1158 return FALSE;
1159 }
1160 }
1161
1162 /* Some Feeding PCM frequencies require to split the number of sample */
1163 /* to read. */
1164 /* E.g 128/6=21.3333 => read 22 and 21 and 21 => max = 2; threshold = 0*/
1165 fract_needed = FALSE; /* Default */
1166 switch (a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq) {
1167 case 32000:
1168 case 8000:
1169 fract_needed = TRUE;
1170 fract_max = 2; /* 0, 1 and 2 */
1171 fract_threshold = 0; /* Add one for the first */
1172 break;
1173 case 16000:
1174 fract_needed = TRUE;
1175 fract_max = 2; /* 0, 1 and 2 */
1176 fract_threshold = 1; /* Add one for the first two frames*/
1177 break;
1178 }
1179
1180 /* Compute number of sample to read from source */
1181 src_samples = blocm_x_subband;
1182 src_samples *= a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq;
1183 src_samples /= sbc_sampling;
1184
1185 /* The previous division may have a remainder not null */
1186 if (fract_needed) {
1187 if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter <= fract_threshold) {
1188 src_samples++; /* for every read before threshold add one sample */
1189 }
1190
1191 /* do nothing if counter >= threshold */
1192 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter++; /* one more read */
1193 if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter > fract_max) {
1194 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_counter = 0;
1195 }
1196 }
1197
1198 /* Compute number of bytes to read from source */
1199 read_size = src_samples;
1200 read_size *= a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel;
1201 read_size *= (a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8);
1202
1203 /* Read Data from data channel */
1204 nb_byte_read = btc_aa_src_data_read((uint8_t *)read_buffer, read_size);
1205
1206 if (nb_byte_read < read_size) {
1207 APPL_TRACE_WARNING("### UNDERRUN :: ONLY READ %d BYTES OUT OF %d ###",
1208 nb_byte_read, read_size);
1209
1210 if (nb_byte_read == 0) {
1211 return FALSE;
1212 }
1213
1214 if (a2dp_source_local_param.btc_aa_src_cb.feeding_mode == BTC_AV_FEEDING_ASYNCHRONOUS) {
1215 /* Fill the unfilled part of the read buffer with silence (0) */
1216 memset(((UINT8 *)read_buffer) + nb_byte_read, 0, read_size - nb_byte_read);
1217 nb_byte_read = read_size;
1218 }
1219 }
1220
1221 /* Initialize PCM up-sampling engine */
1222 bta_av_sbc_init_up_sample(a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq,
1223 sbc_sampling, a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample,
1224 a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel);
1225
1226 /* re-sample read buffer */
1227 /* The output PCM buffer will be stereo, 16 bit per sample */
1228 dst_size_used = bta_av_sbc_up_sample((UINT8 *)read_buffer,
1229 (UINT8 *)up_sampled_buffer + a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue,
1230 nb_byte_read,
1231 sizeof(up_sampled_buffer) - a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue,
1232 &src_size_used);
1233
1234 /* update the residue */
1235 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue += dst_size_used;
1236
1237 /* only copy the pcm sample when we have up-sampled enough PCM */
1238 if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue >= bytes_needed) {
1239 /* Copy the output pcm samples in SBC encoding buffer */
1240 memcpy((UINT8 *)a2dp_source_local_param.btc_aa_src_cb.encoder.as16PcmBuffer,
1241 (UINT8 *)up_sampled_buffer,
1242 bytes_needed);
1243 /* update the residue */
1244 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue -= bytes_needed;
1245
1246 if (a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue != 0) {
1247 memcpy((UINT8 *)up_sampled_buffer,
1248 (UINT8 *)up_sampled_buffer + bytes_needed,
1249 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue);
1250 }
1251 return TRUE;
1252 }
1253
1254 return FALSE;
1255 }
1256
1257 /*******************************************************************************
1258 **
1259 ** Function btc_media_aa_prep_sbc_2_send
1260 **
1261 ** Description
1262 **
1263 ** Returns void
1264 **
1265 *******************************************************************************/
btc_media_aa_prep_sbc_2_send(UINT8 nb_frame)1266 static void btc_media_aa_prep_sbc_2_send(UINT8 nb_frame)
1267 {
1268 BT_HDR *p_buf;
1269 UINT16 blocm_x_subband = a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands *
1270 a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks;
1271
1272 while (nb_frame) {
1273 if (NULL == (p_buf = osi_malloc(BTC_MEDIA_AA_BUF_SIZE))) {
1274 APPL_TRACE_ERROR ("ERROR btc_media_aa_prep_sbc_2_send no buffer TxCnt %d ",
1275 fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ));
1276 return;
1277 }
1278
1279 /* Init buffer */
1280 p_buf->offset = BTC_MEDIA_AA_SBC_OFFSET;
1281 p_buf->len = 0;
1282 p_buf->layer_specific = 0;
1283
1284 do {
1285 /* Write @ of allocated buffer in encoder.pu8Packet */
1286 a2dp_source_local_param.btc_aa_src_cb.encoder.pu8Packet = (UINT8 *) (p_buf + 1) + p_buf->offset + p_buf->len;
1287 /* Fill allocated buffer with 0 */
1288 memset(a2dp_source_local_param.btc_aa_src_cb.encoder.as16PcmBuffer, 0, blocm_x_subband
1289 * a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfChannels);
1290
1291 /* Read PCM data and upsample them if needed */
1292 if (btc_media_aa_read_feeding()) {
1293 /* SBC encode and descramble frame */
1294 SBC_Encoder(&(a2dp_source_local_param.btc_aa_src_cb.encoder));
1295
1296 /* Update SBC frame length */
1297 p_buf->len += a2dp_source_local_param.btc_aa_src_cb.encoder.u16PacketLength;
1298 nb_frame--;
1299 p_buf->layer_specific++;
1300 } else {
1301 APPL_TRACE_WARNING("btc_media_aa_prep_sbc_2_send underflow %d, %d",
1302 nb_frame, a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.aa_feed_residue);
1303 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.counter += nb_frame *
1304 a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfSubBands *
1305 a2dp_source_local_param.btc_aa_src_cb.encoder.s16NumOfBlocks *
1306 a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel *
1307 a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8;
1308 /* no more pcm to read */
1309 nb_frame = 0;
1310
1311 /* break read loop if timer was stopped (media task stopped) */
1312 if ( a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == FALSE ) {
1313 osi_free(p_buf);
1314 return;
1315 }
1316 }
1317
1318 } while (((p_buf->len + a2dp_source_local_param.btc_aa_src_cb.encoder.u16PacketLength) < a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize)
1319 && (p_buf->layer_specific < 0x0F) && nb_frame);
1320
1321 if (p_buf->len) {
1322 /* timestamp of the media packet header represent the TS of the first SBC frame
1323 i.e the timestamp before including this frame */
1324 *((UINT32 *) (p_buf + 1)) = a2dp_source_local_param.btc_aa_src_cb.timestamp;
1325
1326 a2dp_source_local_param.btc_aa_src_cb.timestamp += p_buf->layer_specific * blocm_x_subband;
1327
1328 if (a2dp_source_local_param.btc_aa_src_cb.tx_flush) {
1329 APPL_TRACE_DEBUG("### tx suspended, discarded frame ###");
1330
1331 if (fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ) > 0) {
1332 btc_a2dp_source_flush_q(a2dp_source_local_param.btc_aa_src_cb.TxAaQ);
1333 }
1334
1335 osi_free(p_buf);
1336 return;
1337 }
1338
1339 /* Enqueue the encoded SBC frame in AA Tx Queue */
1340 fixed_queue_enqueue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, p_buf, FIXED_QUEUE_MAX_TIMEOUT);
1341 } else {
1342 osi_free(p_buf);
1343 }
1344 }
1345 }
1346
1347 /*******************************************************************************
1348 **
1349 ** Function btc_a2dp_source_prep_2_send
1350 **
1351 ** Description
1352 **
1353 ** Returns void
1354 **
1355 *******************************************************************************/
btc_a2dp_source_prep_2_send(UINT8 nb_frame)1356 static void btc_a2dp_source_prep_2_send(UINT8 nb_frame)
1357 {
1358 // Check for TX queue overflow
1359 if (nb_frame > MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ) {
1360 nb_frame = MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ;
1361 }
1362
1363 if (fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) {
1364 APPL_TRACE_WARNING("TX Q overflow: %d/%d",
1365 fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ), MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame);
1366 }
1367
1368 while (fixed_queue_length(a2dp_source_local_param.btc_aa_src_cb.TxAaQ) > (MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ - nb_frame)) {
1369 osi_free(fixed_queue_dequeue(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, 0));
1370 }
1371
1372 // Transcode frame
1373
1374 switch (a2dp_source_local_param.btc_aa_src_cb.TxTranscoding) {
1375 case BTC_MEDIA_TRSCD_PCM_2_SBC:
1376 btc_media_aa_prep_sbc_2_send(nb_frame);
1377 break;
1378
1379 default:
1380 APPL_TRACE_ERROR("%s unsupported transcoding format 0x%x", __func__, a2dp_source_local_param.btc_aa_src_cb.TxTranscoding);
1381 break;
1382 }
1383 }
1384
1385 /*******************************************************************************
1386 **
1387 ** Function btc_a2dp_source_send_aa_frame
1388 **
1389 ** Description
1390 **
1391 ** Returns void
1392 **
1393 *******************************************************************************/
btc_a2dp_source_send_aa_frame(void)1394 static void btc_a2dp_source_send_aa_frame(void)
1395 {
1396 UINT8 nb_frame_2_send;
1397
1398 /* get the number of frame to send */
1399 nb_frame_2_send = btc_get_num_aa_frame();
1400
1401 if (nb_frame_2_send != 0) {
1402 /* format and Q buffer to send */
1403 btc_a2dp_source_prep_2_send(nb_frame_2_send);
1404 }
1405
1406 /* send it */
1407 BTC_TRACE_VERBOSE("%s: send %d frames", __FUNCTION__, nb_frame_2_send);
1408 bta_av_ci_src_data_ready(BTA_AV_CHNL_AUDIO);
1409 }
1410
btc_a2dp_source_handle_timer(UNUSED_ATTR void * context)1411 static void btc_a2dp_source_handle_timer(UNUSED_ATTR void *context)
1412 {
1413 log_tstamps_us("media task tx timer");
1414
1415 #if (BTA_AV_INCLUDED == TRUE)
1416 if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON || g_a2dp_source_ongoing_deinit){
1417 return;
1418 }
1419
1420 if (a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == TRUE) {
1421 btc_a2dp_source_send_aa_frame();
1422 } else {
1423 APPL_TRACE_WARNING("Media task Scheduled after Suspend");
1424 }
1425 #endif
1426 }
1427
1428 /*******************************************************************************
1429 **
1430 ** Function btc_a2dp_source_feeding_state_reset
1431 **
1432 ** Description Reset the media feeding state
1433 **
1434 ** Returns void
1435 **
1436 *******************************************************************************/
btc_a2dp_source_feeding_state_reset(void)1437 static void btc_a2dp_source_feeding_state_reset(void)
1438 {
1439 /* By default, just clear the entire state */
1440 memset(&a2dp_source_local_param.btc_aa_src_cb.media_feeding_state, 0, sizeof(a2dp_source_local_param.btc_aa_src_cb.media_feeding_state));
1441
1442 if (a2dp_source_local_param.btc_aa_src_cb.TxTranscoding == BTC_MEDIA_TRSCD_PCM_2_SBC) {
1443 a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick =
1444 (a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.sampling_freq *
1445 a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.bit_per_sample / 8 *
1446 a2dp_source_local_param.btc_aa_src_cb.media_feeding.cfg.pcm.num_channel *
1447 BTC_MEDIA_TIME_TICK_MS) / 1000;
1448
1449 APPL_TRACE_EVENT("pcm bytes per tick %d",
1450 (int)a2dp_source_local_param.btc_aa_src_cb.media_feeding_state.pcm.bytes_per_tick);
1451 }
1452 }
1453
1454 /*******************************************************************************
1455 **
1456 ** Function btc_a2dp_source_aa_stop_tx
1457 **
1458 ** Description Stop media task encoding
1459 **
1460 ** Returns void
1461 **
1462 *******************************************************************************/
btc_a2dp_source_aa_stop_tx(void)1463 static void btc_a2dp_source_aa_stop_tx(void)
1464 {
1465 APPL_TRACE_DEBUG("%s is_tx_timer: %d", __func__, a2dp_source_local_param.btc_aa_src_cb.is_tx_timer);
1466
1467 const bool send_ack = (a2dp_source_local_param.btc_aa_src_cb.is_tx_timer != FALSE);
1468
1469 /* Stop the timer first */
1470 if (a2dp_source_local_param.btc_aa_src_cb.media_alarm) {
1471 osi_alarm_cancel(a2dp_source_local_param.btc_aa_src_cb.media_alarm);
1472 osi_alarm_free(a2dp_source_local_param.btc_aa_src_cb.media_alarm);
1473 }
1474 a2dp_source_local_param.btc_aa_src_cb.media_alarm = NULL;
1475 a2dp_source_local_param.btc_aa_src_cb.is_tx_timer = FALSE;
1476
1477 /* Try to send acknowldegment once the media stream is
1478 stopped. This will make sure that the A2DP HAL layer is
1479 un-blocked on wait for acknowledgment for the sent command.
1480 This resolves a corner cases AVDTP SUSPEND collision
1481 when the DUT and the remote device issue SUSPEND simultaneously
1482 and due to the processing of the SUSPEND request from the remote,
1483 the media path is torn down. If the A2DP HAL happens to wait
1484 for ACK for the initiated SUSPEND, it would never receive it casuing
1485 a block/wait. Due to this acknowledgement, the A2DP HAL is guranteed
1486 to get the ACK for any pending command in such cases. */
1487
1488 if (send_ack) {
1489 btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS);
1490 }
1491
1492 /* audio engine stopped, reset tx suspended flag */
1493 a2dp_source_local_param.btc_aa_src_cb.tx_flush = 0;
1494 a2dp_source_local_param.last_frame_us = 0;
1495
1496 /* Reset the feeding state */
1497 btc_a2dp_source_feeding_state_reset();
1498 }
1499
1500 /*******************************************************************************
1501 **
1502 ** Function btc_a2dp_source_aa_start_tx
1503 **
1504 ** Description Start media task encoding
1505 **
1506 ** Returns void
1507 **
1508 *******************************************************************************/
btc_a2dp_source_alarm_cb(UNUSED_ATTR void * context)1509 static void btc_a2dp_source_alarm_cb(UNUSED_ATTR void *context)
1510 {
1511 if (a2dp_source_local_param.btc_aa_src_task_hdl) {
1512 osi_thread_post(a2dp_source_local_param.btc_aa_src_task_hdl, btc_a2dp_source_handle_timer, NULL, 1, OSI_THREAD_MAX_TIMEOUT);
1513 } else {
1514 APPL_TRACE_DEBUG("[%s] A2DP ALREADY FREED", __func__);
1515 btc_a2dp_source_aa_stop_tx();
1516 }
1517 }
1518
btc_a2dp_source_aa_start_tx(void)1519 static void btc_a2dp_source_aa_start_tx(void)
1520 {
1521 APPL_TRACE_DEBUG("btc_a2dp_source_aa_start_tx is timer %d, feeding mode %d",
1522 a2dp_source_local_param.btc_aa_src_cb.is_tx_timer, a2dp_source_local_param.btc_aa_src_cb.feeding_mode);
1523
1524 a2dp_source_local_param.btc_aa_src_cb.is_tx_timer = TRUE;
1525 a2dp_source_local_param.last_frame_us = 0;
1526
1527 /* Reset the media feeding state */
1528 btc_a2dp_source_feeding_state_reset();
1529
1530 APPL_TRACE_EVENT("starting timer %dms", BTC_MEDIA_TIME_TICK_MS);
1531
1532 assert(a2dp_source_local_param.btc_aa_src_cb.media_alarm == NULL);
1533
1534 a2dp_source_local_param.btc_aa_src_cb.media_alarm = osi_alarm_new("aaTx", btc_a2dp_source_alarm_cb, NULL, BTC_MEDIA_TIME_TICK_MS);
1535
1536 if (!a2dp_source_local_param.btc_aa_src_cb.media_alarm) {
1537 BTC_TRACE_ERROR("%s unable to allocate media alarm.", __func__);
1538 return;
1539 }
1540
1541 osi_alarm_set_periodic(a2dp_source_local_param.btc_aa_src_cb.media_alarm, BTC_MEDIA_TIME_TICK_MS);
1542 }
1543
1544 /*******************************************************************************
1545 **
1546 ** Function btc_a2dp_source_flush_q
1547 **
1548 ** Description
1549 **
1550 ** Returns void
1551 **
1552 *******************************************************************************/
btc_a2dp_source_flush_q(fixed_queue_t * p_q)1553 static void btc_a2dp_source_flush_q(fixed_queue_t *p_q)
1554 {
1555 while (! fixed_queue_is_empty(p_q)) {
1556 osi_free(fixed_queue_dequeue(p_q, 0));
1557 }
1558 }
1559
btc_a2dp_source_thread_init(UNUSED_ATTR void * context)1560 static void btc_a2dp_source_thread_init(UNUSED_ATTR void *context)
1561 {
1562 APPL_TRACE_EVENT("%s\n", __func__);
1563 memset(&a2dp_source_local_param.btc_aa_src_cb, 0, sizeof(a2dp_source_local_param.btc_aa_src_cb));
1564
1565 btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_ON;
1566
1567 a2dp_source_local_param.btc_aa_src_cb.TxAaQ = fixed_queue_new(QUEUE_SIZE_MAX);
1568
1569 btc_a2dp_control_init();
1570 }
1571
btc_a2dp_source_thread_cleanup(UNUSED_ATTR void * context)1572 static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context)
1573 {
1574 /* Clear media task flag */
1575 btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF;
1576
1577 btc_a2dp_control_cleanup();
1578
1579 fixed_queue_free(a2dp_source_local_param.btc_aa_src_cb.TxAaQ, osi_free_func);
1580
1581 a2dp_source_local_param.btc_aa_src_cb.TxAaQ = NULL;
1582 }
1583
1584 #endif /* BTC_AV_INCLUDED */
1585