hylarana_capture/
lib.rs

1#[cfg(target_os = "windows")]
2mod win32 {
3    pub mod audio;
4    pub mod camera;
5    pub mod screen;
6}
7
8#[cfg(target_os = "linux")]
9mod linux {
10    pub mod audio;
11    pub mod camera;
12    pub mod screen;
13}
14
15#[cfg(target_os = "macos")]
16mod macos {
17    pub mod audio;
18    pub mod camera;
19    pub mod screen;
20}
21
22#[cfg(target_os = "windows")]
23pub use self::win32::{
24    audio::{AudioCapture, AudioCaptureError},
25    camera::{CameraCapture, CameraCaptureError},
26    screen::{ScreenCapture, ScreenCaptureError},
27};
28
29#[cfg(target_os = "linux")]
30pub use self::linux::{
31    audio::{AudioCapture, AudioCaptureError},
32    camera::{CameraCapture, CameraCaptureError},
33    screen::{ScreenCapture, ScreenCaptureError},
34};
35
36#[cfg(target_os = "macos")]
37pub use self::macos::{
38    audio::{AudioCapture, AudioCaptureError},
39    camera::{CameraCapture, CameraCaptureError},
40    screen::{ScreenCapture, ScreenCaptureError},
41};
42
43#[cfg(target_os = "windows")]
44use common::win32::Direct3DDevice;
45
46use common::{
47    frame::{AudioFrame, VideoFrame},
48    Size,
49};
50
51use serde::{Deserialize, Serialize};
52use thiserror::Error;
53
54#[derive(Debug, Error)]
55pub enum CaptureError {
56    #[error(transparent)]
57    AudioCaptureError(#[from] AudioCaptureError),
58    #[error(transparent)]
59    ScreenCaptureError(#[from] ScreenCaptureError),
60    #[error(transparent)]
61    CameraCaptureError(#[from] CameraCaptureError),
62}
63
64pub trait FrameArrived: Sync + Send {
65    /// The type of data captured, such as video frames.
66    type Frame;
67
68    /// This method is called when the capture source captures new data. If it
69    /// returns false, the source stops capturing.
70    fn sink(&mut self, frame: &Self::Frame) -> bool;
71}
72
73pub trait CaptureHandler: Sync + Send {
74    type Error;
75
76    /// The type of data captured, such as video frames.
77    type Frame;
78
79    /// Start capturing configuration information, which may be different for
80    /// each source.
81    type CaptureOptions;
82
83    /// Get a list of sources, such as multiple screens in a display source.
84    fn get_sources() -> Result<Vec<Source>, Self::Error>;
85
86    /// Stop capturing the current source.
87    fn stop(&self) -> Result<(), Self::Error>;
88
89    /// Start capturing. This function will not block until capturing is
90    /// stopped, and it maintains its own capture thread internally.
91    fn start<S: FrameArrived<Frame = Self::Frame> + 'static>(
92        &self,
93        options: Self::CaptureOptions,
94        arrived: S,
95    ) -> Result<(), Self::Error>;
96}
97
98/// Video source type or Audio source type.
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
100pub enum SourceType {
101    /// Camera or video capture card and other devices (and support virtual
102    /// camera)
103    Camera,
104    /// The desktop or monitor corresponds to the desktop in the operating
105    /// system.
106    Screen,
107    /// Audio input and output devices.
108    Audio,
109}
110
111/// Video source or Audio source.
112#[derive(Debug, Clone, Deserialize, Serialize)]
113pub struct Source {
114    /// Device ID, usually the symbolic link to the device or the address of the
115    /// device file handle.
116    pub id: String,
117    pub name: String,
118    /// Sequence number, which can normally be ignored, in most cases this field
119    /// has no real meaning and simply indicates the order in which the device
120    /// was acquired internally.
121    pub index: usize,
122    pub kind: SourceType,
123    /// Whether or not it is the default device, normally used to indicate
124    /// whether or not it is the master device.
125    pub is_default: bool,
126}
127
128#[derive(Debug, Clone)]
129pub struct VideoCaptureSourceDescription {
130    #[cfg(target_os = "windows")]
131    pub direct3d: Direct3DDevice,
132    /// Indicates whether the capturer internally outputs hardware frames or
133    /// not, it should be noted that internally it will just output hardware
134    /// frames to the best of its ability and may also output software frames.
135    pub hardware: bool,
136    pub source: Source,
137    pub size: Size,
138    pub fps: u8,
139}
140
141#[derive(Debug, Clone)]
142pub struct AudioCaptureSourceDescription {
143    pub source: Source,
144    pub sample_rate: u32,
145}
146
147pub struct SourceCaptureOptions<T, P> {
148    pub description: P,
149    pub arrived: T,
150}
151
152pub struct CaptureOptions<V, A>
153where
154    V: FrameArrived<Frame = VideoFrame>,
155    A: FrameArrived<Frame = AudioFrame>,
156{
157    pub video: Option<SourceCaptureOptions<V, VideoCaptureSourceDescription>>,
158    pub audio: Option<SourceCaptureOptions<A, AudioCaptureSourceDescription>>,
159}
160
161impl<V, A> Default for CaptureOptions<V, A>
162where
163    V: FrameArrived<Frame = VideoFrame>,
164    A: FrameArrived<Frame = AudioFrame>,
165{
166    fn default() -> Self {
167        Self {
168            video: None,
169            audio: None,
170        }
171    }
172}
173
174enum CaptureImplement {
175    Camera(CameraCapture),
176    Screen(ScreenCapture),
177    Audio(AudioCapture),
178}
179
180/// Capture implementations for audio devices and video devices.
181#[derive(Default)]
182pub struct Capture(Vec<CaptureImplement>);
183
184impl Capture {
185    /// Get all sources that can be used for capture by specifying the type,
186    /// which is usually an audio or video device.
187    #[allow(unreachable_patterns)]
188    pub fn get_sources(kind: SourceType) -> Result<Vec<Source>, CaptureError> {
189        log::info!("capture get sources, kind={:?}", kind);
190
191        Ok(match kind {
192            SourceType::Camera => CameraCapture::get_sources()?,
193            SourceType::Screen => ScreenCapture::get_sources()?,
194            SourceType::Audio => AudioCapture::get_sources()?,
195            _ => Vec::new(),
196        })
197    }
198
199    /// Create a capture and start capturing audio and video frames by
200    /// specifying the source to be captured.
201    pub fn start<V, A>(
202        CaptureOptions { video, audio }: CaptureOptions<V, A>,
203    ) -> Result<Self, CaptureError>
204    where
205        V: FrameArrived<Frame = VideoFrame> + 'static,
206        A: FrameArrived<Frame = AudioFrame> + 'static,
207    {
208        let mut devices = Vec::with_capacity(3);
209
210        if let Some(SourceCaptureOptions {
211            description,
212            arrived,
213        }) = video
214        {
215            match description.source.kind {
216                SourceType::Camera => {
217                    let camera = CameraCapture::default();
218                    camera.start(description, arrived)?;
219                    devices.push(CaptureImplement::Camera(camera));
220                }
221                SourceType::Screen => {
222                    let screen = ScreenCapture::default();
223                    screen.start(description, arrived)?;
224                    devices.push(CaptureImplement::Screen(screen));
225                }
226                _ => (),
227            }
228        }
229
230        if let Some(SourceCaptureOptions {
231            description,
232            arrived,
233        }) = audio
234        {
235            let audio = AudioCapture::default();
236            audio.start(description, arrived)?;
237            devices.push(CaptureImplement::Audio(audio));
238        }
239
240        Ok(Self(devices))
241    }
242
243    /// Stop capturing and turn off internal audio/video frame pushing.
244    pub fn close(&self) -> Result<(), CaptureError> {
245        for item in self.0.iter() {
246            match item {
247                CaptureImplement::Screen(it) => it.stop()?,
248                CaptureImplement::Camera(it) => it.stop()?,
249                CaptureImplement::Audio(it) => it.stop()?,
250            };
251        }
252
253        log::info!("close capture");
254
255        Ok(())
256    }
257}
258
259impl Drop for Capture {
260    fn drop(&mut self) {
261        log::info!("capture drop");
262
263        drop(self.close());
264    }
265}