bumble 0.0.154__py3-none-any.whl → 0.0.156__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bumble/apps/unbond.py CHANGED
@@ -22,40 +22,58 @@ import click
22
22
 
23
23
  from bumble.device import Device
24
24
  from bumble.keys import JsonKeyStore
25
-
25
+ from bumble.transport import open_transport
26
26
 
27
27
  # -----------------------------------------------------------------------------
28
- async def unbond(keystore_file, device_config, address):
29
- # Create a device to manage the host
30
- device = Device.from_config_file(device_config)
28
+ async def unbond_with_keystore(keystore, address):
29
+ if address is None:
30
+ return await keystore.print()
31
+
32
+ try:
33
+ await keystore.delete(address)
34
+ except KeyError:
35
+ print('!!! pairing not found')
31
36
 
32
- # Get all entries in the keystore
37
+
38
+ # -----------------------------------------------------------------------------
39
+ async def unbond(keystore_file, device_config, hci_transport, address):
40
+ # With a keystore file, we can instantiate the keystore directly
33
41
  if keystore_file:
34
- keystore = JsonKeyStore(None, keystore_file)
35
- else:
36
- keystore = device.keystore
42
+ return await unbond_with_keystore(JsonKeyStore(None, keystore_file), address)
37
43
 
38
- if keystore is None:
39
- print('no keystore')
40
- return
44
+ # Without a keystore file, we need to obtain the keystore from the device
45
+ async with await open_transport(hci_transport) as (hci_source, hci_sink):
46
+ # Create a device to manage the host
47
+ device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink)
41
48
 
42
- if address is None:
43
- await keystore.print()
44
- else:
45
- try:
46
- await keystore.delete(address)
47
- except KeyError:
48
- print('!!! pairing not found')
49
+ # Power-on the device to ensure we have a key store
50
+ await device.power_on()
51
+
52
+ return await unbond_with_keystore(device.keystore, address)
49
53
 
50
54
 
51
55
  # -----------------------------------------------------------------------------
52
56
  @click.command()
53
- @click.option('--keystore-file', help='File in which to store the pairing keys')
54
- @click.argument('device-config')
57
+ @click.option('--keystore-file', help='File in which the pairing keys are stored')
58
+ @click.option('--hci-transport', help='HCI transport for the controller')
59
+ @click.argument('device-config', required=False)
55
60
  @click.argument('address', required=False)
56
- def main(keystore_file, device_config, address):
61
+ def main(keystore_file, hci_transport, device_config, address):
62
+ """
63
+ Remove pairing keys for a device, given its address.
64
+
65
+ If no keystore file is specified, the --hci-transport option must be used to
66
+ connect to a controller, so that the keystore for that controller can be
67
+ instantiated.
68
+ If no address is passed, the existing pairing keys for all addresses are printed.
69
+ """
57
70
  logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
58
- asyncio.run(unbond(keystore_file, device_config, address))
71
+
72
+ if not keystore_file and not hci_transport:
73
+ print('either --keystore-file or --hci-transport must be specified.')
74
+ return
75
+
76
+ asyncio.run(unbond(keystore_file, device_config, hci_transport, address))
59
77
 
60
78
 
61
79
  # -----------------------------------------------------------------------------
bumble/avdtp.py CHANGED
@@ -1207,7 +1207,7 @@ class DelayReport_Reject(Simple_Reject):
1207
1207
 
1208
1208
 
1209
1209
  # -----------------------------------------------------------------------------
1210
- class Protocol:
1210
+ class Protocol(EventEmitter):
1211
1211
  SINGLE_PACKET = 0
1212
1212
  START_PACKET = 1
1213
1213
  CONTINUE_PACKET = 2
@@ -1234,6 +1234,7 @@ class Protocol:
1234
1234
  return protocol
1235
1235
 
1236
1236
  def __init__(self, l2cap_channel, version=(1, 3)):
1237
+ super().__init__()
1237
1238
  self.l2cap_channel = l2cap_channel
1238
1239
  self.version = version
1239
1240
  self.rtx_sig_timer = AVDTP_DEFAULT_RTX_SIG_TIMER
