gosuto_libwebrtc/native/
video_source.rs1use std::{
16 sync::Arc,
17 time::{Duration, SystemTime, UNIX_EPOCH},
18};
19
20use cxx::SharedPtr;
21use livekit_runtime::interval;
22use parking_lot::Mutex;
23use gosuto_webrtc_sys::{video_frame as vf_sys, video_frame::ffi::VideoRotation, video_track as vt_sys};
24
25use crate::{
26 video_frame::{I420Buffer, VideoBuffer, VideoFrame},
27 video_source::VideoResolution,
28};
29
30impl From<vt_sys::ffi::VideoResolution> for VideoResolution {
31 fn from(res: vt_sys::ffi::VideoResolution) -> Self {
32 Self { width: res.width, height: res.height }
33 }
34}
35
36impl From<VideoResolution> for vt_sys::ffi::VideoResolution {
37 fn from(res: VideoResolution) -> Self {
38 Self { width: res.width, height: res.height }
39 }
40}
41
42#[derive(Clone)]
43pub struct NativeVideoSource {
44 sys_handle: SharedPtr<vt_sys::ffi::VideoTrackSource>,
45 inner: Arc<Mutex<VideoSourceInner>>,
46}
47
48struct VideoSourceInner {
49 captured_frames: usize,
50}
51
52impl NativeVideoSource {
53 pub fn new(resolution: VideoResolution, is_screencast: bool) -> NativeVideoSource {
54 let source = Self {
55 sys_handle: vt_sys::ffi::new_video_track_source(
56 &vt_sys::ffi::VideoResolution::from(resolution.clone()),
57 is_screencast,
58 ),
59 inner: Arc::new(Mutex::new(VideoSourceInner { captured_frames: 0 })),
60 };
61
62 livekit_runtime::spawn({
63 let source = source.clone();
64 let i420 = I420Buffer::new(resolution.width, resolution.height);
65 async move {
66 let mut interval = interval(Duration::from_millis(100)); loop {
69 interval.tick().await;
70
71 let inner = source.inner.lock();
72 if inner.captured_frames > 0 {
73 break;
74 }
75
76 let mut builder = vf_sys::ffi::new_video_frame_builder();
77 builder.pin_mut().set_rotation(VideoRotation::VideoRotation0);
78 builder.pin_mut().set_video_frame_buffer(i420.as_ref().sys_handle());
79
80 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
81 builder.pin_mut().set_timestamp_us(now.as_micros() as i64);
82
83 source.sys_handle.on_captured_frame(&builder.pin_mut().build());
84 }
85 }
86 });
87
88 source
89 }
90
91 pub fn sys_handle(&self) -> SharedPtr<vt_sys::ffi::VideoTrackSource> {
92 self.sys_handle.clone()
93 }
94
95 pub fn capture_frame<T: AsRef<dyn VideoBuffer>>(&self, frame: &VideoFrame<T>) {
96 let mut inner = self.inner.lock();
97 inner.captured_frames += 1;
98
99 let mut builder = vf_sys::ffi::new_video_frame_builder();
100 builder.pin_mut().set_rotation(frame.rotation.into());
101 builder.pin_mut().set_video_frame_buffer(frame.buffer.as_ref().sys_handle());
102
103 if frame.timestamp_us == 0 {
104 let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
106 builder.pin_mut().set_timestamp_us(now.as_micros() as i64);
107 } else {
108 builder.pin_mut().set_timestamp_us(frame.timestamp_us);
109 }
110
111 self.sys_handle.on_captured_frame(&builder.pin_mut().build());
112 }
113
114 pub fn video_resolution(&self) -> VideoResolution {
115 self.sys_handle.video_resolution().into()
116 }
117}