libobs_wrapper\encoders/
mod.rs1use 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}