# Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from bumble.avdtp import Listener as AvdtpListener, MediaCodecCapabilities, AVDTP_AUDIO_MEDIA_TYPE from bumble.avrcp import Protocol as AvrcpProtocol, make_target_service_sdp_records, make_controller_service_sdp_records from bumble.a2dp import (A2DP_SBC_CODEC_TYPE, SBC_DUAL_CHANNEL_MODE, SBC_JOINT_STEREO_CHANNEL_MODE, SBC_LOUDNESS_ALLOCATION_METHOD, SBC_MONO_CHANNEL_MODE, SBC_SNR_ALLOCATION_METHOD, SBC_STEREO_CHANNEL_MODE, SbcMediaCodecInformation, make_audio_sink_service_sdp_records, make_audio_source_service_sdp_records) from bumble.device import Device from pandora_experimental.avrcp_grpc_aio import AVRCPServicer class AvrcpService(AVRCPServicer): device: Device def __init__(self, device: Device) -> None: super().__init__() self.device = device sdp_records = { 0x00010002: make_audio_source_service_sdp_records(0x00010002), # A2DP Source 0x00010003: make_audio_sink_service_sdp_records(0x00010003), # A2DP Sink 0x00010004: make_controller_service_sdp_records(0x00010004), # AVRCP Controller 0x00010005: make_target_service_sdp_records(0x00010005), # AVRCP Target } self.device.sdp_service_records.update(sdp_records) # Register AVDTP L2cap avdtp_listener = AvdtpListener.for_device(device) def on_avdtp_connection(server) -> None: # type: ignore server.add_sink(codec_capabilities()) # type: ignore avdtp_listener.on('connection', on_avdtp_connection) # type: ignore # Register AVRCP L2cap avrcp_protocol = AvrcpProtocol(delegate=None) avrcp_protocol.listen(device) def codec_capabilities() -> MediaCodecCapabilities: """Codec capabilities for the Bumble sink devices.""" return MediaCodecCapabilities( media_type=AVDTP_AUDIO_MEDIA_TYPE, media_codec_type=A2DP_SBC_CODEC_TYPE, media_codec_information=SbcMediaCodecInformation.from_lists( sampling_frequencies=[48000, 44100, 32000, 16000], channel_modes=[ SBC_MONO_CHANNEL_MODE, SBC_DUAL_CHANNEL_MODE, SBC_STEREO_CHANNEL_MODE, SBC_JOINT_STEREO_CHANNEL_MODE, ], block_lengths=[4, 8, 12, 16], subbands=[4, 8], allocation_methods=[ SBC_LOUDNESS_ALLOCATION_METHOD, SBC_SNR_ALLOCATION_METHOD, ], minimum_bitpool_value=2, maximum_bitpool_value=53, ), )