@@ -1250,6 +1251,7 @@ class Protocol:
1250
1251
  # Register to receive PDUs from the channel
1251
1252
  l2cap_channel.sink = self.on_pdu
1252
1253
  l2cap_channel.on('open', self.on_l2cap_channel_open)
1254
+ l2cap_channel.on('close', self.on_l2cap_channel_close)
1253
1255
 
1254
1256
  def get_local_endpoint_by_seid(self, seid):
1255
1257
  if 0 < seid <= len(self.local_endpoints):
@@ -1392,11 +1394,18 @@ class Protocol:
1392
1394
 
1393
1395
  def on_l2cap_connection(self, channel):
1394
1396
  # Forward the channel to the endpoint that's expecting it
1395
- if self.channel_acceptor:
1396
- self.channel_acceptor.on_l2cap_connection(channel)
1397
+ if self.channel_acceptor is None:
1398
+ logger.warning(color('!!! l2cap connection with no acceptor', 'red'))
1399
+ return
1400
+ self.channel_acceptor.on_l2cap_connection(channel)
1397
1401
 
1398
1402
  def on_l2cap_channel_open(self):
1399
1403
  logger.debug(color('<<< L2CAP channel open', 'magenta'))
1404
+ self.emit('open')
1405
+
1406
+ def on_l2cap_channel_close(self):
1407
+ logger.debug(color('<<< L2CAP channel close', 'magenta'))
1408
+ self.emit('close')
1400
1409
 
1401
1410
  def send_message(self, transaction_label, message):
1402
1411
  logger.debug(
@@ -1651,6 +1660,10 @@ class Listener(EventEmitter):
1651
1660
  def set_server(self, connection, server):
1652
1661
  self.servers[connection.handle] = server
1653
1662
 
1663
+ def remove_server(self, connection):
1664
+ if connection.handle in self.servers:
1665
+ del self.servers[connection.handle]
1666
+
1654
1667
  def __init__(self, registrar, version=(1, 3)):
1655
1668
  super().__init__()
1656
1669
  self.version = version
@@ -1669,11 +1682,17 @@ class Listener(EventEmitter):
1669
1682
  else:
1670
1683
  # This is a new command/response channel
1671
1684
  def on_channel_open():
1685
+ logger.debug('setting up new Protocol for the connection')
1672
1686
  server = Protocol(channel, self.version)
1673
1687
  self.set_server(channel.connection, server)
1674
1688
  self.emit('connection', server)
1675
1689
 
1690
+ def on_channel_close():
1691
+ logger.debug('removing Protocol for the connection')
1692
+ self.remove_server(channel.connection)
1693
+
1676
1694
  channel.on('open', on_channel_open)
1695
+ channel.on('close', on_channel_close)
1677
1696
 
1678
1697
 
1679
1698
  # -----------------------------------------------------------------------------
@@ -1967,11 +1986,12 @@ class DiscoveredStreamEndPoint(StreamEndPoint, StreamEndPointProxy):
1967
1986
 
1968
1987
 
1969
1988
  # -----------------------------------------------------------------------------
1970
- class LocalStreamEndPoint(StreamEndPoint):
1989
+ class LocalStreamEndPoint(StreamEndPoint, EventEmitter):
1971
1990
  def __init__(
1972
1991
  self, protocol, seid, media_type, tsep, capabilities, configuration=None
1973
1992
  ):
1974
- super().__init__(seid, media_type, tsep, 0, capabilities)
1993
+ StreamEndPoint.__init__(self, seid, media_type, tsep, 0, capabilities)
1994
+ EventEmitter.__init__(self)
1975
1995
  self.protocol = protocol
1976
1996
  self.configuration = configuration if configuration is not None else []
1977
1997
  self.stream = None
@@ -1988,40 +2008,47 @@ class LocalStreamEndPoint(StreamEndPoint):
1988
2008
  def on_reconfigure_command(self, command):
1989
2009
  pass
1990
2010
 
2011
+ def on_set_configuration_command(self, configuration):
2012
+ logger.debug(
2013
+ '<<< received configuration: '
2014
+ f'{",".join([str(capability) for capability in configuration])}'
2015
+ )
2016
+ self.configuration = configuration
2017
+ self.emit('configuration')
2018
+
1991
2019
  def on_get_configuration_command(self):
1992
2020
  return Get_Configuration_Response(self.configuration)
1993
2021
 
1994
2022
  def on_open_command(self):
1995
- pass
2023
+ self.emit('open')
1996
2024
 
1997
2025
  def on_start_command(self):
1998
- pass
2026
+ self.emit('start')
1999
2027
 
2000
2028
  def on_suspend_command(self):
2001
- pass
2029
+ self.emit('suspend')
2002
2030
 
2003
2031
  def on_close_command(self):
2004
- pass
2032
+ self.emit('close')
2005
2033
 
2006
2034
  def on_abort_command(self):
2007
- pass
2035
+ self.emit('abort')
2008
2036
 
2009
2037
  def on_rtp_channel_open(self):
2010
- pass
2038
+ self.emit('rtp_channel_open')
2011
2039
 
2012
2040
  def on_rtp_channel_close(self):
2013
- pass
2041
+ self.emit('rtp_channel_close')
2014
2042
 
2015
2043
 
2016
2044
  # -----------------------------------------------------------------------------
2017
- class LocalSource(LocalStreamEndPoint, EventEmitter):
2045
+ class LocalSource(LocalStreamEndPoint):
2018
2046
  def __init__(self, protocol, seid, codec_capabilities, packet_pump):
2019
2047
  capabilities = [
2020
2048
  ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY),
2021
2049
  codec_capabilities,
2022
2050
  ]
