forked from auracaster/bumble_mirror
152 lines
5.8 KiB
Python
152 lines
5.8 KiB
Python
# Copyright 2021-2023 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.
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Imports
|
|
# -----------------------------------------------------------------------------
|
|
import asyncio
|
|
import os
|
|
import pytest
|
|
import logging
|
|
|
|
from bumble import device
|
|
from bumble.hci import CodecID, CodingFormat
|
|
from bumble.profiles.bap import (
|
|
AudioLocation,
|
|
SupportedFrameDuration,
|
|
SupportedSamplingFrequency,
|
|
CodecSpecificCapabilities,
|
|
ContextType,
|
|
PacRecord,
|
|
PublishedAudioCapabilitiesService,
|
|
PublishedAudioCapabilitiesServiceProxy,
|
|
)
|
|
from .test_utils import TwoDevices
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Logging
|
|
# -----------------------------------------------------------------------------
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_codec_specific_capabilities() -> None:
|
|
SAMPLE_FREQUENCY = SupportedSamplingFrequency.FREQ_16000
|
|
FRAME_SURATION = SupportedFrameDuration.DURATION_10000_US_SUPPORTED
|
|
AUDIO_CHANNEL_COUNTS = [1]
|
|
cap = CodecSpecificCapabilities(
|
|
supported_sampling_frequencies=SAMPLE_FREQUENCY,
|
|
supported_frame_durations=FRAME_SURATION,
|
|
supported_audio_channel_counts=AUDIO_CHANNEL_COUNTS,
|
|
min_octets_per_codec_frame=40,
|
|
max_octets_per_codec_frame=40,
|
|
supported_max_codec_frames_per_sdu=1,
|
|
)
|
|
assert CodecSpecificCapabilities.from_bytes(bytes(cap)) == cap
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_pac_record() -> None:
|
|
SAMPLE_FREQUENCY = SupportedSamplingFrequency.FREQ_16000
|
|
FRAME_SURATION = SupportedFrameDuration.DURATION_10000_US_SUPPORTED
|
|
AUDIO_CHANNEL_COUNTS = [1]
|
|
cap = CodecSpecificCapabilities(
|
|
supported_sampling_frequencies=SAMPLE_FREQUENCY,
|
|
supported_frame_durations=FRAME_SURATION,
|
|
supported_audio_channel_counts=AUDIO_CHANNEL_COUNTS,
|
|
min_octets_per_codec_frame=40,
|
|
max_octets_per_codec_frame=40,
|
|
supported_max_codec_frames_per_sdu=1,
|
|
)
|
|
|
|
pac_record = PacRecord(
|
|
coding_format=CodingFormat(CodecID.LC3),
|
|
codec_specific_capabilities=cap,
|
|
metadata=b'',
|
|
)
|
|
assert PacRecord.from_bytes(bytes(pac_record)) == pac_record
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def test_vendor_specific_pac_record() -> None:
|
|
# Vendor-Specific codec, Google, ID=0xFFFF. No capabilities and metadata.
|
|
RAW_DATA = bytes.fromhex('ffe000ffff0000')
|
|
assert bytes(PacRecord.from_bytes(RAW_DATA)) == RAW_DATA
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
@pytest.mark.asyncio
|
|
async def test_pacs():
|
|
devices = TwoDevices()
|
|
devices[0].add_service(
|
|
PublishedAudioCapabilitiesService(
|
|
supported_sink_context=ContextType.MEDIA,
|
|
available_sink_context=ContextType.MEDIA,
|
|
supported_source_context=0,
|
|
available_source_context=0,
|
|
sink_pac=[
|
|
# Codec Capability Setting 16_2
|
|
PacRecord(
|
|
coding_format=CodingFormat(CodecID.LC3),
|
|
codec_specific_capabilities=CodecSpecificCapabilities(
|
|
supported_sampling_frequencies=(
|
|
SupportedSamplingFrequency.FREQ_16000
|
|
),
|
|
supported_frame_durations=(
|
|
SupportedFrameDuration.DURATION_10000_US_SUPPORTED
|
|
),
|
|
supported_audio_channel_counts=[1],
|
|
min_octets_per_codec_frame=40,
|
|
max_octets_per_codec_frame=40,
|
|
supported_max_codec_frames_per_sdu=1,
|
|
),
|
|
),
|
|
# Codec Capability Setting 24_2
|
|
PacRecord(
|
|
coding_format=CodingFormat(CodecID.LC3),
|
|
codec_specific_capabilities=CodecSpecificCapabilities(
|
|
supported_sampling_frequencies=(
|
|
SupportedSamplingFrequency.FREQ_24000
|
|
),
|
|
supported_frame_durations=(
|
|
SupportedFrameDuration.DURATION_10000_US_SUPPORTED
|
|
),
|
|
supported_audio_channel_counts=[1],
|
|
min_octets_per_codec_frame=60,
|
|
max_octets_per_codec_frame=60,
|
|
supported_max_codec_frames_per_sdu=1,
|
|
),
|
|
),
|
|
],
|
|
sink_audio_locations=AudioLocation.FRONT_LEFT | AudioLocation.FRONT_RIGHT,
|
|
)
|
|
)
|
|
|
|
await devices.setup_connection()
|
|
peer = device.Peer(devices.connections[1])
|
|
pacs_client = await peer.discover_service_and_create_proxy(
|
|
PublishedAudioCapabilitiesServiceProxy
|
|
)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
async def run():
|
|
await test_pacs()
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
if __name__ == '__main__':
|
|
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
|
|
asyncio.run(run())
|