libobs_wrapper\encoders/
mod.rs

1//! This module contains video- and audio-encoders.
2//! In simple terms, you need a video and audio encoder, so a proper mp4
3//! file can get written to disk (with video and audio).
4//! If you are unsure which encoder to use, you might as well
5//! take a look at the [libobs-simple](https://crates.io/crates/libobs-simple) crate for
6//! a output that has all needed encoders attached already.
7
8use crate::{
9    context::ObsContext,
10    enums::{ObsEncoderType, OsEnumType},
11    impl_obs_drop, run_with_obs,
12    runtime::ObsRuntime,
13    unsafe_send::Sendable,
14    utils::{ObsDropGuard, ObsError, ENCODER_HIDE_FLAGS},
15};
16use std::{ffi::CStr, os::raw::c_char};
17
18pub mod audio;
19mod enums;
20mod traits;
21pub use traits::*;
22mod property_helper;
23pub use property_helper::*;
24pub mod video;
25pub use enums::*;
26
27pub trait ObsContextEncoders {
28    fn best_video_encoder(&self) -> Result<ObsVideoEncoderBuilder, ObsError>;
29
30    fn best_audio_encoder(&self) -> Result<ObsAudioEncoderBuilder, ObsError>;
31
32    fn available_audio_encoders(&self) -> Result<Vec<ObsAudioEncoderBuilder>, ObsError>;
33
34    fn available_video_encoders(&self) -> Result<Vec<ObsVideoEncoderBuilder>, ObsError>;
35}
36
37fn get_encoders_raw(
38    encoder_type: ObsEncoderType,
39    runtime: &ObsRuntime,
40) -> Result<Vec<String>, ObsError> {
41    let type_primitive = encoder_type as OsEnumType;
42
43    run_with_obs!(runtime, move || {
44        let mut n = 0;
45        let mut encoders = Vec::new();
46
47        let mut ptr: *const c_char = unsafe {
48            // Safety: We are not dereferencing this pointer yet, it is first set by the method below
49            // and we are in the runtime
50            std::mem::zeroed()
51        };
52        while unsafe {
53            // Safety: We initialized the memory above and are modifying the pointer in the loop
54            libobs::obs_enum_encoder_types(n, &mut ptr)
55        } {
56            n += 1;
57            if ptr.is_null() {
58                continue;
59            }
60
61            let cstring = unsafe {
62                // Safety: We made sure that the pointer is not null, so it must be valid
63                CStr::from_ptr(ptr)
64            };
65            if let Ok(enc) = cstring.to_str() {
66                unsafe {
67                    // Safety: We know know that the pointer is valid, therefore we can use it again
68                    let is_hidden = libobs::obs_get_encoder_caps(ptr) & ENCODER_HIDE_FLAGS != 0;
69                    if is_hidden || libobs::obs_get_encoder_type(ptr) != type_primitive {
70                        continue;
71                    }
72                }
73
74                log::debug!("Found encoder: {}", enc);
75                encoders.push(enc.into());
76            }
77        }
78
79        encoders.sort_unstable();
80        encoders
81    })
82}
83
84impl ObsContextEncoders for ObsContext {
85    fn best_video_encoder(&self) -> Result<ObsVideoEncoderBuilder, ObsError> {
86        let encoders = self.available_video_encoders()?;
87        encoders
88            .into_iter()
89            .next()
90            .ok_or(ObsError::NoAvailableEncoders)
91    }
92
93    fn best_audio_encoder(&self) -> Result<ObsAudioEncoderBuilder, ObsError> {
94        let encoders = self.available_audio_encoders()?;
95        encoders
96            .into_iter()
97            .next()
98            .ok_or(ObsError::NoAvailableEncoders)
99    }
100
101    fn available_audio_encoders(&self) -> Result<Vec<ObsAudioEncoderBuilder>, ObsError> {
102        Ok(get_encoders_raw(ObsEncoderType::Audio, self.runtime())?
103            .into_iter()
104            .map(|x| ObsAudioEncoderBuilder::new(self.clone(), &x))
105            .collect::<Vec<_>>())
106    }
107
108    fn available_video_encoders(&self) -> Result<Vec<ObsVideoEncoderBuilder>, ObsError> {
109        Ok(get_encoders_raw(ObsEncoderType::Video, self.runtime())?
110            .into_iter()
111            .map(|x| ObsVideoEncoderBuilder::new(self.clone(), &x))
112            .collect::<Vec<_>>())
113    }
114}
115
116#[derive(Debug)]
117pub(super) struct _ObsEncoderDropGuard {
118    encoder: Sendable<*mut libobs::obs_encoder_t>,
119    runtime: ObsRuntime,
120}
121
122impl ObsDropGuard for _ObsEncoderDropGuard {}
123
124impl_obs_drop!(_ObsEncoderDropGuard, (encoder), move || unsafe {
125    // Safety: The pointer is valid because we are in the runtime and the guard is alive.
126    libobs::obs_encoder_release(encoder.0);
127});