2023
- LocalStreamEndPoint.__init__(
2024
- self,
2051
+ super().__init__(
2025
2052
  protocol,
2026
2053
  seid,
2027
2054
  codec_capabilities.media_type,
@@ -2029,14 +2056,13 @@ class LocalSource(LocalStreamEndPoint, EventEmitter):
2029
2056
  capabilities,
2030
2057
  capabilities,
2031
2058
  )
2032
- EventEmitter.__init__(self)
2033
2059
  self.packet_pump = packet_pump
2034
2060
 
2035
2061
  async def start(self):
2036
2062
  if self.packet_pump:
2037
2063
  return await self.packet_pump.start(self.stream.rtp_channel)
2038
2064
 
2039
- self.emit('start', self.stream.rtp_channel)
2065
+ self.emit('start')
2040
2066
 
2041
2067
  async def stop(self):
2042
2068
  if self.packet_pump:
@@ -2044,11 +2070,6 @@ class LocalSource(LocalStreamEndPoint, EventEmitter):
2044
2070
 
2045
2071
  self.emit('stop')
2046
2072
 
2047
- def on_set_configuration_command(self, configuration):
2048
- # For now, blindly accept the configuration
2049
- logger.debug(f'<<< received source configuration: {configuration}')
2050
- self.configuration = configuration
2051
-
2052
2073
  def on_start_command(self):
2053
2074
  asyncio.create_task(self.start())
2054
2075
 
@@ -2057,30 +2078,28 @@ class LocalSource(LocalStreamEndPoint, EventEmitter):
2057
2078
 
2058
2079
 
2059
2080
  # -----------------------------------------------------------------------------
2060
- class LocalSink(LocalStreamEndPoint, EventEmitter):
2081
+ class LocalSink(LocalStreamEndPoint):
2061
2082
  def __init__(self, protocol, seid, codec_capabilities):
2062
2083
  capabilities = [
2063
2084
  ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY),
2064
2085
  codec_capabilities,
2065
2086
  ]
