You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
3.8 KiB
111 lines
3.8 KiB
#!/usr/bin/env python
|
|
|
|
import argparse
|
|
import asyncio
|
|
import dataclasses
|
|
import json
|
|
import logging
|
|
|
|
import aiortc
|
|
from aiortc.mediastreams import VideoStreamTrack, AudioStreamTrack
|
|
|
|
from teleoprtc import WebRTCOfferBuilder, WebRTCAnswerBuilder
|
|
from teleoprtc.stream import StreamingOffer
|
|
from teleoprtc.info import parse_info_from_offer
|
|
|
|
|
|
async def async_input():
|
|
return await asyncio.to_thread(input)
|
|
|
|
|
|
async def StdioConnectionProvider(offer: StreamingOffer) -> aiortc.RTCSessionDescription:
|
|
print("-- Please send this JSON to server --")
|
|
print(json.dumps(dataclasses.asdict(offer)))
|
|
print("-- Press enter when the answer is ready --")
|
|
raw_payload = await async_input()
|
|
payload = json.loads(raw_payload)
|
|
answer = aiortc.RTCSessionDescription(**payload)
|
|
|
|
return answer
|
|
|
|
|
|
async def run_answer(args):
|
|
streams = []
|
|
while True:
|
|
print("-- Please enter a JSON from client --")
|
|
raw_payload = await async_input()
|
|
|
|
payload = json.loads(raw_payload)
|
|
offer = StreamingOffer(**payload)
|
|
info = parse_info_from_offer(offer.sdp)
|
|
assert len(offer.video) == info.n_expected_camera_tracks
|
|
video_tracks = [VideoStreamTrack() for _ in offer.video]
|
|
audio_tracks = [AudioStreamTrack()] if info.expected_audio_track else []
|
|
|
|
stream_builder = WebRTCAnswerBuilder(offer.sdp)
|
|
for cam, track in zip(offer.video, video_tracks, strict=True):
|
|
stream_builder.add_video_stream(cam, track)
|
|
for track in audio_tracks:
|
|
stream_builder.add_audio_stream(track)
|
|
stream = stream_builder.stream()
|
|
answer = await stream.start()
|
|
streams.append(stream)
|
|
|
|
print("-- Please send this JSON to client --")
|
|
print(json.dumps({"sdp": answer.sdp, "type": answer.type}))
|
|
|
|
await stream.wait_for_connection()
|
|
|
|
|
|
async def run_offer(args):
|
|
stream_builder = WebRTCOfferBuilder(StdioConnectionProvider)
|
|
for cam in args.cameras:
|
|
stream_builder.offer_to_receive_video_stream(cam)
|
|
if args.audio:
|
|
stream_builder.offer_to_receive_audio_stream()
|
|
if args.messaging:
|
|
stream_builder.add_messaging()
|
|
stream = stream_builder.stream()
|
|
_ = await stream.start()
|
|
await stream.wait_for_connection()
|
|
print("Connection established and all tracks are ready")
|
|
|
|
video_tracks = [stream.get_incoming_video_track(cam, False) for cam in args.cameras]
|
|
audio_track = None
|
|
if stream.has_incoming_audio_track():
|
|
audio_track = stream.get_incoming_audio_track(False)
|
|
while True:
|
|
try:
|
|
frames = await asyncio.gather(*[track.recv() for track in video_tracks])
|
|
for key, frame in zip(args.cameras, frames, strict=True):
|
|
print("Received frame from", key, frame.time)
|
|
if audio_track:
|
|
frame = await audio_track.recv()
|
|
print("Received frame from audio", frame.time)
|
|
except aiortc.mediastreams.MediaStreamError:
|
|
return
|
|
print("=====================================")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser()
|
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
|
|
offer_parser = subparsers.add_parser("offer", description="Create offer stream")
|
|
offer_parser.add_argument("--audio", action="store_true", help="Offer to receive audio")
|
|
offer_parser.add_argument("--messaging", action="store_true", help="Add messaging support")
|
|
offer_parser.add_argument("cameras", metavar="CAMERA", type=str, nargs="+", default=[], help="Camera types to stream")
|
|
|
|
answer_parser = subparsers.add_parser("answer", description="Create answer stream")
|
|
|
|
args = parser.parse_args()
|
|
|
|
logging.basicConfig(level=logging.CRITICAL, handlers=[logging.StreamHandler()])
|
|
logger = logging.getLogger("WebRTCStream")
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
loop = asyncio.get_event_loop()
|
|
if args.command == "offer":
|
|
loop.run_until_complete(run_offer(args))
|
|
elif args.command == "answer":
|
|
loop.run_until_complete(run_answer(args))
|
|
|