1 /*
2 * Copyright 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <errno.h>
18 #include <grp.h>
19 #include <math.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include <cfloat>
24 #include <memory>
25
26 // Define before including log.h
27 #define LOG_TAG "sco_hci"
28
29 #include "btif/include/core_callbacks.h"
30 #include "btif/include/stack_manager.h"
31 #include "osi/include/allocator.h"
32 #include "osi/include/log.h"
33 #include "stack/btm/btm_sco.h"
34 #include "udrv/include/uipc.h"
35
36 #define SCO_DATA_READ_POLL_MS 10
37 #define SCO_HOST_DATA_PATH "/var/run/bluetooth/audio/.sco_data"
38 // TODO(b/198260375): Make SCO data owner group configurable.
39 #define SCO_HOST_DATA_GROUP "bluetooth-audio"
40
41 /* Per Bluetooth Core v5.0 and HFP 1.7 specification. */
42 #define BTM_MSBC_H2_HEADER_0 0x01
43 #define BTM_MSBC_H2_HEADER_LEN 2
44 #define BTM_MSBC_PKT_LEN 60
45 #define BTM_MSBC_PKT_FRAME_LEN 57 /* Packet length without the header */
46 #define BTM_MSBC_SYNC_WORD 0xAD
47
48 /* Used by PLC */
49 #define BTM_MSBC_SAMPLE_SIZE 2 /* 2 bytes*/
50 #define BTM_MSBC_FS 120 /* Frame Size */
51
52 #define BTM_PLC_WL 256 /* 16ms - Window Length for pattern matching */
53 #define BTM_PLC_TL 64 /* 4ms - Template Length for matching */
54 #define BTM_PLC_HL \
55 (BTM_PLC_WL + BTM_MSBC_FS - 1) /* Length of History buffer required */
56 #define BTM_PLC_SBCRL 36 /* SBC Reconvergence sample Length */
57 #define BTM_PLC_OLAL 16 /* OverLap-Add Length */
58
59 /* Disable the PLC when there are more than threshold of lost packets in the
60 * window */
61 #define BTM_PLC_WINDOW_SIZE 5
62 #define BTM_PLC_PL_THRESHOLD 1
63
64 namespace {
65
66 std::unique_ptr<tUIPC_STATE> sco_uipc = nullptr;
67
sco_data_cb(tUIPC_CH_ID,tUIPC_EVENT event)68 void sco_data_cb(tUIPC_CH_ID, tUIPC_EVENT event) {
69 switch (event) {
70 case UIPC_OPEN_EVT:
71 /*
72 * Read directly from media task from here on (keep callback for
73 * connection events.
74 */
75 UIPC_Ioctl(*sco_uipc, UIPC_CH_ID_AV_AUDIO, UIPC_REG_REMOVE_ACTIVE_READSET,
76 NULL);
77 UIPC_Ioctl(*sco_uipc, UIPC_CH_ID_AV_AUDIO, UIPC_SET_READ_POLL_TMO,
78 reinterpret_cast<void*>(SCO_DATA_READ_POLL_MS));
79 break;
80 default:
81 break;
82 }
83 }
84
85 } // namespace
86
87 namespace bluetooth {
88 namespace audio {
89 namespace sco {
90
open()91 void open() {
92 if (sco_uipc != nullptr) {
93 LOG_WARN("Re-opening UIPC that is already running");
94 }
95
96 sco_uipc = UIPC_Init();
97 if (sco_uipc == nullptr) {
98 LOG_ERROR("%s failed to init UIPC", __func__);
99 return;
100 }
101
102 UIPC_Open(*sco_uipc, UIPC_CH_ID_AV_AUDIO, sco_data_cb, SCO_HOST_DATA_PATH);
103 struct group* grp = getgrnam(SCO_HOST_DATA_GROUP);
104 chmod(SCO_HOST_DATA_PATH, 0770);
105 if (grp) {
106 int res = chown(SCO_HOST_DATA_PATH, -1, grp->gr_gid);
107 if (res == -1) {
108 LOG_ERROR("%s failed: %s", __func__, strerror(errno));
109 }
110 }
111 }
112
cleanup()113 void cleanup() {
114 if (sco_uipc == nullptr) {
115 return;
116 }
117 UIPC_Close(*sco_uipc, UIPC_CH_ID_ALL);
118 sco_uipc = nullptr;
119 }
120
read(uint8_t * p_buf,uint32_t len)121 size_t read(uint8_t* p_buf, uint32_t len) {
122 if (sco_uipc == nullptr) {
123 LOG_WARN("Read from uninitialized or closed UIPC");
124 return 0;
125 }
126 return UIPC_Read(*sco_uipc, UIPC_CH_ID_AV_AUDIO, p_buf, len);
127 }
128
write(const uint8_t * p_buf,uint32_t len)129 size_t write(const uint8_t* p_buf, uint32_t len) {
130 if (sco_uipc == nullptr) {
131 LOG_WARN("Write to uninitialized or closed UIPC");
132 return 0;
133 }
134 return UIPC_Send(*sco_uipc, UIPC_CH_ID_AV_AUDIO, 0, p_buf, len) ? len : 0;
135 }
136
137 namespace wbs {
138
139 /* Second octet of H2 header is composed by 4 bits fixed 0x8 and 4 bits
140 * sequence number 0000, 0011, 1100, 1111. */
141 static const uint8_t btm_h2_header_frames_count[] = {0x08, 0x38, 0xc8, 0xf8};
142
143 /* Supported SCO packet sizes for mSBC. The wideband speech mSBC frame parsing
144 * code ties to limited packet size values. Specifically list them out
145 * to check against when setting packet size. The first entry is the default
146 * value as a fallback. */
147 constexpr size_t btm_wbs_supported_pkt_size[] = {BTM_MSBC_PKT_LEN, 72, 0};
148 /* Buffer size should be set to least common multiple of SCO packet size and
149 * BTM_MSBC_PKT_LEN for optimizing buffer copy. */
150 constexpr size_t btm_wbs_msbc_buffer_size[] = {BTM_MSBC_PKT_LEN, 360, 0};
151
152 /* The pre-computed SCO packet per HFP 1.7 spec. This mSBC packet will be
153 * decoded into all-zero input PCM. */
154 static const uint8_t btm_msbc_zero_packet[] = {
155 0x01, 0x08, /* Mock H2 header */
156 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d, 0xb6, 0xdd,
157 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6,
158 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d,
159 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77,
160 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c,
161 /* End of Audio Samples */
162 0x00 /* A padding byte defined by mSBC */};
163
164 /* Raised Cosine table for OLA */
165 static const float rcos[BTM_PLC_OLAL] = {
166 0.99148655f, 0.96623611f, 0.92510857f, 0.86950446f,
167 0.80131732f, 0.72286918f, 0.63683150f, 0.54613418f,
168 0.45386582f, 0.36316850f, 0.27713082f, 0.19868268f,
169 0.13049554f, 0.07489143f, 0.03376389f, 0.00851345f};
170
f_to_s16(float input)171 static int16_t f_to_s16(float input) {
172 return input > INT16_MAX ? INT16_MAX
173 : input < INT16_MIN ? INT16_MIN
174 : (int16_t)input;
175 }
176 /* This structure tracks the packet loss for last PLC_WINDOW_SIZE of packets */
177 struct tBTM_MSBC_BTM_PLC_WINDOW {
178 bool loss_hist[BTM_PLC_WINDOW_SIZE]; /* The packet loss history of receiving
179 packets.*/
180 unsigned int idx; /* The index of the to be updated packet loss status. */
181 unsigned int count; /* The count of lost packets in the window. */
182
183 public:
update_plc_statebluetooth::audio::sco::wbs::tBTM_MSBC_BTM_PLC_WINDOW184 void update_plc_state(bool is_packet_loss) {
185 bool* curr = &loss_hist[idx];
186 if (is_packet_loss != *curr) {
187 count += (is_packet_loss - *curr);
188 *curr = is_packet_loss;
189 }
190 idx = (idx + 1) % BTM_PLC_WINDOW_SIZE;
191 }
192
is_packet_loss_too_highbluetooth::audio::sco::wbs::tBTM_MSBC_BTM_PLC_WINDOW193 bool is_packet_loss_too_high() {
194 /* The packet loss count comes from a time window and we use it as an
195 * indicator of our confidence of the PLC algorithm. It is known to
196 * generate poorer and robotic feeling sounds, when the majority of
197 * samples in the PLC history buffer are from the concealment results.
198 */
199 return count > BTM_PLC_PL_THRESHOLD;
200 }
201 };
202
203 /* The PLC is specifically designed for mSBC. The algorithm searches the
204 * history of receiving samples to find the best match samples and constructs
205 * substitutions for the lost samples. The selection is based on pattern
206 * matching a template, composed of a length of samples preceding to the lost
207 * samples. It then uses the following samples after the best match as the
208 * replacement samples and applies Overlap-Add to reduce the audible
209 * distortion.
210 *
211 * This structure holds related info needed to conduct the PLC algorithm.
212 */
213 struct tBTM_MSBC_PLC {
214 int16_t hist[BTM_PLC_HL + BTM_MSBC_FS + BTM_PLC_SBCRL +
215 BTM_PLC_OLAL]; /* The history buffer for receiving samples, we
216 also use it to buffer the processed
217 replacement samples */
218 unsigned best_lag; /* The index of the best substitution samples in the
219 sample history */
220 int handled_bad_frames; /* Number of bad frames handled since the last good
221 frame */
222 int16_t decoded_buffer[BTM_MSBC_FS]; /* Used for storing the samples from
223 decoding the mSBC zero frame packet and
224 also constructed frames */
225 tBTM_MSBC_BTM_PLC_WINDOW*
226 pl_window; /* Used to monitor how many packets are bad within the recent
227 BTM_PLC_WINDOW_SIZE of packets. We use this to determine if
228 we want to disable the PLC temporarily */
229
230 int num_decoded_frames; /* Number of total read mSBC frames. */
231 int num_lost_frames; /* Number of total lost mSBC frames. */
232
overlap_addbluetooth::audio::sco::wbs::tBTM_MSBC_PLC233 void overlap_add(int16_t* output, float scaler_d, const int16_t* desc,
234 float scaler_a, const int16_t* asc) {
235 for (int i = 0; i < BTM_PLC_OLAL; i++) {
236 output[i] = f_to_s16(scaler_d * desc[i] * rcos[i] +
237 scaler_a * asc[i] * rcos[BTM_PLC_OLAL - 1 - i]);
238 }
239 }
240
cross_correlationbluetooth::audio::sco::wbs::tBTM_MSBC_PLC241 float cross_correlation(int16_t* x, int16_t* y) {
242 float sum = 0, x2 = 0, y2 = 0;
243
244 for (int i = 0; i < BTM_PLC_TL; i++) {
245 sum += ((float)x[i]) * y[i];
246 x2 += ((float)x[i]) * x[i];
247 y2 += ((float)y[i]) * y[i];
248 }
249 return sum / sqrtf(x2 * y2);
250 }
251
pattern_matchbluetooth::audio::sco::wbs::tBTM_MSBC_PLC252 int pattern_match(int16_t* hist) {
253 int best = 0;
254 float cn, max_cn = FLT_MIN;
255
256 for (int i = 0; i < BTM_PLC_WL; i++) {
257 cn = cross_correlation(&hist[BTM_PLC_HL - BTM_PLC_TL], &hist[i]);
258 if (cn > max_cn) {
259 best = i;
260 max_cn = cn;
261 }
262 }
263 return best;
264 }
265
amplitude_matchbluetooth::audio::sco::wbs::tBTM_MSBC_PLC266 float amplitude_match(int16_t* x, int16_t* y) {
267 uint32_t sum_x = 0, sum_y = 0;
268 float scaler;
269 for (int i = 0; i < BTM_MSBC_FS; i++) {
270 sum_x += abs(x[i]);
271 sum_y += abs(y[i]);
272 }
273
274 if (sum_y == 0) return 1.2f;
275
276 scaler = (float)sum_x / sum_y;
277 return scaler > 1.2f ? 1.2f : scaler < 0.75f ? 0.75f : scaler;
278 }
279
280 public:
initbluetooth::audio::sco::wbs::tBTM_MSBC_PLC281 void init() {
282 if (pl_window) osi_free(pl_window);
283 pl_window = (tBTM_MSBC_BTM_PLC_WINDOW*)osi_calloc(sizeof(*pl_window));
284 }
285
deinitbluetooth::audio::sco::wbs::tBTM_MSBC_PLC286 void deinit() {
287 if (pl_window) osi_free_and_reset((void**)&pl_window);
288 }
289
get_num_decoded_framesbluetooth::audio::sco::wbs::tBTM_MSBC_PLC290 int get_num_decoded_frames() { return num_decoded_frames; }
291
get_num_lost_framesbluetooth::audio::sco::wbs::tBTM_MSBC_PLC292 int get_num_lost_frames() { return num_lost_frames; }
293
handle_bad_framesbluetooth::audio::sco::wbs::tBTM_MSBC_PLC294 void handle_bad_frames(const uint8_t** output) {
295 float scaler;
296 int16_t* best_match_hist;
297 int16_t* frame_head = &hist[BTM_PLC_HL];
298
299 num_decoded_frames++;
300 num_lost_frames++;
301
302 /* mSBC codec is stateful, the history of signal would contribute to the
303 * decode result decoded_buffer. This should never fail. */
304 GetInterfaceToProfiles()->msbcCodec->decodePacket(
305 btm_msbc_zero_packet, decoded_buffer, sizeof(decoded_buffer));
306
307 /* The PLC algorithm is more likely to generate bad results that sound
308 * robotic after severe packet losses happened. Only applying it when
309 * we are confident. */
310 if (!pl_window->is_packet_loss_too_high()) {
311 if (handled_bad_frames == 0) {
312 /* Finds the best matching samples and amplitude */
313 best_lag = pattern_match(hist) + BTM_PLC_TL;
314 best_match_hist = &hist[best_lag];
315 scaler =
316 amplitude_match(&hist[BTM_PLC_HL - BTM_MSBC_FS], best_match_hist);
317
318 /* Constructs the substitution samples */
319 overlap_add(frame_head, 1.0, decoded_buffer, scaler, best_match_hist);
320 for (int i = BTM_PLC_OLAL; i < BTM_MSBC_FS; i++)
321 hist[BTM_PLC_HL + i] = f_to_s16(scaler * best_match_hist[i]);
322 overlap_add(&frame_head[BTM_MSBC_FS], scaler,
323 &best_match_hist[BTM_MSBC_FS], 1.0,
324 &best_match_hist[BTM_MSBC_FS]);
325
326 memmove(&frame_head[BTM_MSBC_FS + BTM_PLC_OLAL],
327 &best_match_hist[BTM_MSBC_FS + BTM_PLC_OLAL],
328 BTM_PLC_SBCRL * BTM_MSBC_SAMPLE_SIZE);
329 } else {
330 /* Using the existing best lag and copy the following frames */
331 memmove(frame_head, &hist[best_lag],
332 (BTM_MSBC_FS + BTM_PLC_SBCRL + BTM_PLC_OLAL) *
333 BTM_MSBC_SAMPLE_SIZE);
334 }
335 /* Copy the constructed frames to decoded buffer for caller to use */
336 std::copy(frame_head, &frame_head[BTM_MSBC_FS], decoded_buffer);
337
338 handled_bad_frames++;
339 } else {
340 /* This is a case similar to receiving a good frame with all zeros, we set
341 * handled_bad_frames to zero to prevent the following good frame from
342 * being concealed to reconverge with the zero frames we fill in. The
343 * concealment result sounds more artificial and weird than simply writing
344 * zeros and following samples.
345 */
346 std::copy(std::begin(decoded_buffer), std::end(decoded_buffer),
347 frame_head);
348 std::fill(&frame_head[BTM_MSBC_FS],
349 &frame_head[BTM_MSBC_FS + BTM_PLC_SBCRL + BTM_PLC_OLAL], 0);
350 /* No need to copy the frames as we'll use the decoded zero frames in the
351 * decoded buffer as our concealment frames */
352
353 handled_bad_frames = 0;
354 }
355
356 *output = (const uint8_t*)decoded_buffer;
357
358 /* Shift the frames to update the history window */
359 memmove(hist, &hist[BTM_MSBC_FS],
360 (BTM_PLC_HL + BTM_PLC_SBCRL + BTM_PLC_OLAL) * BTM_MSBC_SAMPLE_SIZE);
361 pl_window->update_plc_state(1);
362 }
363
handle_good_framesbluetooth::audio::sco::wbs::tBTM_MSBC_PLC364 void handle_good_frames(int16_t* input) {
365 int16_t* frame_head;
366 num_decoded_frames++;
367 if (handled_bad_frames != 0) {
368 /* If there was a packet concealment before this good frame, we need to
369 * reconverge the input frames */
370 frame_head = &hist[BTM_PLC_HL];
371
372 /* For the first good frame after packet loss, we need to conceal the
373 * received samples to have it reconverge with the true output */
374 std::copy(frame_head, &frame_head[BTM_PLC_SBCRL], input);
375 /* Overlap the input frame with the previous output frame */
376 overlap_add(&input[BTM_PLC_SBCRL], 1.0, &frame_head[BTM_PLC_SBCRL], 1.0,
377 &input[BTM_PLC_SBCRL]);
378 handled_bad_frames = 0;
379 }
380
381 /* Shift the history and update the good frame to the end of it */
382 memmove(hist, &hist[BTM_MSBC_FS],
383 (BTM_PLC_HL - BTM_MSBC_FS) * BTM_MSBC_SAMPLE_SIZE);
384 std::copy(input, &input[BTM_MSBC_FS], &hist[BTM_PLC_HL - BTM_MSBC_FS]);
385 pl_window->update_plc_state(0);
386 }
387 };
388
389 /* Define the structure that contains mSBC data */
390 struct tBTM_MSBC_INFO {
391 size_t packet_size; /* SCO mSBC packet size supported by lower layer */
392 size_t buf_size; /* The size of the buffer, determined by the packet_size. */
393
394 uint8_t* msbc_decode_buf; /* Buffer to store mSBC packets to decode */
395 size_t decode_buf_wo; /* Write offset of the decode buffer */
396 size_t decode_buf_ro; /* Read offset of the decode buffer */
397 bool read_corrupted; /* If the current mSBC packet read is corrupted */
398
399 uint8_t* msbc_encode_buf; /* Buffer to store the encoded SCO packets */
400 size_t encode_buf_wo; /* Write offset of the encode buffer */
401 size_t encode_buf_ro; /* Read offset of the encode buffer */
402
403 int16_t decoded_pcm_buf[BTM_MSBC_FS]; /* Buffer to store decoded PCM */
404
405 uint8_t num_encoded_msbc_pkts; /* Number of the encoded mSBC packets */
406
407 tBTM_MSBC_PLC* plc; /* PLC component to handle the packet loss of input */
get_supported_packet_sizebluetooth::audio::sco::wbs::tBTM_MSBC_INFO408 static size_t get_supported_packet_size(size_t pkt_size,
409 size_t* buffer_size) {
410 int i;
411 for (i = 0; btm_wbs_supported_pkt_size[i] != 0 &&
412 btm_wbs_supported_pkt_size[i] != pkt_size;
413 i++)
414 ;
415 /* In case of unsupported value, error log and fallback to
416 * BTM_MSBC_PKT_LEN(60). */
417 if (btm_wbs_supported_pkt_size[i] == 0) {
418 LOG_WARN("Unsupported packet size %lu", (unsigned long)pkt_size);
419 i = 0;
420 }
421
422 if (buffer_size) {
423 *buffer_size = btm_wbs_msbc_buffer_size[i];
424 }
425 return btm_wbs_supported_pkt_size[i];
426 }
427
verify_h2_header_seq_numbluetooth::audio::sco::wbs::tBTM_MSBC_INFO428 bool verify_h2_header_seq_num(const uint8_t num) {
429 for (int i = 0; i < 4; i++) {
430 if (num == btm_h2_header_frames_count[i]) {
431 return true;
432 }
433 }
434 return false;
435 }
436
437 public:
initbluetooth::audio::sco::wbs::tBTM_MSBC_INFO438 size_t init(size_t pkt_size) {
439 decode_buf_wo = 0;
440 decode_buf_ro = 0;
441 encode_buf_wo = 0;
442 encode_buf_ro = 0;
443
444 pkt_size = get_supported_packet_size(pkt_size, &buf_size);
445 if (pkt_size == packet_size) return packet_size;
446 packet_size = pkt_size;
447
448 if (msbc_decode_buf) osi_free(msbc_decode_buf);
449 msbc_decode_buf = (uint8_t*)osi_calloc(buf_size);
450
451 if (msbc_encode_buf) osi_free(msbc_encode_buf);
452 msbc_encode_buf = (uint8_t*)osi_calloc(buf_size);
453
454 if (plc) {
455 plc->deinit();
456 osi_free(plc);
457 }
458 plc = (tBTM_MSBC_PLC*)osi_calloc(sizeof(*plc));
459 plc->init();
460 return packet_size;
461 }
462
deinitbluetooth::audio::sco::wbs::tBTM_MSBC_INFO463 void deinit() {
464 if (msbc_decode_buf) osi_free(msbc_decode_buf);
465 if (msbc_encode_buf) osi_free(msbc_encode_buf);
466 if (plc) {
467 plc->deinit();
468 osi_free_and_reset((void**)&plc);
469 }
470 }
471
decodablebluetooth::audio::sco::wbs::tBTM_MSBC_INFO472 size_t decodable() { return decode_buf_wo - decode_buf_ro; }
473
mark_pkt_decodedbluetooth::audio::sco::wbs::tBTM_MSBC_INFO474 void mark_pkt_decoded() {
475 if (decode_buf_ro + BTM_MSBC_PKT_LEN > decode_buf_wo) {
476 LOG_ERROR("Trying to mark read offset beyond write offset.");
477 return;
478 }
479
480 decode_buf_ro += BTM_MSBC_PKT_LEN;
481 if (decode_buf_ro == decode_buf_wo) {
482 decode_buf_ro = 0;
483 decode_buf_wo = 0;
484 }
485 }
486
writebluetooth::audio::sco::wbs::tBTM_MSBC_INFO487 size_t write(const uint8_t* input, size_t len) {
488 if (len > buf_size - decode_buf_wo) {
489 return 0;
490 }
491
492 std::copy(input, input + len, msbc_decode_buf + decode_buf_wo);
493 decode_buf_wo += len;
494 return len;
495 }
496
find_msbc_pkt_headbluetooth::audio::sco::wbs::tBTM_MSBC_INFO497 const uint8_t* find_msbc_pkt_head() {
498 if (read_corrupted) {
499 LOG_DEBUG("Skip corrupted mSBC packets");
500 read_corrupted = false;
501 return nullptr;
502 }
503
504 size_t rp = 0;
505 while (rp < BTM_MSBC_PKT_LEN &&
506 decode_buf_wo - (decode_buf_ro + rp) >= BTM_MSBC_PKT_LEN) {
507 if ((msbc_decode_buf[decode_buf_ro + rp] != BTM_MSBC_H2_HEADER_0) ||
508 (!verify_h2_header_seq_num(
509 msbc_decode_buf[decode_buf_ro + rp + 1])) ||
510 (msbc_decode_buf[decode_buf_ro + rp + 2] != BTM_MSBC_SYNC_WORD)) {
511 rp++;
512 continue;
513 }
514
515 if (rp != 0) {
516 LOG_WARN("Skipped %lu bytes of mSBC data ahead of a valid mSBC frame",
517 (unsigned long)rp);
518 decode_buf_ro += rp;
519 }
520 return &msbc_decode_buf[decode_buf_ro];
521 }
522
523 return nullptr;
524 }
525
526 /* Fill in the mSBC header and update the buffer's write offset to guard the
527 * buffer space to be written. Return a pointer to the start of mSBC packet's
528 * body for the caller to fill the encoded mSBC data if there is enough space
529 * in the buffer to fill in a new packet, otherwise return a nullptr. */
fill_msbc_pkt_templatebluetooth::audio::sco::wbs::tBTM_MSBC_INFO530 uint8_t* fill_msbc_pkt_template() {
531 uint8_t* wp = &msbc_encode_buf[encode_buf_wo];
532 if (buf_size - encode_buf_wo < BTM_MSBC_PKT_LEN) {
533 LOG_DEBUG("Packet queue can't accommodate more packets.");
534 return nullptr;
535 }
536
537 wp[0] = BTM_MSBC_H2_HEADER_0;
538 wp[1] = btm_h2_header_frames_count[num_encoded_msbc_pkts % 4];
539 encode_buf_wo += BTM_MSBC_PKT_LEN;
540
541 num_encoded_msbc_pkts++;
542 return wp + BTM_MSBC_H2_HEADER_LEN;
543 }
544
mark_pkt_dequeuedbluetooth::audio::sco::wbs::tBTM_MSBC_INFO545 size_t mark_pkt_dequeued() {
546 LOG_DEBUG(
547 "Try to mark an encoded packet dequeued: ro:%lu wo:%lu pkt_size:%lu",
548 (unsigned long)encode_buf_ro, (unsigned long)encode_buf_wo,
549 (unsigned long)packet_size);
550
551 if (encode_buf_wo - encode_buf_ro < packet_size) return 0;
552
553 encode_buf_ro += packet_size;
554 if (encode_buf_ro == encode_buf_wo) {
555 encode_buf_ro = 0;
556 encode_buf_wo = 0;
557 }
558
559 return packet_size;
560 }
561
sco_pkt_read_ptrbluetooth::audio::sco::wbs::tBTM_MSBC_INFO562 const uint8_t* sco_pkt_read_ptr() {
563 if (encode_buf_wo - encode_buf_ro < packet_size) {
564 LOG_DEBUG("Insufficient data as a SCO packet to read.");
565 return nullptr;
566 }
567
568 return &msbc_encode_buf[encode_buf_ro];
569 }
570 };
571
572 static tBTM_MSBC_INFO* msbc_info = nullptr;
573
init(size_t pkt_size)574 size_t init(size_t pkt_size) {
575 GetInterfaceToProfiles()->msbcCodec->initialize();
576
577 if (msbc_info) {
578 LOG_WARN("Re-initiating mSBC buffer that is active or not cleaned");
579 msbc_info->deinit();
580 osi_free(msbc_info);
581 }
582
583 msbc_info = (tBTM_MSBC_INFO*)osi_calloc(sizeof(*msbc_info));
584 return msbc_info->init(pkt_size);
585 }
586
cleanup()587 void cleanup() {
588 GetInterfaceToProfiles()->msbcCodec->cleanup();
589
590 if (msbc_info == nullptr) return;
591
592 msbc_info->deinit();
593 osi_free_and_reset((void**)&msbc_info);
594 }
595
fill_plc_stats(int * num_decoded_frames,double * packet_loss_ratio)596 bool fill_plc_stats(int* num_decoded_frames, double* packet_loss_ratio) {
597 if (msbc_info == NULL || num_decoded_frames == NULL ||
598 packet_loss_ratio == NULL)
599 return false;
600
601 int decoded_frames = msbc_info->plc->get_num_decoded_frames();
602 int lost_frames = msbc_info->plc->get_num_lost_frames();
603 if (decoded_frames <= 0 || lost_frames < 0 || lost_frames > decoded_frames)
604 return false;
605
606 *num_decoded_frames = decoded_frames;
607 *packet_loss_ratio = (double)lost_frames / decoded_frames;
608 return true;
609 }
610
enqueue_packet(const uint8_t * data,size_t pkt_size,bool corrupted)611 size_t enqueue_packet(const uint8_t* data, size_t pkt_size, bool corrupted) {
612 if (msbc_info == nullptr) {
613 LOG_WARN("mSBC buffer uninitialized or cleaned");
614 return 0;
615 }
616
617 if (pkt_size != msbc_info->packet_size) {
618 LOG_WARN(
619 "Ignoring the coming packet with size %lu that is inconsistent with "
620 "the HAL reported packet size %lu",
621 (unsigned long)pkt_size, (unsigned long)msbc_info->packet_size);
622 return 0;
623 }
624
625 if (data == nullptr) {
626 LOG_WARN("Invalid data to enqueue");
627 return 0;
628 }
629
630 msbc_info->read_corrupted |= corrupted;
631 if (msbc_info->write(data, pkt_size) != pkt_size) {
632 LOG_DEBUG("Fail to write packet with size %lu to buffer",
633 (unsigned long)pkt_size);
634 return 0;
635 }
636
637 return pkt_size;
638 }
639
decode(const uint8_t ** out_data)640 size_t decode(const uint8_t** out_data) {
641 const uint8_t* frame_head = nullptr;
642
643 if (msbc_info == nullptr) {
644 LOG_WARN("mSBC buffer uninitialized or cleaned");
645 return 0;
646 }
647
648 if (out_data == nullptr) {
649 LOG_WARN("%s Invalid output pointer", __func__);
650 return 0;
651 }
652
653 if (msbc_info->decodable() < BTM_MSBC_PKT_LEN) {
654 LOG_DEBUG("No complete mSBC packet to decode");
655 return 0;
656 }
657
658 frame_head = msbc_info->find_msbc_pkt_head();
659 if (frame_head == nullptr) {
660 LOG_DEBUG("No valid mSBC packet to decode %lu, %lu",
661 (unsigned long)msbc_info->decode_buf_ro,
662 (unsigned long)msbc_info->decode_buf_wo);
663 /* Done with parsing the raw bytes just read. If we couldn't find a valid
664 * mSBC frame head, we shall treat the existing BTM_MSBC_PKT_LEN length
665 * of mSBC data as a corrupted packet and conduct the PLC. */
666 goto packet_loss;
667 }
668
669 if (!GetInterfaceToProfiles()->msbcCodec->decodePacket(
670 frame_head, msbc_info->decoded_pcm_buf,
671 sizeof(msbc_info->decoded_pcm_buf))) {
672 LOG_DEBUG("Decoding mSBC packet failed");
673 goto packet_loss;
674 }
675
676 msbc_info->plc->handle_good_frames(msbc_info->decoded_pcm_buf);
677 *out_data = (const uint8_t*)msbc_info->decoded_pcm_buf;
678 msbc_info->mark_pkt_decoded();
679 return BTM_MSBC_CODE_SIZE;
680
681 packet_loss:
682 msbc_info->plc->handle_bad_frames(out_data);
683 msbc_info->mark_pkt_decoded();
684 return BTM_MSBC_CODE_SIZE;
685 }
686
encode(int16_t * data,size_t len)687 size_t encode(int16_t* data, size_t len) {
688 uint8_t* pkt_body = nullptr;
689 uint32_t encoded_size = 0;
690 if (msbc_info == nullptr) {
691 LOG_WARN("mSBC buffer uninitialized or cleaned");
692 return 0;
693 }
694
695 if (data == nullptr) {
696 LOG_WARN("Invalid data to encode");
697 return 0;
698 }
699
700 if (len < BTM_MSBC_CODE_SIZE) {
701 LOG_DEBUG(
702 "PCM frames with size %lu is insufficient to be encoded into a mSBC "
703 "packet",
704 (unsigned long)len);
705 return 0;
706 }
707
708 pkt_body = msbc_info->fill_msbc_pkt_template();
709 if (pkt_body == nullptr) {
710 LOG_DEBUG("Failed to fill the template to fill the mSBC packet");
711 return 0;
712 }
713
714 encoded_size =
715 GetInterfaceToProfiles()->msbcCodec->encodePacket(data, pkt_body);
716 if (encoded_size != BTM_MSBC_PKT_FRAME_LEN) {
717 LOG_WARN("Encoding invalid packet size: %lu", (unsigned long)encoded_size);
718 std::copy(&btm_msbc_zero_packet[BTM_MSBC_H2_HEADER_LEN],
719 std::end(btm_msbc_zero_packet), pkt_body);
720 }
721
722 return BTM_MSBC_CODE_SIZE;
723 }
724
dequeue_packet(const uint8_t ** output)725 size_t dequeue_packet(const uint8_t** output) {
726 if (msbc_info == nullptr) {
727 LOG_WARN("mSBC buffer uninitialized or cleaned");
728 return 0;
729 }
730
731 if (output == nullptr) {
732 LOG_WARN("%s Invalid output pointer", __func__);
733 return 0;
734 }
735
736 *output = msbc_info->sco_pkt_read_ptr();
737 if (*output == nullptr) {
738 LOG_DEBUG("Insufficient data to dequeue.");
739 return 0;
740 }
741
742 return msbc_info->mark_pkt_dequeued();
743 }
744
745 } // namespace wbs
746
747 } // namespace sco
748 } // namespace audio
749 } // namespace bluetooth
750