2066
- LocalStreamEndPoint.__init__(
2067
- self,
2087
+ super().__init__(
2068
2088
  protocol,
2069
2089
  seid,
2070
2090
  codec_capabilities.media_type,
2071
2091
  AVDTP_TSEP_SNK,
2072
2092
  capabilities,
2073
2093
  )
2074
- EventEmitter.__init__(self)
2075
-
2076
- def on_set_configuration_command(self, configuration):
2077
- # For now, blindly accept the configuration
2078
- logger.debug(f'<<< received sink configuration: {configuration}')
2079
- self.configuration = configuration
2080
2094
 
2081
2095
  def on_rtp_channel_open(self):
2082
2096
  logger.debug(color('<<< RTP channel open', 'magenta'))
2083
2097
  self.stream.rtp_channel.sink = self.on_avdtp_packet
2098
+ super().on_rtp_channel_open()
2099
+
2100
+ def on_rtp_channel_close(self):
2101
+ logger.debug(color('<<< RTP channel close', 'magenta'))
2102
+ super().on_rtp_channel_close()
2084
2103
 
2085
2104
  def on_avdtp_packet(self, packet):
2086
2105
  rtp_packet = MediaPacket.from_bytes(packet)
bumble/codecs.py ADDED
@@ -0,0 +1,381 @@
1
+ # Copyright 2023 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
+ # -----------------------------------------------------------------------------
18
+ from __future__ import annotations
19
+ from dataclasses import dataclass
20
+
21
+
22
+ # -----------------------------------------------------------------------------
23
+ class BitReader:
24
+ """Simple but not optimized bit stream reader."""
25
+
26
+ data: bytes
27
+ bytes_position: int
28
+ bit_position: int
29
+ cache: int
30
+ bits_cached: int
31
+
32
+ def __init__(self, data: bytes):
33
+ self.data = data
34
+ self.byte_position = 0
35
+ self.bit_position = 0
36
+ self.cache = 0
37
+ self.bits_cached = 0
38
+
39
+ def read(self, bits: int) -> int:
40
+ """ "Read up to 32 bits."""
41
+
42
+ if bits > 32:
43
+ raise ValueError('maximum read size is 32')
44
+
45
+ if self.bits_cached >= bits:
46
+ # We have enough bits.
47
+ self.bits_cached -= bits
48
+ self.bit_position += bits
49
+ return (self.cache >> self.bits_cached) & ((1 << bits) - 1)
50
+
51
+ # Read more cache, up to 32 bits
52
+ feed_bytes = self.data[self.byte_position : self.byte_position + 4]
53
+ feed_size = len(feed_bytes)
54
+ feed_int = int.from_bytes(feed_bytes, byteorder='big')
55
+ if 8 * feed_size + self.bits_cached < bits:
56
+ raise ValueError('trying to read past the data')
57
+ self.byte_position += feed_size
58
+
59
+ # Combine the new cache and the old cache
60
+ cache = self.cache & ((1 << self.bits_cached) - 1)
61
+ new_bits = bits - self.bits_cached
62
+ self.bits_cached = 8 * feed_size - new_bits
63
+ result = (feed_int >> self.bits_cached) | (cache << new_bits)
64
+ self.cache = feed_int
65
+
66
+ self.bit_position += bits
67
+ return result
68
+
69
+ def read_bytes(self, count: int):
70
+ if self.bit_position + 8 * count > 8 * len(self.data):
71
+ raise ValueError('not enough data')
72
+
73
+ if self.bit_position % 8:
74
+ # Not byte aligned
75
+ result = bytearray(count)
76
+ for i in range(count):
77
+ result[i] = self.read(8)
78
+ return bytes(result)
79
+
80
+ # Byte aligned
81
+ self.byte_position = self.bit_position // 8
82
+ self.bits_cached = 0
83
+ self.cache = 0
84
+ offset = self.bit_position // 8
85
+ self.bit_position += 8 * count
86
+ return self.data[offset : offset + count]
87
+
88
+ def bits_left(self) -> int:
89
+ return (8 * len(self.data)) - self.bit_position
90
+
91
+ def skip(self, bits: int) -> None:
92
+ # Slow, but simple...
93
+ while bits:
94
+ if bits > 32:
95
+ self.read(32)
96
+ bits -= 32
97
+ else:
98
+ self.read(bits)
99
+ break
100
+
101
+
102
+ # -----------------------------------------------------------------------------
103
+ class AacAudioRtpPacket:
104
+ """AAC payload encapsulated in an RTP packet payload"""
105
+
106
+ @staticmethod
107
+ def latm_value(reader: BitReader) -> int:
108
+ bytes_for_value = reader.read(2)
109
+ value = 0
110
+ for _ in range(bytes_for_value + 1):
111
+ value = value * 256 + reader.read(8)
112
+ return value
113
+
114
+ @staticmethod
115
+ def program_config_element(reader: BitReader):
116
+ raise ValueError('program_config_element not supported')
117
+
118
+ @dataclass
119
+ class GASpecificConfig:
120
+ def __init__(
121
+ self, reader: BitReader, channel_configuration: int, audio_object_type: int
122
+ ) -> None:
123
+ # GASpecificConfig - ISO/EIC 14496-3 Table 4.1
124
+ frame_length_flag = reader.read(1)
125
+ depends_on_core_coder = reader.read(1)
126
+ if depends_on_core_coder:
127
+ self.core_coder_delay = reader.read(14)
128
+ extension_flag = reader.read(1)
129
+ if not channel_configuration:
130
+ AacAudioRtpPacket.program_config_element(reader)
131
+ if audio_object_type in (6, 20):
132
+ self.layer_nr = reader.read(3)
133
+ if extension_flag:
134
+ if audio_object_type == 22:
135
+ num_of_sub_frame = reader.read(5)
136
+ layer_length = reader.read(11)
137
+ if audio_object_type in (17, 19, 20, 23):
138
+ aac_section_data_resilience_flags = reader.read(1)
139
+ aac_scale_factor_data_resilience_flags = reader.read(1)
140
+ aac_spectral_data_resilience_flags = reader.read(1)
141
+ extension_flag_3 = reader.read(1)
142
+ if extension_flag_3 == 1:
143
+ raise ValueError('extensionFlag3 == 1 not supported')
144
+
145
+ @staticmethod
146
+ def audio_object_type(reader: BitReader):
147
+ # GetAudioObjectType - ISO/EIC 14496-3 Table 1.16
148
+ audio_object_type = reader.read(5)
149
+ if audio_object_type == 31:
150
+ audio_object_type = 32 + reader.read(6)
151
+
152
+ return audio_object_type
153
+
154
+ @dataclass
155
+ class AudioSpecificConfig:
156
+ audio_object_type: int
157
+ sampling_frequency_index: int
158
+ sampling_frequency: int
159
+ channel_configuration: int
160
+ sbr_present_flag: int
161
+ ps_present_flag: int
162
+ extension_audio_object_type: int
163
+ extension_sampling_frequency_index: int
164
+ extension_sampling_frequency: int
165
+ extension_channel_configuration: int
166
+
167
+ SAMPLING_FREQUENCIES = [
168
+ 96000,
169
+ 88200,
170
+ 64000,
171
+ 48000,
172
+ 44100,
173
+ 32000,
174
+ 24000,
175
+ 22050,
176
+ 16000,
177
+ 12000,
178
+ 11025,
179
+ 8000,
180
+ 7350,
181
+ ]
182
+
183
+ def __init__(self, reader: BitReader) -> None:
184
+ # AudioSpecificConfig - ISO/EIC 14496-3 Table 1.15
185
+ self.audio_object_type = AacAudioRtpPacket.audio_object_type(reader)
186
+ self.sampling_frequency_index = reader.read(4)
187
+ if self.sampling_frequency_index == 0xF:
188
+ self.sampling_frequency = reader.read(24)
189
+ else:
190
+ self.sampling_frequency = self.SAMPLING_FREQUENCIES[
191
+ self.sampling_frequency_index
192
+ ]
193
+ self.channel_configuration = reader.read(4)
194
+ self.sbr_present_flag = -1
195
+ self.ps_present_flag = -1
196
+ if self.audio_object_type in (5, 29):
197
+ self.extension_audio_object_type = 5
198
+ self.sbc_present_flag = 1
199
+ if self.audio_object_type == 29:
200
+ self.ps_present_flag = 1
201
+ self.extension_sampling_frequency_index = reader.read(4)
202
+ if self.extension_sampling_frequency_index == 0xF:
203
+ self.extension_sampling_frequency = reader.read(24)
204
+ else:
205
+ self.extension_sampling_frequency = self.SAMPLING_FREQUENCIES[
206
+ self.extension_sampling_frequency_index
207
+ ]
208
+ self.audio_object_type = AacAudioRtpPacket.audio_object_type(reader)
209
+ if self.audio_object_type == 22:
210
+ self.extension_channel_configuration = reader.read(4)
211
+ else:
212
+ self.extension_audio_object_type = 0
213
+
214
+ if self.audio_object_type in (1, 2, 3, 4, 6, 7, 17, 19, 20, 21, 22, 23):
215
+ ga_specific_config = AacAudioRtpPacket.GASpecificConfig(
216
+ reader, self.channel_configuration, self.audio_object_type
217
+ )
218
+ else:
219
+ raise ValueError(
220
+ f'audioObjectType {self.audio_object_type} not supported'
221
+ )
222
+
223
+ # if self.extension_audio_object_type != 5 and bits_to_decode >= 16:
224
+ # sync_extension_type = reader.read(11)
225
+ # if sync_extension_type == 0x2B7:
226
+ # self.extension_audio_object_type = AacAudioRtpPacket.audio_object_type(reader)
227
+ # if self.extension_audio_object_type == 5:
228
+ # self.sbr_present_flag = reader.read(1)
229
+ # if self.sbr_present_flag:
230
+ # self.extension_sampling_frequency_index = reader.read(4)
231
+ # if self.extension_sampling_frequency_index == 0xF:
232
+ # self.extension_sampling_frequency = reader.read(24)
233
+ # else:
234
+ # self.extension_sampling_frequency = self.SAMPLING_FREQUENCIES[self.extension_sampling_frequency_index]
235
+ # if bits_to_decode >= 12:
236
+ # sync_extension_type = reader.read(11)
237
+ # if sync_extension_type == 0x548:
238
+ # self.ps_present_flag = reader.read(1)
239
+ # elif self.extension_audio_object_type == 22:
240
+ # self.sbr_present_flag = reader.read(1)
241
+ # if self.sbr_present_flag:
242
+ # self.extension_sampling_frequency_index = reader.read(4)
243
+ # if self.extension_sampling_frequency_index == 0xF:
244
+ # self.extension_sampling_frequency = reader.read(24)
245
+ # else:
246
+ # self.extension_sampling_frequency = self.SAMPLING_FREQUENCIES[self.extension_sampling_frequency_index]
247
+ # self.extension_channel_configuration = reader.read(4)
248
+
249
+ @dataclass
250
+ class StreamMuxConfig:
251
+ other_data_present: int
252
+ other_data_len_bits: int
253
+ audio_specific_config: AacAudioRtpPacket.AudioSpecificConfig
254
+
255
+ def __init__(self, reader: BitReader) -> None:
256
+ # StreamMuxConfig - ISO/EIC 14496-3 Table 1.42
257
+ audio_mux_version = reader.read(1)
258
+ if audio_mux_version == 1:
259
+ audio_mux_version_a = reader.read(1)
260
+ else:
261
+ audio_mux_version_a = 0
262
+ if audio_mux_version_a != 0:
263
+ raise ValueError('audioMuxVersionA != 0 not supported')
264
+ if audio_mux_version == 1:
265
+ tara_buffer_fullness = AacAudioRtpPacket.latm_value(reader)
266
+ stream_cnt = 0
267
+ all_streams_same_time_framing = reader.read(1)
268
+ num_sub_frames = reader.read(6)
269
+ num_program = reader.read(4)
270
+ if num_program != 0:
271
+ raise ValueError('num_program != 0 not supported')
272
+ num_layer = reader.read(3)
273
+ if num_layer != 0:
274
+ raise ValueError('num_layer != 0 not supported')
275
+ if audio_mux_version == 0:
276
+ self.audio_specific_config = AacAudioRtpPacket.AudioSpecificConfig(
277
+ reader
278
+ )
279
+ else:
280
+ asc_len = AacAudioRtpPacket.latm_value(reader)
281
+ marker = reader.bit_position
282
+ self.audio_specific_config = AacAudioRtpPacket.AudioSpecificConfig(
283
+ reader
284
+ )
285
+ audio_specific_config_len = reader.bit_position - marker
286
+ if asc_len < audio_specific_config_len:
287
+ raise ValueError('audio_specific_config_len > asc_len')
288
+ asc_len -= audio_specific_config_len
289
+ reader.skip(asc_len)
290
+ frame_length_type = reader.read(3)
291
+ if frame_length_type == 0:
292
+ latm_buffer_fullness = reader.read(8)
293
+ elif frame_length_type == 1:
294
+ frame_length = reader.read(9)
295
+ else:
296
+ raise ValueError(f'frame_length_type {frame_length_type} not supported')
297
+
298
+ self.other_data_present = reader.read(1)
299
+ if self.other_data_present:
300
+ if audio_mux_version == 1:
301
+ self.other_data_len_bits = AacAudioRtpPacket.latm_value(reader)
302
+ else:
303
+ self.other_data_len_bits = 0
304
+ while True:
305
+ self.other_data_len_bits *= 256
306
+ other_data_len_esc = reader.read(1)
307
+ self.other_data_len_bits += reader.read(8)
308
+ if other_data_len_esc == 0:
309
+ break
310
+ crc_check_present = reader.read(1)
311
+ if crc_check_present:
312
+ crc_checksum = reader.read(8)
313
+
314
+ @dataclass
315
+ class AudioMuxElement:
316
+ payload: bytes
317
+ stream_mux_config: AacAudioRtpPacket.StreamMuxConfig
318
+
319
+ def __init__(self, reader: BitReader, mux_config_present: int):
320
+ if mux_config_present == 0:
321
+ raise ValueError('muxConfigPresent == 0 not supported')
322
+
323
+ # AudioMuxElement - ISO/EIC 14496-3 Table 1.41
324
+ use_same_stream_mux = reader.read(1)
325
+ if use_same_stream_mux:
326
+ raise ValueError('useSameStreamMux == 1 not supported')
327
+ self.stream_mux_config = AacAudioRtpPacket.StreamMuxConfig(reader)
328
+
329
+ # We only support:
330
+ # allStreamsSameTimeFraming == 1
331
+ # audioMuxVersionA == 0,
332
+ # numProgram == 0
333
+ # numSubFrames == 0
334
+ # numLayer == 0
335
+
336
+ mux_slot_length_bytes = 0
337
+ while True:
338
+ tmp = reader.read(8)
339
+ mux_slot_length_bytes += tmp
340
+ if tmp != 255:
341
+ break
342
+
343
+ self.payload = reader.read_bytes(mux_slot_length_bytes)
344
+
345
+ if self.stream_mux_config.other_data_present:
346
+ reader.skip(self.stream_mux_config.other_data_len_bits)
347
+
348
+ # ByteAlign
349
+ while reader.bit_position % 8:
350
+ reader.read(1)
351
+
352
+ def __init__(self, data: bytes) -> None:
353
+ # Parse the bit stream
354
+ reader = BitReader(data)
355
+ self.audio_mux_element = self.AudioMuxElement(reader, mux_config_present=1)
356
+
357
+ def to_adts(self):
358
+ # pylint: disable=line-too-long
359
+ sampling_frequency_index = (
360
+ self.audio_mux_element.stream_mux_config.audio_specific_config.sampling_frequency_index
361
+ )
362
+ channel_configuration = (
363
+ self.audio_mux_element.stream_mux_config.audio_specific_config.channel_configuration
364
+ )
365
+ frame_size = len(self.audio_mux_element.payload)
366
+ return (
367
+ bytes(
368
+ [
369
+ 0xFF,
370
+ 0xF1, # 0xF9 (MPEG2)
371
+ 0x40
372
+ | (sampling_frequency_index << 2)
373
+ | (channel_configuration >> 2),
374
+ ((channel_configuration & 0x3) << 6) | ((frame_size + 7) >> 11),
375
+ ((frame_size + 7) >> 3) & 0xFF,
376
+ (((frame_size + 7) << 5) & 0xFF) | 0x1F,
377
+ 0xFC,
378
+ ]
379
+ )
380
+ + self.audio_mux_element.payload
381
+ )