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# -----------------------------------------------------------------------------
18from __future__ import annotations
19
20import dataclasses
21import struct
22import logging
23from collections.abc import AsyncGenerator
24from typing import List, Callable, Awaitable
25
26from .company_ids import COMPANY_IDENTIFIERS
27from .sdp import (
28    DataElement,
29    ServiceAttribute,
30    SDP_PUBLIC_BROWSE_ROOT,
31    SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
32    SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
33    SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
34    SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
35    SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
36)
37from .core import (
38    BT_L2CAP_PROTOCOL_ID,
39    BT_AUDIO_SOURCE_SERVICE,
40    BT_AUDIO_SINK_SERVICE,
41    BT_AVDTP_PROTOCOL_ID,
42    BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE,
43    name_or_number,
44)
45
46
47# -----------------------------------------------------------------------------
48# Logging
49# -----------------------------------------------------------------------------
50logger = logging.getLogger(__name__)
51
52
53# -----------------------------------------------------------------------------
54# Constants
55# -----------------------------------------------------------------------------
56# fmt: off
57
58A2DP_SBC_CODEC_TYPE            = 0x00
59A2DP_MPEG_1_2_AUDIO_CODEC_TYPE = 0x01
60A2DP_MPEG_2_4_AAC_CODEC_TYPE   = 0x02
61A2DP_ATRAC_FAMILY_CODEC_TYPE   = 0x03
62A2DP_NON_A2DP_CODEC_TYPE       = 0xFF
63
64A2DP_CODEC_TYPE_NAMES = {
65    A2DP_SBC_CODEC_TYPE:            'A2DP_SBC_CODEC_TYPE',
66    A2DP_MPEG_1_2_AUDIO_CODEC_TYPE: 'A2DP_MPEG_1_2_AUDIO_CODEC_TYPE',
67    A2DP_MPEG_2_4_AAC_CODEC_TYPE:   'A2DP_MPEG_2_4_AAC_CODEC_TYPE',
68    A2DP_ATRAC_FAMILY_CODEC_TYPE:   'A2DP_ATRAC_FAMILY_CODEC_TYPE',
69    A2DP_NON_A2DP_CODEC_TYPE:       'A2DP_NON_A2DP_CODEC_TYPE'
70}
71
72
73SBC_SYNC_WORD = 0x9C
74
75SBC_SAMPLING_FREQUENCIES = [
76    16000,
77    22050,
78    44100,
79    48000
80]
81
82SBC_MONO_CHANNEL_MODE         = 0x00
83SBC_DUAL_CHANNEL_MODE         = 0x01
84SBC_STEREO_CHANNEL_MODE       = 0x02
85SBC_JOINT_STEREO_CHANNEL_MODE = 0x03
86
87SBC_CHANNEL_MODE_NAMES = {
88    SBC_MONO_CHANNEL_MODE:         'SBC_MONO_CHANNEL_MODE',
89    SBC_DUAL_CHANNEL_MODE:         'SBC_DUAL_CHANNEL_MODE',
90    SBC_STEREO_CHANNEL_MODE:       'SBC_STEREO_CHANNEL_MODE',
91    SBC_JOINT_STEREO_CHANNEL_MODE: 'SBC_JOINT_STEREO_CHANNEL_MODE'
92}
93
94SBC_BLOCK_LENGTHS = [4, 8, 12, 16]
95
96SBC_SUBBANDS = [4, 8]
97
98SBC_SNR_ALLOCATION_METHOD      = 0x00
99SBC_LOUDNESS_ALLOCATION_METHOD = 0x01
100
101SBC_ALLOCATION_METHOD_NAMES = {
102    SBC_SNR_ALLOCATION_METHOD:      'SBC_SNR_ALLOCATION_METHOD',
103    SBC_LOUDNESS_ALLOCATION_METHOD: 'SBC_LOUDNESS_ALLOCATION_METHOD'
104}
105
106MPEG_2_4_AAC_SAMPLING_FREQUENCIES = [
107    8000,
108    11025,
109    12000,
110    16000,
111    22050,
112    24000,
113    32000,
114    44100,
115    48000,
116    64000,
117    88200,
118    96000
119]
120
121MPEG_2_AAC_LC_OBJECT_TYPE       = 0x00
122MPEG_4_AAC_LC_OBJECT_TYPE       = 0x01
123MPEG_4_AAC_LTP_OBJECT_TYPE      = 0x02
124MPEG_4_AAC_SCALABLE_OBJECT_TYPE = 0x03
125
126MPEG_2_4_OBJECT_TYPE_NAMES = {
127    MPEG_2_AAC_LC_OBJECT_TYPE:       'MPEG_2_AAC_LC_OBJECT_TYPE',
128    MPEG_4_AAC_LC_OBJECT_TYPE:       'MPEG_4_AAC_LC_OBJECT_TYPE',
129    MPEG_4_AAC_LTP_OBJECT_TYPE:      'MPEG_4_AAC_LTP_OBJECT_TYPE',
130    MPEG_4_AAC_SCALABLE_OBJECT_TYPE: 'MPEG_4_AAC_SCALABLE_OBJECT_TYPE'
131}
132
133# fmt: on
134
135
136# -----------------------------------------------------------------------------
137def flags_to_list(flags, values):
138    result = []
139    for i, value in enumerate(values):
140        if flags & (1 << (len(values) - i - 1)):
141            result.append(value)
142    return result
143
144
145# -----------------------------------------------------------------------------
146def make_audio_source_service_sdp_records(service_record_handle, version=(1, 3)):
147    # pylint: disable=import-outside-toplevel
148    from .avdtp import AVDTP_PSM
149
150    version_int = version[0] << 8 | version[1]
151    return [
152        ServiceAttribute(
153            SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
154            DataElement.unsigned_integer_32(service_record_handle),
155        ),
156        ServiceAttribute(
157            SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
158            DataElement.sequence([DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)]),
159        ),
160        ServiceAttribute(
161            SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
162            DataElement.sequence([DataElement.uuid(BT_AUDIO_SOURCE_SERVICE)]),
163        ),
164        ServiceAttribute(
165            SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
166            DataElement.sequence(
167                [
168                    DataElement.sequence(
169                        [
170                            DataElement.uuid(BT_L2CAP_PROTOCOL_ID),
171                            DataElement.unsigned_integer_16(AVDTP_PSM),
172                        ]
173                    ),
174                    DataElement.sequence(
175                        [
176                            DataElement.uuid(BT_AVDTP_PROTOCOL_ID),
177                            DataElement.unsigned_integer_16(version_int),
178                        ]
179                    ),
180                ]
181            ),
182        ),
183        ServiceAttribute(
184            SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
185            DataElement.sequence(
186                [
187                    DataElement.sequence(
188                        [
189                            DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE),
190                            DataElement.unsigned_integer_16(version_int),
191                        ]
192                    )
193                ]
194            ),
195        ),
196    ]
197
198
199# -----------------------------------------------------------------------------
200def make_audio_sink_service_sdp_records(service_record_handle, version=(1, 3)):
201    # pylint: disable=import-outside-toplevel
202    from .avdtp import AVDTP_PSM
203
204    version_int = version[0] << 8 | version[1]
205    return [
206        ServiceAttribute(
207            SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
208            DataElement.unsigned_integer_32(service_record_handle),
209        ),
210        ServiceAttribute(
211            SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
212            DataElement.sequence([DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)]),
213        ),
214        ServiceAttribute(
215            SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
216            DataElement.sequence([DataElement.uuid(BT_AUDIO_SINK_SERVICE)]),
217        ),
218        ServiceAttribute(
219            SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
220            DataElement.sequence(
221                [
222                    DataElement.sequence(
223                        [
224                            DataElement.uuid(BT_L2CAP_PROTOCOL_ID),
225                            DataElement.unsigned_integer_16(AVDTP_PSM),
226                        ]
227                    ),
228                    DataElement.sequence(
229                        [
230                            DataElement.uuid(BT_AVDTP_PROTOCOL_ID),
231                            DataElement.unsigned_integer_16(version_int),
232                        ]
233                    ),
234                ]
235            ),
236        ),
237        ServiceAttribute(
238            SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
239            DataElement.sequence(
240                [
241                    DataElement.sequence(
242                        [
243                            DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE),
244                            DataElement.unsigned_integer_16(version_int),
245                        ]
246                    )
247                ]
248            ),
249        ),
250    ]
251
252
253# -----------------------------------------------------------------------------
254@dataclasses.dataclass
255class SbcMediaCodecInformation:
256    '''
257    A2DP spec - 4.3.2 Codec Specific Information Elements
258    '''
259
260    sampling_frequency: int
261    channel_mode: int
262    block_length: int
263    subbands: int
264    allocation_method: int
265    minimum_bitpool_value: int
266    maximum_bitpool_value: int
267
268    SAMPLING_FREQUENCY_BITS = {16000: 1 << 3, 32000: 1 << 2, 44100: 1 << 1, 48000: 1}
269    CHANNEL_MODE_BITS = {
270        SBC_MONO_CHANNEL_MODE: 1 << 3,
271        SBC_DUAL_CHANNEL_MODE: 1 << 2,
272        SBC_STEREO_CHANNEL_MODE: 1 << 1,
273        SBC_JOINT_STEREO_CHANNEL_MODE: 1,
274    }
275    BLOCK_LENGTH_BITS = {4: 1 << 3, 8: 1 << 2, 12: 1 << 1, 16: 1}
276    SUBBANDS_BITS = {4: 1 << 1, 8: 1}
277    ALLOCATION_METHOD_BITS = {
278        SBC_SNR_ALLOCATION_METHOD: 1 << 1,
279        SBC_LOUDNESS_ALLOCATION_METHOD: 1,
280    }
281
282    @staticmethod
283    def from_bytes(data: bytes) -> SbcMediaCodecInformation:
284        sampling_frequency = (data[0] >> 4) & 0x0F
285        channel_mode = (data[0] >> 0) & 0x0F
286        block_length = (data[1] >> 4) & 0x0F
287        subbands = (data[1] >> 2) & 0x03
288        allocation_method = (data[1] >> 0) & 0x03
289        minimum_bitpool_value = (data[2] >> 0) & 0xFF
290        maximum_bitpool_value = (data[3] >> 0) & 0xFF
291        return SbcMediaCodecInformation(
292            sampling_frequency,
293            channel_mode,
294            block_length,
295            subbands,
296            allocation_method,
297            minimum_bitpool_value,
298            maximum_bitpool_value,
299        )
300
301    @classmethod
302    def from_discrete_values(
303        cls,
304        sampling_frequency: int,
305        channel_mode: int,
306        block_length: int,
307        subbands: int,
308        allocation_method: int,
309        minimum_bitpool_value: int,
310        maximum_bitpool_value: int,
311    ) -> SbcMediaCodecInformation:
312        return SbcMediaCodecInformation(
313            sampling_frequency=cls.SAMPLING_FREQUENCY_BITS[sampling_frequency],
314            channel_mode=cls.CHANNEL_MODE_BITS[channel_mode],
315            block_length=cls.BLOCK_LENGTH_BITS[block_length],
316            subbands=cls.SUBBANDS_BITS[subbands],
317            allocation_method=cls.ALLOCATION_METHOD_BITS[allocation_method],
318            minimum_bitpool_value=minimum_bitpool_value,
319            maximum_bitpool_value=maximum_bitpool_value,
320        )
321
322    @classmethod
323    def from_lists(
324        cls,
325        sampling_frequencies: List[int],
326        channel_modes: List[int],
327        block_lengths: List[int],
328        subbands: List[int],
329        allocation_methods: List[int],
330        minimum_bitpool_value: int,
331        maximum_bitpool_value: int,
332    ) -> SbcMediaCodecInformation:
333        return SbcMediaCodecInformation(
334            sampling_frequency=sum(
335                cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies
336            ),
337            channel_mode=sum(cls.CHANNEL_MODE_BITS[x] for x in channel_modes),
338            block_length=sum(cls.BLOCK_LENGTH_BITS[x] for x in block_lengths),
339            subbands=sum(cls.SUBBANDS_BITS[x] for x in subbands),
340            allocation_method=sum(
341                cls.ALLOCATION_METHOD_BITS[x] for x in allocation_methods
342            ),
343            minimum_bitpool_value=minimum_bitpool_value,
344            maximum_bitpool_value=maximum_bitpool_value,
345        )
346
347    def __bytes__(self) -> bytes:
348        return bytes(
349            [
350                (self.sampling_frequency << 4) | self.channel_mode,
351                (self.block_length << 4)
352                | (self.subbands << 2)
353                | self.allocation_method,
354                self.minimum_bitpool_value,
355                self.maximum_bitpool_value,
356            ]
357        )
358
359    def __str__(self) -> str:
360        channel_modes = ['MONO', 'DUAL_CHANNEL', 'STEREO', 'JOINT_STEREO']
361        allocation_methods = ['SNR', 'Loudness']
362        return '\n'.join(
363            # pylint: disable=line-too-long
364            [
365                'SbcMediaCodecInformation(',
366                f'  sampling_frequency:    {",".join([str(x) for x in flags_to_list(self.sampling_frequency, SBC_SAMPLING_FREQUENCIES)])}',
367                f'  channel_mode:          {",".join([str(x) for x in flags_to_list(self.channel_mode, channel_modes)])}',
368                f'  block_length:          {",".join([str(x) for x in flags_to_list(self.block_length, SBC_BLOCK_LENGTHS)])}',
369                f'  subbands:              {",".join([str(x) for x in flags_to_list(self.subbands, SBC_SUBBANDS)])}',
370                f'  allocation_method:     {",".join([str(x) for x in flags_to_list(self.allocation_method, allocation_methods)])}',
371                f'  minimum_bitpool_value: {self.minimum_bitpool_value}',
372                f'  maximum_bitpool_value: {self.maximum_bitpool_value}' ')',
373            ]
374        )
375
376
377# -----------------------------------------------------------------------------
378@dataclasses.dataclass
379class AacMediaCodecInformation:
380    '''
381    A2DP spec - 4.5.2 Codec Specific Information Elements
382    '''
383
384    object_type: int
385    sampling_frequency: int
386    channels: int
387    rfa: int
388    vbr: int
389    bitrate: int
390
391    OBJECT_TYPE_BITS = {
392        MPEG_2_AAC_LC_OBJECT_TYPE: 1 << 7,
393        MPEG_4_AAC_LC_OBJECT_TYPE: 1 << 6,
394        MPEG_4_AAC_LTP_OBJECT_TYPE: 1 << 5,
395        MPEG_4_AAC_SCALABLE_OBJECT_TYPE: 1 << 4,
396    }
397    SAMPLING_FREQUENCY_BITS = {
398        8000: 1 << 11,
399        11025: 1 << 10,
400        12000: 1 << 9,
401        16000: 1 << 8,
402        22050: 1 << 7,
403        24000: 1 << 6,
404        32000: 1 << 5,
405        44100: 1 << 4,
406        48000: 1 << 3,
407        64000: 1 << 2,
408        88200: 1 << 1,
409        96000: 1,
410    }
411    CHANNELS_BITS = {1: 1 << 1, 2: 1}
412
413    @staticmethod
414    def from_bytes(data: bytes) -> AacMediaCodecInformation:
415        object_type = data[0]
416        sampling_frequency = (data[1] << 4) | ((data[2] >> 4) & 0x0F)
417        channels = (data[2] >> 2) & 0x03
418        rfa = 0
419        vbr = (data[3] >> 7) & 0x01
420        bitrate = ((data[3] & 0x7F) << 16) | (data[4] << 8) | data[5]
421        return AacMediaCodecInformation(
422            object_type, sampling_frequency, channels, rfa, vbr, bitrate
423        )
424
425    @classmethod
426    def from_discrete_values(
427        cls,
428        object_type: int,
429        sampling_frequency: int,
430        channels: int,
431        vbr: int,
432        bitrate: int,
433    ) -> AacMediaCodecInformation:
434        return AacMediaCodecInformation(
435            object_type=cls.OBJECT_TYPE_BITS[object_type],
436            sampling_frequency=cls.SAMPLING_FREQUENCY_BITS[sampling_frequency],
437            channels=cls.CHANNELS_BITS[channels],
438            rfa=0,
439            vbr=vbr,
440            bitrate=bitrate,
441        )
442
443    @classmethod
444    def from_lists(
445        cls,
446        object_types: List[int],
447        sampling_frequencies: List[int],
448        channels: List[int],
449        vbr: int,
450        bitrate: int,
451    ) -> AacMediaCodecInformation:
452        return AacMediaCodecInformation(
453            object_type=sum(cls.OBJECT_TYPE_BITS[x] for x in object_types),
454            sampling_frequency=sum(
455                cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies
456            ),
457            channels=sum(cls.CHANNELS_BITS[x] for x in channels),
458            rfa=0,
459            vbr=vbr,
460            bitrate=bitrate,
461        )
462
463    def __bytes__(self) -> bytes:
464        return bytes(
465            [
466                self.object_type & 0xFF,
467                (self.sampling_frequency >> 4) & 0xFF,
468                (((self.sampling_frequency & 0x0F) << 4) | (self.channels << 2)) & 0xFF,
469                ((self.vbr << 7) | ((self.bitrate >> 16) & 0x7F)) & 0xFF,
470                ((self.bitrate >> 8) & 0xFF) & 0xFF,
471                self.bitrate & 0xFF,
472            ]
473        )
474
475    def __str__(self) -> str:
476        object_types = [
477            'MPEG_2_AAC_LC',
478            'MPEG_4_AAC_LC',
479            'MPEG_4_AAC_LTP',
480            'MPEG_4_AAC_SCALABLE',
481            '[4]',
482            '[5]',
483            '[6]',
484            '[7]',
485        ]
486        channels = [1, 2]
487        # pylint: disable=line-too-long
488        return '\n'.join(
489            [
490                'AacMediaCodecInformation(',
491                f'  object_type:        {",".join([str(x) for x in flags_to_list(self.object_type, object_types)])}',
492                f'  sampling_frequency: {",".join([str(x) for x in flags_to_list(self.sampling_frequency, MPEG_2_4_AAC_SAMPLING_FREQUENCIES)])}',
493                f'  channels:           {",".join([str(x) for x in flags_to_list(self.channels, channels)])}',
494                f'  vbr:                {self.vbr}',
495                f'  bitrate:            {self.bitrate}' ')',
496            ]
497        )
498
499
500@dataclasses.dataclass
501# -----------------------------------------------------------------------------
502class VendorSpecificMediaCodecInformation:
503    '''
504    A2DP spec - 4.7.2 Codec Specific Information Elements
505    '''
506
507    vendor_id: int
508    codec_id: int
509    value: bytes
510
511    @staticmethod
512    def from_bytes(data: bytes) -> VendorSpecificMediaCodecInformation:
513        (vendor_id, codec_id) = struct.unpack_from('<IH', data, 0)
514        return VendorSpecificMediaCodecInformation(vendor_id, codec_id, data[6:])
515
516    def __bytes__(self) -> bytes:
517        return struct.pack('<IH', self.vendor_id, self.codec_id, self.value)
518
519    def __str__(self) -> str:
520        # pylint: disable=line-too-long
521        return '\n'.join(
522            [
523                'VendorSpecificMediaCodecInformation(',
524                f'  vendor_id: {self.vendor_id:08X} ({name_or_number(COMPANY_IDENTIFIERS, self.vendor_id & 0xFFFF)})',
525                f'  codec_id:  {self.codec_id:04X}',
526                f'  value:     {self.value.hex()}' ')',
527            ]
528        )
529
530
531# -----------------------------------------------------------------------------
532@dataclasses.dataclass
533class SbcFrame:
534    sampling_frequency: int
535    block_count: int
536    channel_mode: int
537    subband_count: int
538    payload: bytes
539
540    @property
541    def sample_count(self) -> int:
542        return self.subband_count * self.block_count
543
544    @property
545    def bitrate(self) -> int:
546        return 8 * ((len(self.payload) * self.sampling_frequency) // self.sample_count)
547
548    @property
549    def duration(self) -> float:
550        return self.sample_count / self.sampling_frequency
551
552    def __str__(self) -> str:
553        return (
554            f'SBC(sf={self.sampling_frequency},'
555            f'cm={self.channel_mode},'
556            f'br={self.bitrate},'
557            f'sc={self.sample_count},'
558            f'size={len(self.payload)})'
559        )
560
561
562# -----------------------------------------------------------------------------
563class SbcParser:
564    def __init__(self, read: Callable[[int], Awaitable[bytes]]) -> None:
565        self.read = read
566
567    @property
568    def frames(self) -> AsyncGenerator[SbcFrame, None]:
569        async def generate_frames() -> AsyncGenerator[SbcFrame, None]:
570            while True:
571                # Read 4 bytes of header
572                header = await self.read(4)
573                if len(header) != 4:
574                    return
575
576                # Check the sync word
577                if header[0] != SBC_SYNC_WORD:
578                    logger.debug('invalid sync word')
579                    return
580
581                # Extract some of the header fields
582                sampling_frequency = SBC_SAMPLING_FREQUENCIES[(header[1] >> 6) & 3]
583                blocks = 4 * (1 + ((header[1] >> 4) & 3))
584                channel_mode = (header[1] >> 2) & 3
585                channels = 1 if channel_mode == SBC_MONO_CHANNEL_MODE else 2
586                subbands = 8 if ((header[1]) & 1) else 4
587                bitpool = header[2]
588
589                # Compute the frame length
590                frame_length = 4 + (4 * subbands * channels) // 8
591                if channel_mode in (SBC_MONO_CHANNEL_MODE, SBC_DUAL_CHANNEL_MODE):
592                    frame_length += (blocks * channels * bitpool) // 8
593                else:
594                    frame_length += (
595                        (1 if channel_mode == SBC_JOINT_STEREO_CHANNEL_MODE else 0)
596                        * subbands
597                        + blocks * bitpool
598                    ) // 8
599
600                # Read the rest of the frame
601                payload = header + await self.read(frame_length - 4)
602
603                # Emit the next frame
604                yield SbcFrame(
605                    sampling_frequency, blocks, channel_mode, subbands, payload
606                )
607
608        return generate_frames()
609
610
611# -----------------------------------------------------------------------------
612class SbcPacketSource:
613    def __init__(
614        self, read: Callable[[int], Awaitable[bytes]], mtu: int, codec_capabilities
615    ) -> None:
616        self.read = read
617        self.mtu = mtu
618        self.codec_capabilities = codec_capabilities
619
620    @property
621    def packets(self):
622        async def generate_packets():
623            # pylint: disable=import-outside-toplevel
624            from .avdtp import MediaPacket  # Import here to avoid a circular reference
625
626            sequence_number = 0
627            timestamp = 0
628            frames = []
629            frames_size = 0
630            max_rtp_payload = self.mtu - 12 - 1
631
632            # NOTE: this doesn't support frame fragments
633            sbc_parser = SbcParser(self.read)
634            async for frame in sbc_parser.frames:
635                print(frame)
636
637                if (
638                    frames_size + len(frame.payload) > max_rtp_payload
639                    or len(frames) == 16
640                ):
641                    # Need to flush what has been accumulated so far
642
643                    # Emit a packet
644                    sbc_payload = bytes([len(frames)]) + b''.join(
645                        [frame.payload for frame in frames]
646                    )
647                    packet = MediaPacket(
648                        2, 0, 0, 0, sequence_number, timestamp, 0, [], 96, sbc_payload
649                    )
650                    packet.timestamp_seconds = timestamp / frame.sampling_frequency
651                    yield packet
652
653                    # Prepare for next packets
654                    sequence_number += 1
655                    sequence_number &= 0xFFFF
656                    timestamp += sum((frame.sample_count for frame in frames))
657                    timestamp &= 0xFFFFFFFF
658                    frames = [frame]
659                    frames_size = len(frame.payload)
660                else:
661                    # Accumulate
662                    frames.append(frame)
663                    frames_size += len(frame.payload)
664
665        return generate_packets()
666