1# Copyright 2021-2022 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# ----------------------------------------------------------------------------- 16# Imports 17# ----------------------------------------------------------------------------- 18import struct 19import bitstruct 20import logging 21from collections import namedtuple 22from colors import color 23 24from .company_ids import COMPANY_IDENTIFIERS 25from .sdp import ( 26 DataElement, 27 ServiceAttribute, 28 SDP_PUBLIC_BROWSE_ROOT, 29 SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, 30 SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, 31 SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, 32 SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, 33 SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID 34) 35from .core import ( 36 BT_L2CAP_PROTOCOL_ID, 37 BT_AUDIO_SOURCE_SERVICE, 38 BT_AUDIO_SINK_SERVICE, 39 BT_AVDTP_PROTOCOL_ID, 40 BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE, 41 name_or_number 42) 43 44 45# ----------------------------------------------------------------------------- 46# Logging 47# ----------------------------------------------------------------------------- 48logger = logging.getLogger(__name__) 49 50 51# ----------------------------------------------------------------------------- 52# Constants 53# ----------------------------------------------------------------------------- 54 55A2DP_SBC_CODEC_TYPE = 0x00 56A2DP_MPEG_1_2_AUDIO_CODEC_TYPE = 0x01 57A2DP_MPEG_2_4_AAC_CODEC_TYPE = 0x02 58A2DP_ATRAC_FAMILY_CODEC_TYPE = 0x03 59A2DP_NON_A2DP_CODEC_TYPE = 0xFF 60 61A2DP_CODEC_TYPE_NAMES = { 62 A2DP_SBC_CODEC_TYPE: 'A2DP_SBC_CODEC_TYPE', 63 A2DP_MPEG_1_2_AUDIO_CODEC_TYPE: 'A2DP_MPEG_1_2_AUDIO_CODEC_TYPE', 64 A2DP_MPEG_2_4_AAC_CODEC_TYPE: 'A2DP_MPEG_2_4_AAC_CODEC_TYPE', 65 A2DP_ATRAC_FAMILY_CODEC_TYPE: 'A2DP_ATRAC_FAMILY_CODEC_TYPE', 66 A2DP_NON_A2DP_CODEC_TYPE: 'A2DP_NON_A2DP_CODEC_TYPE' 67} 68 69 70SBC_SYNC_WORD = 0x9C 71 72SBC_SAMPLING_FREQUENCIES = [ 73 16000, 74 22050, 75 44100, 76 48000 77] 78 79SBC_MONO_CHANNEL_MODE = 0x00 80SBC_DUAL_CHANNEL_MODE = 0x01 81SBC_STEREO_CHANNEL_MODE = 0x02 82SBC_JOINT_STEREO_CHANNEL_MODE = 0x03 83 84SBC_CHANNEL_MODE_NAMES = { 85 SBC_MONO_CHANNEL_MODE: 'SBC_MONO_CHANNEL_MODE', 86 SBC_DUAL_CHANNEL_MODE: 'SBC_DUAL_CHANNEL_MODE', 87 SBC_STEREO_CHANNEL_MODE: 'SBC_STEREO_CHANNEL_MODE', 88 SBC_JOINT_STEREO_CHANNEL_MODE: 'SBC_JOINT_STEREO_CHANNEL_MODE' 89} 90 91SBC_BLOCK_LENGTHS = [4, 8, 12, 16] 92 93SBC_SUBBANDS = [4, 8] 94 95SBC_SNR_ALLOCATION_METHOD = 0x00 96SBC_LOUDNESS_ALLOCATION_METHOD = 0x01 97 98SBC_ALLOCATION_METHOD_NAMES = { 99 SBC_SNR_ALLOCATION_METHOD: 'SBC_SNR_ALLOCATION_METHOD', 100 SBC_LOUDNESS_ALLOCATION_METHOD: 'SBC_LOUDNESS_ALLOCATION_METHOD' 101} 102 103MPEG_2_4_AAC_SAMPLING_FREQUENCIES = [ 104 8000, 105 11025, 106 12000, 107 16000, 108 22050, 109 24000, 110 32000, 111 44100, 112 48000, 113 64000, 114 88200, 115 96000 116] 117 118MPEG_2_AAC_LC_OBJECT_TYPE = 0x00 119MPEG_4_AAC_LC_OBJECT_TYPE = 0x01 120MPEG_4_AAC_LTP_OBJECT_TYPE = 0x02 121MPEG_4_AAC_SCALABLE_OBJECT_TYPE = 0x03 122 123MPEG_2_4_OBJECT_TYPE_NAMES = { 124 MPEG_2_AAC_LC_OBJECT_TYPE: 'MPEG_2_AAC_LC_OBJECT_TYPE', 125 MPEG_4_AAC_LC_OBJECT_TYPE: 'MPEG_4_AAC_LC_OBJECT_TYPE', 126 MPEG_4_AAC_LTP_OBJECT_TYPE: 'MPEG_4_AAC_LTP_OBJECT_TYPE', 127 MPEG_4_AAC_SCALABLE_OBJECT_TYPE: 'MPEG_4_AAC_SCALABLE_OBJECT_TYPE' 128} 129 130 131# ----------------------------------------------------------------------------- 132def flags_to_list(flags, values): 133 result = [] 134 for i in range(len(values)): 135 if flags & (1 << (len(values) - i - 1)): 136 result.append(values[i]) 137 return result 138 139 140# ----------------------------------------------------------------------------- 141def make_audio_source_service_sdp_records(service_record_handle, version=(1, 3)): 142 from .avdtp import AVDTP_PSM 143 version_int = version[0] << 8 | version[1] 144 return [ 145 ServiceAttribute(SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, DataElement.unsigned_integer_32(service_record_handle)), 146 ServiceAttribute(SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, DataElement.sequence([ 147 DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT) 148 ])), 149 ServiceAttribute(SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, DataElement.sequence([ 150 DataElement.uuid(BT_AUDIO_SOURCE_SERVICE) 151 ])), 152 ServiceAttribute(SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([ 153 DataElement.sequence([ 154 DataElement.uuid(BT_L2CAP_PROTOCOL_ID), 155 DataElement.unsigned_integer_16(AVDTP_PSM) 156 ]), 157 DataElement.sequence([ 158 DataElement.uuid(BT_AVDTP_PROTOCOL_ID), 159 DataElement.unsigned_integer_16(version_int) 160 ]) 161 ])), 162 ServiceAttribute(SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([ 163 DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE), 164 DataElement.unsigned_integer_16(version_int) 165 ])), 166 ] 167 168 169# ----------------------------------------------------------------------------- 170def make_audio_sink_service_sdp_records(service_record_handle, version=(1, 3)): 171 from .avdtp import AVDTP_PSM 172 version_int = version[0] << 8 | version[1] 173 return [ 174 ServiceAttribute(SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, DataElement.unsigned_integer_32(service_record_handle)), 175 ServiceAttribute(SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, DataElement.sequence([ 176 DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT) 177 ])), 178 ServiceAttribute(SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, DataElement.sequence([ 179 DataElement.uuid(BT_AUDIO_SINK_SERVICE) 180 ])), 181 ServiceAttribute(SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([ 182 DataElement.sequence([ 183 DataElement.uuid(BT_L2CAP_PROTOCOL_ID), 184 DataElement.unsigned_integer_16(AVDTP_PSM) 185 ]), 186 DataElement.sequence([ 187 DataElement.uuid(BT_AVDTP_PROTOCOL_ID), 188 DataElement.unsigned_integer_16(version_int) 189 ]) 190 ])), 191 ServiceAttribute(SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([ 192 DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE), 193 DataElement.unsigned_integer_16(version_int) 194 ])), 195 ] 196 197 198# ----------------------------------------------------------------------------- 199class SbcMediaCodecInformation( 200 namedtuple( 201 'SbcMediaCodecInformation', 202 [ 203 'sampling_frequency', 204 'channel_mode', 205 'block_length', 206 'subbands', 207 'allocation_method', 208 'minimum_bitpool_value', 209 'maximum_bitpool_value' 210 ] 211 ) 212): 213 ''' 214 A2DP spec - 4.3.2 Codec Specific Information Elements 215 ''' 216 217 BIT_FIELDS = 'u4u4u4u2u2u8u8' 218 SAMPLING_FREQUENCY_BITS = { 219 16000: 1 << 3, 220 32000: 1 << 2, 221 44100: 1 << 1, 222 48000: 1 223 } 224 CHANNEL_MODE_BITS = { 225 SBC_MONO_CHANNEL_MODE: 1 << 3, 226 SBC_DUAL_CHANNEL_MODE: 1 << 2, 227 SBC_STEREO_CHANNEL_MODE: 1 << 1, 228 SBC_JOINT_STEREO_CHANNEL_MODE: 1 229 } 230 BLOCK_LENGTH_BITS = { 231 4: 1 << 3, 232 8: 1 << 2, 233 12: 1 << 1, 234 16: 1 235 } 236 SUBBANDS_BITS = { 237 4: 1 << 1, 238 8: 1 239 } 240 ALLOCATION_METHOD_BITS = { 241 SBC_SNR_ALLOCATION_METHOD: 1 << 1, 242 SBC_LOUDNESS_ALLOCATION_METHOD: 1 243 } 244 245 @staticmethod 246 def from_bytes(data): 247 return SbcMediaCodecInformation(*bitstruct.unpack(SbcMediaCodecInformation.BIT_FIELDS, data)) 248 249 @classmethod 250 def from_discrete_values( 251 cls, 252 sampling_frequency, 253 channel_mode, 254 block_length, 255 subbands, 256 allocation_method, 257 minimum_bitpool_value, 258 maximum_bitpool_value 259 ): 260 return SbcMediaCodecInformation( 261 sampling_frequency = cls.SAMPLING_FREQUENCY_BITS[sampling_frequency], 262 channel_mode = cls.CHANNEL_MODE_BITS[channel_mode], 263 block_length = cls.BLOCK_LENGTH_BITS[block_length], 264 subbands = cls.SUBBANDS_BITS[subbands], 265 allocation_method = cls.ALLOCATION_METHOD_BITS[allocation_method], 266 minimum_bitpool_value = minimum_bitpool_value, 267 maximum_bitpool_value = maximum_bitpool_value 268 ) 269 270 @classmethod 271 def from_lists( 272 cls, 273 sampling_frequencies, 274 channel_modes, 275 block_lengths, 276 subbands, 277 allocation_methods, 278 minimum_bitpool_value, 279 maximum_bitpool_value 280 ): 281 return SbcMediaCodecInformation( 282 sampling_frequency = sum(cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies), 283 channel_mode = sum(cls.CHANNEL_MODE_BITS[x] for x in channel_modes), 284 block_length = sum(cls.BLOCK_LENGTH_BITS[x] for x in block_lengths), 285 subbands = sum(cls.SUBBANDS_BITS[x] for x in subbands), 286 allocation_method = sum(cls.ALLOCATION_METHOD_BITS[x] for x in allocation_methods), 287 minimum_bitpool_value = minimum_bitpool_value, 288 maximum_bitpool_value = maximum_bitpool_value 289 ) 290 291 def __bytes__(self): 292 return bitstruct.pack(self.BIT_FIELDS, *self) 293 294 def __str__(self): 295 channel_modes = ['MONO', 'DUAL_CHANNEL', 'STEREO', 'JOINT_STEREO'] 296 allocation_methods = ['SNR', 'Loudness'] 297 return '\n'.join([ 298 'SbcMediaCodecInformation(', 299 f' sampling_frequency: {",".join([str(x) for x in flags_to_list(self.sampling_frequency, SBC_SAMPLING_FREQUENCIES)])}', 300 f' channel_mode: {",".join([str(x) for x in flags_to_list(self.channel_mode, channel_modes)])}', 301 f' block_length: {",".join([str(x) for x in flags_to_list(self.block_length, SBC_BLOCK_LENGTHS)])}', 302 f' subbands: {",".join([str(x) for x in flags_to_list(self.subbands, SBC_SUBBANDS)])}', 303 f' allocation_method: {",".join([str(x) for x in flags_to_list(self.allocation_method, allocation_methods)])}', 304 f' minimum_bitpool_value: {self.minimum_bitpool_value}', 305 f' maximum_bitpool_value: {self.maximum_bitpool_value}' 306 ')' 307 ]) 308 309 310# ----------------------------------------------------------------------------- 311class AacMediaCodecInformation( 312 namedtuple( 313 'AacMediaCodecInformation', 314 [ 315 'object_type', 316 'sampling_frequency', 317 'channels', 318 'vbr', 319 'bitrate' 320 ] 321 ) 322): 323 ''' 324 A2DP spec - 4.5.2 Codec Specific Information Elements 325 ''' 326 327 BIT_FIELDS = 'u8u12u2p2u1u23' 328 OBJECT_TYPE_BITS = { 329 MPEG_2_AAC_LC_OBJECT_TYPE: 1 << 7, 330 MPEG_4_AAC_LC_OBJECT_TYPE: 1 << 6, 331 MPEG_4_AAC_LTP_OBJECT_TYPE: 1 << 5, 332 MPEG_4_AAC_SCALABLE_OBJECT_TYPE: 1 << 4 333 } 334 SAMPLING_FREQUENCY_BITS = { 335 8000: 1 << 11, 336 11025: 1 << 10, 337 12000: 1 << 9, 338 16000: 1 << 8, 339 22050: 1 << 7, 340 24000: 1 << 6, 341 32000: 1 << 5, 342 44100: 1 << 4, 343 48000: 1 << 3, 344 64000: 1 << 2, 345 88200: 1 << 1, 346 96000: 1 347 } 348 CHANNELS_BITS = { 349 1: 1 << 1, 350 2: 1 351 } 352 353 @staticmethod 354 def from_bytes(data): 355 return AacMediaCodecInformation(*bitstruct.unpack(AacMediaCodecInformation.BIT_FIELDS, data)) 356 357 @classmethod 358 def from_discrete_values( 359 cls, 360 object_type, 361 sampling_frequency, 362 channels, 363 vbr, 364 bitrate 365 ): 366 return AacMediaCodecInformation( 367 object_type = cls.OBJECT_TYPE_BITS[object_type], 368 sampling_frequency = cls.SAMPLING_FREQUENCY_BITS[sampling_frequency], 369 channels = cls.CHANNELS_BITS[channels], 370 vbr = vbr, 371 bitrate = bitrate 372 ) 373 374 @classmethod 375 def from_lists( 376 cls, 377 object_types, 378 sampling_frequencies, 379 channels, 380 vbr, 381 bitrate 382 ): 383 return AacMediaCodecInformation( 384 object_type = sum(cls.OBJECT_TYPE_BITS[x] for x in object_types), 385 sampling_frequency = sum(cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies), 386 channels = sum(cls.CHANNELS_BITS[x] for x in channels), 387 vbr = vbr, 388 bitrate = bitrate 389 ) 390 391 def __bytes__(self): 392 return bitstruct.pack(self.BIT_FIELDS, *self) 393 394 def __str__(self): 395 object_types = ['MPEG_2_AAC_LC', 'MPEG_4_AAC_LC', 'MPEG_4_AAC_LTP', 'MPEG_4_AAC_SCALABLE', '[4]', '[5]', '[6]', '[7]'] 396 channels = [1, 2] 397 return '\n'.join([ 398 'AacMediaCodecInformation(', 399 f' object_type: {",".join([str(x) for x in flags_to_list(self.object_type, object_types)])}', 400 f' sampling_frequency: {",".join([str(x) for x in flags_to_list(self.sampling_frequency, MPEG_2_4_AAC_SAMPLING_FREQUENCIES)])}', 401 f' channels: {",".join([str(x) for x in flags_to_list(self.channels, channels)])}', 402 f' vbr: {self.vbr}', 403 f' bitrate: {self.bitrate}' 404 ')' 405 ]) 406 407 408# ----------------------------------------------------------------------------- 409class VendorSpecificMediaCodecInformation: 410 ''' 411 A2DP spec - 4.7.2 Codec Specific Information Elements 412 ''' 413 414 @staticmethod 415 def from_bytes(data): 416 (vendor_id, codec_id) = struct.unpack_from('<IH', data, 0) 417 return VendorSpecificMediaCodecInformation(vendor_id, codec_id, data[6:]) 418 419 def __init__(self, vendor_id, codec_id, value): 420 self.vendor_id = vendor_id 421 self.codec_id = codec_id 422 self.value = value 423 424 def __bytes__(self): 425 return struct.pack('<IH', self.vendor_id, self.codec_id, self.value) 426 427 def __str__(self): 428 return '\n'.join([ 429 'VendorSpecificMediaCodecInformation(', 430 f' vendor_id: {self.vendor_id:08X} ({name_or_number(COMPANY_IDENTIFIERS, self.vendor_id & 0xFFFF)})', 431 f' codec_id: {self.codec_id:04X}', 432 f' value: {self.value.hex()}' 433 ')' 434 ]) 435 436 437# ----------------------------------------------------------------------------- 438class SbcFrame: 439 def __init__( 440 self, 441 sampling_frequency, 442 block_count, 443 channel_mode, 444 subband_count, 445 payload 446 ): 447 self.sampling_frequency = sampling_frequency 448 self.block_count = block_count 449 self.channel_mode = channel_mode 450 self.subband_count = subband_count 451 self.payload = payload 452 453 @property 454 def sample_count(self): 455 return self.subband_count * self.block_count 456 457 @property 458 def bitrate(self): 459 return 8 * ((len(self.payload) * self.sampling_frequency) // self.sample_count) 460 461 @property 462 def duration(self): 463 return self.sample_count / self.sampling_frequency 464 465 def __str__(self): 466 return f'SBC(sf={self.sampling_frequency},cm={self.channel_mode},br={self.bitrate},sc={self.sample_count},size={len(self.payload)})' 467 468 469# ----------------------------------------------------------------------------- 470class SbcParser: 471 def __init__(self, read): 472 self.read = read 473 474 @property 475 def frames(self): 476 async def generate_frames(): 477 while True: 478 # Read 4 bytes of header 479 header = await self.read(4) 480 if len(header) != 4: 481 return 482 483 # Check the sync word 484 if header[0] != SBC_SYNC_WORD: 485 logger.debug('invalid sync word') 486 return 487 488 # Extract some of the header fields 489 sampling_frequency = SBC_SAMPLING_FREQUENCIES[(header[1] >> 6) & 3] 490 blocks = 4 * (1 + ((header[1] >> 4) & 3)) 491 channel_mode = (header[1] >> 2) & 3 492 channels = 1 if channel_mode == SBC_MONO_CHANNEL_MODE else 2 493 subbands = 8 if ((header[1]) & 1) else 4 494 bitpool = header[2] 495 496 # Compute the frame length 497 frame_length = 4 + (4 * subbands * channels) // 8 498 if channel_mode in (SBC_MONO_CHANNEL_MODE, SBC_DUAL_CHANNEL_MODE): 499 frame_length += (blocks * channels * bitpool) // 8 500 else: 501 frame_length += ((1 if channel_mode == SBC_JOINT_STEREO_CHANNEL_MODE else 0) * subbands + blocks * bitpool) // 8 502 503 # Read the rest of the frame 504 payload = header + await self.read(frame_length - 4) 505 506 # Emit the next frame 507 yield SbcFrame(sampling_frequency, blocks, channel_mode, subbands, payload) 508 509 return generate_frames() 510 511 512# ----------------------------------------------------------------------------- 513class SbcPacketSource: 514 def __init__(self, read, mtu, codec_capabilities): 515 self.read = read 516 self.mtu = mtu 517 self.codec_capabilities = codec_capabilities 518 519 @property 520 def packets(self): 521 async def generate_packets(): 522 from .avdtp import MediaPacket # Import here to avoid a circular reference 523 524 sequence_number = 0 525 timestamp = 0 526 frames = [] 527 frames_size = 0 528 max_rtp_payload = self.mtu - 12 - 1 529 530 # NOTE: this doesn't support frame fragments 531 sbc_parser = SbcParser(self.read) 532 async for frame in sbc_parser.frames: 533 print(frame) 534 535 if frames_size + len(frame.payload) > max_rtp_payload or len(frames) == 16: 536 # Need to flush what has been accumulated so far 537 538 # Emit a packet 539 sbc_payload = bytes([len(frames)]) + b''.join([frame.payload for frame in frames]) 540 packet = MediaPacket(2, 0, 0, 0, sequence_number, timestamp, 0, [], 96, sbc_payload) 541 packet.timestamp_seconds = timestamp / frame.sampling_frequency 542 yield packet 543 544 # Prepare for next packets 545 sequence_number += 1 546 timestamp += sum([frame.sample_count for frame in frames]) 547 frames = [frame] 548 frames_size = len(frame.payload) 549 else: 550 # Accumulate 551 frames.append(frame) 552 frames_size += len(frame.payload) 553 554 return generate_packets() 555