2023-11-29 14:09:46 +08:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
2024-07-10 03:09:42 +08:00
|
|
|
import pytest
|
2023-11-29 14:09:46 +08:00
|
|
|
import asyncio
|
2024-06-05 05:11:38 +08:00
|
|
|
import sys
|
2023-11-29 14:09:46 +08:00
|
|
|
|
|
|
|
from aiortc.mediastreams import AudioStreamTrack, VideoStreamTrack
|
|
|
|
from parameterized import parameterized
|
|
|
|
|
|
|
|
from teleoprtc.builder import WebRTCOfferBuilder, WebRTCAnswerBuilder
|
|
|
|
from teleoprtc.stream import StreamingOffer
|
|
|
|
from teleoprtc.info import parse_info_from_offer
|
|
|
|
|
|
|
|
|
2024-06-05 05:11:38 +08:00
|
|
|
if sys.version_info >= (3, 11):
|
|
|
|
timeout = asyncio.timeout
|
|
|
|
else:
|
|
|
|
class Timeout:
|
|
|
|
def __init__(self, delay: float):
|
|
|
|
self._delay = delay
|
|
|
|
self._task = None
|
|
|
|
self._timeout_handle = None
|
|
|
|
|
|
|
|
def _timeout(self):
|
|
|
|
if self._task:
|
|
|
|
self._task.cancel()
|
|
|
|
|
|
|
|
async def __aenter__(self):
|
|
|
|
self._task = asyncio.current_task()
|
|
|
|
loop = asyncio.events.get_running_loop()
|
|
|
|
self._timeout_handle = loop.call_later(self._delay, self._timeout)
|
|
|
|
return self
|
|
|
|
|
|
|
|
async def __aexit__(self, exc_type, exc, tb):
|
|
|
|
if self._timeout_handle:
|
|
|
|
self._timeout_handle.cancel()
|
|
|
|
if exc_type is asyncio.CancelledError and self._task and self._task.cancelled():
|
|
|
|
raise asyncio.TimeoutError from exc
|
|
|
|
return False
|
|
|
|
|
|
|
|
def timeout(delay):
|
|
|
|
return Timeout(delay)
|
|
|
|
|
|
|
|
|
2023-11-29 14:09:46 +08:00
|
|
|
class SimpleAnswerProvider:
|
|
|
|
def __init__(self):
|
|
|
|
self.stream = None
|
|
|
|
|
|
|
|
async def __call__(self, offer: StreamingOffer):
|
|
|
|
assert self.stream is None, "This may only be called once"
|
|
|
|
|
|
|
|
info = parse_info_from_offer(offer.sdp)
|
|
|
|
|
|
|
|
builder = WebRTCAnswerBuilder(offer.sdp)
|
|
|
|
for cam in offer.video:
|
|
|
|
builder.add_video_stream(cam, VideoStreamTrack())
|
|
|
|
if info.expected_audio_track:
|
|
|
|
builder.add_audio_stream(AudioStreamTrack())
|
|
|
|
if info.incoming_audio_track:
|
|
|
|
builder.offer_to_receive_audio_stream()
|
|
|
|
|
|
|
|
self.stream = builder.stream()
|
|
|
|
answer = await self.stream.start()
|
|
|
|
|
|
|
|
return answer
|
2023-11-30 04:17:45 +08:00
|
|
|
|
|
|
|
|
2024-07-10 03:09:42 +08:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
class TestStreamIntegration:
|
2023-11-29 14:09:46 +08:00
|
|
|
@parameterized.expand([
|
|
|
|
# name, recv_cameras, recv_audio, messaging
|
|
|
|
("multi_camera", ["driver", "wideRoad", "road"], False, False),
|
|
|
|
("camera_and_audio", ["driver"], True, False),
|
|
|
|
("camera_and__messaging", ["driver"], False, True),
|
|
|
|
("camera_and_audio_and_messaging", ["driver", "wideRoad", "road"], True, True),
|
|
|
|
])
|
|
|
|
async def test_multi_camera(self, name, cameras, recv_audio, add_messaging):
|
|
|
|
simple_answerer = SimpleAnswerProvider()
|
|
|
|
offer_builder = WebRTCOfferBuilder(simple_answerer)
|
|
|
|
for cam in cameras:
|
|
|
|
offer_builder.offer_to_receive_video_stream(cam)
|
|
|
|
if recv_audio:
|
|
|
|
offer_builder.offer_to_receive_audio_stream()
|
|
|
|
if add_messaging:
|
|
|
|
offer_builder.add_messaging()
|
|
|
|
stream = offer_builder.stream()
|
|
|
|
|
2023-11-30 04:33:19 +08:00
|
|
|
_ = await stream.start()
|
2024-07-10 03:09:42 +08:00
|
|
|
assert stream.is_started
|
2023-11-29 14:09:46 +08:00
|
|
|
|
|
|
|
try:
|
2024-06-05 05:11:38 +08:00
|
|
|
async with timeout(2):
|
2023-11-29 14:09:46 +08:00
|
|
|
await stream.wait_for_connection()
|
2024-02-27 01:45:32 +08:00
|
|
|
except TimeoutError:
|
2024-07-10 03:09:42 +08:00
|
|
|
pytest.fail("Timed out waiting for connection")
|
|
|
|
assert stream.is_connected_and_ready
|
2023-11-29 14:09:46 +08:00
|
|
|
|
2024-07-10 03:09:42 +08:00
|
|
|
assert stream.has_messaging_channel() == add_messaging
|
2023-11-29 14:09:46 +08:00
|
|
|
if stream.has_messaging_channel():
|
|
|
|
channel = stream.get_messaging_channel()
|
2024-07-10 03:09:42 +08:00
|
|
|
assert channel is not None
|
|
|
|
assert channel.readyState == "open"
|
2023-12-07 07:39:21 +08:00
|
|
|
|
2024-07-10 03:09:42 +08:00
|
|
|
assert stream.has_incoming_audio_track() == recv_audio
|
2023-11-29 14:09:46 +08:00
|
|
|
if stream.has_incoming_audio_track():
|
|
|
|
track = stream.get_incoming_audio_track(False)
|
2024-07-10 03:09:42 +08:00
|
|
|
assert track is not None
|
|
|
|
assert track.readyState == "live"
|
|
|
|
assert track.kind == "audio"
|
2023-11-29 14:09:46 +08:00
|
|
|
# test audio recv
|
|
|
|
try:
|
2024-06-05 05:11:38 +08:00
|
|
|
async with timeout(1):
|
2023-11-29 14:09:46 +08:00
|
|
|
await track.recv()
|
2024-02-27 01:45:32 +08:00
|
|
|
except TimeoutError:
|
2024-07-10 03:09:42 +08:00
|
|
|
pytest.fail("Timed out waiting for audio frame")
|
2023-11-29 14:09:46 +08:00
|
|
|
|
|
|
|
for cam in cameras:
|
2024-07-10 03:09:42 +08:00
|
|
|
assert stream.has_incoming_video_track(cam)
|
2023-11-29 14:09:46 +08:00
|
|
|
if stream.has_incoming_video_track(cam):
|
|
|
|
track = stream.get_incoming_video_track(cam, False)
|
2024-07-10 03:09:42 +08:00
|
|
|
assert track is not None
|
|
|
|
assert track.readyState == "live"
|
|
|
|
assert track.kind == "video"
|
2023-11-29 14:09:46 +08:00
|
|
|
# test video recv
|
|
|
|
try:
|
2024-06-05 05:11:38 +08:00
|
|
|
async with timeout(1):
|
2023-11-29 14:09:46 +08:00
|
|
|
await stream.get_incoming_video_track(cam, False).recv()
|
2024-02-27 01:45:32 +08:00
|
|
|
except TimeoutError:
|
2024-07-10 03:09:42 +08:00
|
|
|
pytest.fail("Timed out waiting for video frame")
|
2023-11-29 14:09:46 +08:00
|
|
|
|
|
|
|
await stream.stop()
|
|
|
|
await simple_answerer.stream.stop()
|
2024-07-10 03:09:42 +08:00
|
|
|
assert not stream.is_started
|
|
|
|
assert not stream.is_connected_and_ready
|