BaseOutputTransport: reword camera with video
This commit is contained in:
@@ -53,11 +53,10 @@ class BaseOutputTransport(FrameProcessor):
|
|||||||
self._sink_clock_task = None
|
self._sink_clock_task = None
|
||||||
|
|
||||||
# Task to write/send audio and image frames.
|
# Task to write/send audio and image frames.
|
||||||
self._camera_out_task = None
|
self._video_out_task = None
|
||||||
|
|
||||||
# These are the images that we should send to the camera at our desired
|
# These are the images that we should send at our desired framerate.
|
||||||
# framerate.
|
self._video_images = None
|
||||||
self._camera_images = None
|
|
||||||
|
|
||||||
# Output sample rate. It will be initialized on StartFrame.
|
# Output sample rate. It will be initialized on StartFrame.
|
||||||
self._sample_rate = 0
|
self._sample_rate = 0
|
||||||
@@ -88,7 +87,7 @@ class BaseOutputTransport(FrameProcessor):
|
|||||||
# Start audio mixer.
|
# Start audio mixer.
|
||||||
if self._params.audio_out_mixer:
|
if self._params.audio_out_mixer:
|
||||||
await self._params.audio_out_mixer.start(self._sample_rate)
|
await self._params.audio_out_mixer.start(self._sample_rate)
|
||||||
self._create_camera_task()
|
self._create_video_task()
|
||||||
self._create_sink_tasks()
|
self._create_sink_tasks()
|
||||||
|
|
||||||
async def stop(self, frame: EndFrame):
|
async def stop(self, frame: EndFrame):
|
||||||
@@ -98,26 +97,26 @@ class BaseOutputTransport(FrameProcessor):
|
|||||||
|
|
||||||
# At this point we have enqueued an EndFrame and we need to wait for
|
# At this point we have enqueued an EndFrame and we need to wait for
|
||||||
# that EndFrame to be processed by the sink tasks. We also need to wait
|
# that EndFrame to be processed by the sink tasks. We also need to wait
|
||||||
# for these tasks before cancelling the camera and audio tasks below
|
# for these tasks before cancelling the video and audio tasks below
|
||||||
# because they might be still rendering.
|
# because they might be still rendering.
|
||||||
if self._sink_task:
|
if self._sink_task:
|
||||||
await self.wait_for_task(self._sink_task)
|
await self.wait_for_task(self._sink_task)
|
||||||
if self._sink_clock_task:
|
if self._sink_clock_task:
|
||||||
await self.wait_for_task(self._sink_clock_task)
|
await self.wait_for_task(self._sink_clock_task)
|
||||||
|
|
||||||
# We can now cancel the camera task.
|
# We can now cancel the video task.
|
||||||
await self._cancel_camera_task()
|
await self._cancel_video_task()
|
||||||
|
|
||||||
async def cancel(self, frame: CancelFrame):
|
async def cancel(self, frame: CancelFrame):
|
||||||
# Since we are cancelling everything it doesn't matter if we cancel sink
|
# Since we are cancelling everything it doesn't matter if we cancel sink
|
||||||
# tasks first or not.
|
# tasks first or not.
|
||||||
await self._cancel_sink_tasks()
|
await self._cancel_sink_tasks()
|
||||||
await self._cancel_camera_task()
|
await self._cancel_video_task()
|
||||||
|
|
||||||
async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame):
|
async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def write_frame_to_camera(self, frame: OutputImageRawFrame):
|
async def write_raw_video_frame(self, frame: OutputImageRawFrame):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def write_raw_audio_frames(self, frames: bytes):
|
async def write_raw_audio_frames(self, frames: bytes):
|
||||||
@@ -181,11 +180,11 @@ class BaseOutputTransport(FrameProcessor):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(frame, StartInterruptionFrame):
|
if isinstance(frame, StartInterruptionFrame):
|
||||||
# Cancel sink and camera tasks.
|
# Cancel sink and video tasks.
|
||||||
await self._cancel_sink_tasks()
|
await self._cancel_sink_tasks()
|
||||||
await self._cancel_camera_task()
|
await self._cancel_video_task()
|
||||||
# Create sink and camera tasks.
|
# Create sink and video tasks.
|
||||||
self._create_camera_task()
|
self._create_video_task()
|
||||||
self._create_sink_tasks()
|
self._create_sink_tasks()
|
||||||
# Let's send a bot stopped speaking if we have to.
|
# Let's send a bot stopped speaking if we have to.
|
||||||
await self._bot_stopped_speaking()
|
await self._bot_stopped_speaking()
|
||||||
@@ -216,7 +215,7 @@ class BaseOutputTransport(FrameProcessor):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self._params.video_out_is_live:
|
if self._params.video_out_is_live:
|
||||||
await self._camera_out_queue.put(frame)
|
await self._video_out_queue.put(frame)
|
||||||
else:
|
else:
|
||||||
await self._sink_queue.put(frame)
|
await self._sink_queue.put(frame)
|
||||||
|
|
||||||
@@ -261,9 +260,9 @@ class BaseOutputTransport(FrameProcessor):
|
|||||||
|
|
||||||
async def _sink_frame_handler(self, frame: Frame):
|
async def _sink_frame_handler(self, frame: Frame):
|
||||||
if isinstance(frame, OutputImageRawFrame):
|
if isinstance(frame, OutputImageRawFrame):
|
||||||
await self._set_camera_image(frame)
|
await self._set_video_image(frame)
|
||||||
elif isinstance(frame, SpriteFrame):
|
elif isinstance(frame, SpriteFrame):
|
||||||
await self._set_camera_images(frame.images)
|
await self._set_video_images(frame.images)
|
||||||
elif isinstance(frame, TransportMessageFrame):
|
elif isinstance(frame, TransportMessageFrame):
|
||||||
await self.send_message(frame)
|
await self.send_message(frame)
|
||||||
|
|
||||||
@@ -368,20 +367,20 @@ class BaseOutputTransport(FrameProcessor):
|
|||||||
await self.write_raw_audio_frames(frame.audio)
|
await self.write_raw_audio_frames(frame.audio)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Camera task
|
# Video task
|
||||||
#
|
#
|
||||||
|
|
||||||
def _create_camera_task(self):
|
def _create_video_task(self):
|
||||||
# Create camera output queue and task if needed.
|
# Create video output queue and task if needed.
|
||||||
if not self._camera_out_task and self._params.video_out_enabled:
|
if not self._video_out_task and self._params.video_out_enabled:
|
||||||
self._camera_out_queue = asyncio.Queue()
|
self._video_out_queue = asyncio.Queue()
|
||||||
self._camera_out_task = self.create_task(self._camera_out_task_handler())
|
self._video_out_task = self.create_task(self._video_out_task_handler())
|
||||||
|
|
||||||
async def _cancel_camera_task(self):
|
async def _cancel_video_task(self):
|
||||||
# Stop camera output task.
|
# Stop video output task.
|
||||||
if self._camera_out_task and self._params.video_out_enabled:
|
if self._video_out_task and self._params.video_out_enabled:
|
||||||
await self.cancel_task(self._camera_out_task)
|
await self.cancel_task(self._video_out_task)
|
||||||
self._camera_out_task = None
|
self._video_out_task = None
|
||||||
|
|
||||||
async def _draw_image(self, frame: OutputImageRawFrame):
|
async def _draw_image(self, frame: OutputImageRawFrame):
|
||||||
desired_size = (self._params.video_out_width, self._params.video_out_height)
|
desired_size = (self._params.video_out_width, self._params.video_out_height)
|
||||||
@@ -397,50 +396,50 @@ class BaseOutputTransport(FrameProcessor):
|
|||||||
resized_image.tobytes(), resized_image.size, resized_image.format
|
resized_image.tobytes(), resized_image.size, resized_image.format
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.write_frame_to_camera(frame)
|
await self.write_raw_video_frame(frame)
|
||||||
|
|
||||||
async def _set_camera_image(self, image: OutputImageRawFrame):
|
async def _set_video_image(self, image: OutputImageRawFrame):
|
||||||
self._camera_images = itertools.cycle([image])
|
self._video_images = itertools.cycle([image])
|
||||||
|
|
||||||
async def _set_camera_images(self, images: List[OutputImageRawFrame]):
|
async def _set_video_images(self, images: List[OutputImageRawFrame]):
|
||||||
self._camera_images = itertools.cycle(images)
|
self._video_images = itertools.cycle(images)
|
||||||
|
|
||||||
async def _camera_out_task_handler(self):
|
async def _video_out_task_handler(self):
|
||||||
self._camera_out_start_time = None
|
self._video_out_start_time = None
|
||||||
self._camera_out_frame_index = 0
|
self._video_out_frame_index = 0
|
||||||
self._camera_out_frame_duration = 1 / self._params.video_out_framerate
|
self._video_out_frame_duration = 1 / self._params.video_out_framerate
|
||||||
self._camera_out_frame_reset = self._camera_out_frame_duration * 5
|
self._video_out_frame_reset = self._video_out_frame_duration * 5
|
||||||
while True:
|
while True:
|
||||||
if self._params.video_out_is_live:
|
if self._params.video_out_is_live:
|
||||||
await self._camera_out_is_live_handler()
|
await self._video_out_is_live_handler()
|
||||||
elif self._camera_images:
|
elif self._video_images:
|
||||||
image = next(self._camera_images)
|
image = next(self._video_images)
|
||||||
await self._draw_image(image)
|
await self._draw_image(image)
|
||||||
await asyncio.sleep(self._camera_out_frame_duration)
|
await asyncio.sleep(self._video_out_frame_duration)
|
||||||
else:
|
else:
|
||||||
await asyncio.sleep(self._camera_out_frame_duration)
|
await asyncio.sleep(self._video_out_frame_duration)
|
||||||
|
|
||||||
async def _camera_out_is_live_handler(self):
|
async def _video_out_is_live_handler(self):
|
||||||
image = await self._camera_out_queue.get()
|
image = await self._video_out_queue.get()
|
||||||
|
|
||||||
# We get the start time as soon as we get the first image.
|
# We get the start time as soon as we get the first image.
|
||||||
if not self._camera_out_start_time:
|
if not self._video_out_start_time:
|
||||||
self._camera_out_start_time = time.time()
|
self._video_out_start_time = time.time()
|
||||||
self._camera_out_frame_index = 0
|
self._video_out_frame_index = 0
|
||||||
|
|
||||||
# Calculate how much time we need to wait before rendering next image.
|
# Calculate how much time we need to wait before rendering next image.
|
||||||
real_elapsed_time = time.time() - self._camera_out_start_time
|
real_elapsed_time = time.time() - self._video_out_start_time
|
||||||
real_render_time = self._camera_out_frame_index * self._camera_out_frame_duration
|
real_render_time = self._video_out_frame_index * self._video_out_frame_duration
|
||||||
delay_time = self._camera_out_frame_duration + real_render_time - real_elapsed_time
|
delay_time = self._video_out_frame_duration + real_render_time - real_elapsed_time
|
||||||
|
|
||||||
if abs(delay_time) > self._camera_out_frame_reset:
|
if abs(delay_time) > self._video_out_frame_reset:
|
||||||
self._camera_out_start_time = time.time()
|
self._video_out_start_time = time.time()
|
||||||
self._camera_out_frame_index = 0
|
self._video_out_frame_index = 0
|
||||||
elif delay_time > 0:
|
elif delay_time > 0:
|
||||||
await asyncio.sleep(delay_time)
|
await asyncio.sleep(delay_time)
|
||||||
self._camera_out_frame_index += 1
|
self._video_out_frame_index += 1
|
||||||
|
|
||||||
# Render image
|
# Render image
|
||||||
await self._draw_image(image)
|
await self._draw_image(image)
|
||||||
|
|
||||||
self._camera_out_queue.task_done()
|
self._video_out_queue.task_done()
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class TkOutputTransport(BaseOutputTransport):
|
|||||||
self._executor, self._out_stream.write, frames
|
self._executor, self._out_stream.write, frames
|
||||||
)
|
)
|
||||||
|
|
||||||
async def write_frame_to_camera(self, frame: OutputImageRawFrame):
|
async def write_raw_video_frame(self, frame: OutputImageRawFrame):
|
||||||
self.get_event_loop().call_soon(self._write_frame_to_tk, frame)
|
self.get_event_loop().call_soon(self._write_frame_to_tk, frame)
|
||||||
|
|
||||||
def _write_frame_to_tk(self, frame: OutputImageRawFrame):
|
def _write_frame_to_tk(self, frame: OutputImageRawFrame):
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ class SmallWebRTCClient:
|
|||||||
if self._can_send() and self._audio_output_track:
|
if self._can_send() and self._audio_output_track:
|
||||||
await self._audio_output_track.add_audio_bytes(data)
|
await self._audio_output_track.add_audio_bytes(data)
|
||||||
|
|
||||||
async def write_frame_to_camera(self, frame: OutputImageRawFrame):
|
async def write_raw_video_frame(self, frame: OutputImageRawFrame):
|
||||||
if self._can_send() and self._video_output_track:
|
if self._can_send() and self._video_output_track:
|
||||||
self._video_output_track.add_video_frame(frame)
|
self._video_output_track.add_video_frame(frame)
|
||||||
|
|
||||||
@@ -500,8 +500,8 @@ class SmallWebRTCOutputTransport(BaseOutputTransport):
|
|||||||
async def write_raw_audio_frames(self, frames: bytes):
|
async def write_raw_audio_frames(self, frames: bytes):
|
||||||
await self._client.write_raw_audio_frames(frames)
|
await self._client.write_raw_audio_frames(frames)
|
||||||
|
|
||||||
async def write_frame_to_camera(self, frame: OutputImageRawFrame):
|
async def write_raw_video_frame(self, frame: OutputImageRawFrame):
|
||||||
await self._client.write_frame_to_camera(frame)
|
await self._client.write_raw_video_frame(frame)
|
||||||
|
|
||||||
|
|
||||||
class SmallWebRTCTransport(BaseTransport):
|
class SmallWebRTCTransport(BaseTransport):
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ class DailyTransportClient(EventHandler):
|
|||||||
self._mic.write_frames(frames, completion=completion_callback(future))
|
self._mic.write_frames(frames, completion=completion_callback(future))
|
||||||
await future
|
await future
|
||||||
|
|
||||||
async def write_frame_to_camera(self, frame: OutputImageRawFrame):
|
async def write_raw_video_frame(self, frame: OutputImageRawFrame):
|
||||||
if not self._camera:
|
if not self._camera:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -1035,8 +1035,8 @@ class DailyOutputTransport(BaseOutputTransport):
|
|||||||
async def write_raw_audio_frames(self, frames: bytes):
|
async def write_raw_audio_frames(self, frames: bytes):
|
||||||
await self._client.write_raw_audio_frames(frames)
|
await self._client.write_raw_audio_frames(frames)
|
||||||
|
|
||||||
async def write_frame_to_camera(self, frame: OutputImageRawFrame):
|
async def write_raw_video_frame(self, frame: OutputImageRawFrame):
|
||||||
await self._client.write_frame_to_camera(frame)
|
await self._client.write_raw_video_frame(frame)
|
||||||
|
|
||||||
|
|
||||||
class DailyTransport(BaseTransport):
|
class DailyTransport(BaseTransport):
|
||||||
|
|||||||
Reference in New Issue
Block a user