libobs_wrapper\encoders/
mod.rs

1use std::{ffi::CStr, os::raw::c_char, str::FromStr};
2
3use num_traits::ToPrimitive;
4
5use crate::{
6    context::ObsContext,
7    enums::ObsEncoderType,
8    run_with_obs,
9    runtime::ObsRuntime,
10    utils::{ObsError, ENCODER_HIDE_FLAGS},
11};
12
13pub mod audio;
14mod enums;
15pub mod video;
16pub use enums::*;
17
18#[cfg_attr(not(feature = "blocking"), async_trait::async_trait)]
19pub trait ObsContextEncoders {
20    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
21    async fn get_best_video_encoder(&self) -> Result<ObsVideoEncoderType, ObsError>;
22
23    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
24    async fn get_best_audio_encoder(&self) -> Result<ObsAudioEncoderType, ObsError>;
25
26    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
27    async fn get_available_audio_encoders(&self) -> Result<Vec<ObsAudioEncoderType>, ObsError>;
28
29    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
30    async fn get_available_video_encoders(&self) -> Result<Vec<ObsVideoEncoderType>, ObsError>;
31}
32
33#[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
34async fn get_encoders_raw(
35    encoder_type: ObsEncoderType,
36    runtime: &ObsRuntime,
37) -> Result<Vec<String>, ObsError> {
38    let type_primitive = encoder_type.to_i32().unwrap();
39
40    run_with_obs!(runtime, move || {
41        let mut n = 0;
42        let mut encoders = Vec::new();
43
44        let mut ptr: *const c_char = unsafe { std::mem::zeroed() };
45        while unsafe { libobs::obs_enum_encoder_types(n, &mut ptr) } {
46            n += 1;
47            let cstring = unsafe { CStr::from_ptr(ptr) };
48            if let Ok(enc) = cstring.to_str() {
49                unsafe {
50                    let is_hidden = libobs::obs_get_encoder_caps(ptr) & ENCODER_HIDE_FLAGS != 0;
51                    if is_hidden || libobs::obs_get_encoder_type(ptr) != type_primitive {
52                        continue;
53                    }
54                }
55
56                log::debug!("Found encoder: {}", enc);
57                encoders.push(enc.into());
58            }
59        }
60
61        encoders.sort_unstable();
62        encoders
63    })
64    .await
65}
66
67#[cfg_attr(not(feature = "blocking"), async_trait::async_trait)]
68impl ObsContextEncoders for ObsContext {
69    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
70    async fn get_best_video_encoder(&self) -> Result<ObsVideoEncoderType, ObsError> {
71        Ok(self
72            .get_available_video_encoders()
73            .await?
74            .first()
75            .unwrap()
76            .clone())
77    }
78
79    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
80    async fn get_best_audio_encoder(&self) -> Result<ObsAudioEncoderType, ObsError> {
81        Ok(self
82            .get_available_audio_encoders()
83            .await?
84            .first()
85            .unwrap()
86            .clone())
87    }
88
89    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
90    async fn get_available_audio_encoders(&self) -> Result<Vec<ObsAudioEncoderType>, ObsError> {
91        Ok(get_encoders_raw(ObsEncoderType::Audio, &self.runtime)
92            .await?
93            .into_iter()
94            .map(|x| ObsAudioEncoderType::from_str(&x).unwrap())
95            .collect::<Vec<_>>())
96    }
97
98    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
99    async fn get_available_video_encoders(&self) -> Result<Vec<ObsVideoEncoderType>, ObsError> {
100        Ok(get_encoders_raw(ObsEncoderType::Video, &self.runtime)
101            .await?
102            .into_iter()
103            .map(|x| ObsVideoEncoderType::from_str(&x).unwrap())
104            .collect::<Vec<_>>())
105    }
106}