Skip to main content

waycap_rs/encoders/
dynamic_encoder.rs

1use crossbeam::channel::Receiver;
2use ffmpeg_next::codec::encoder;
3
4use crate::{
5    encoders::video::{PipewireSPA, ProcessingThread},
6    types::{
7        config::VideoEncoder as VideoEncoderType,
8        error::Result,
9        video_frame::{EncodedVideoFrame, RawVideoFrame},
10    },
11    VideoEncoder,
12};
13
14#[cfg(feature = "vaapi")]
15use crate::encoders::vaapi_encoder::VaapiEncoder;
16
17#[cfg(feature = "nvidia")]
18use crate::encoders::nvenc_encoder::NvencEncoder;
19#[cfg(all(feature = "vaapi", feature = "nvidia"))]
20use crate::types::error::WaycapError;
21
22// detect_gpu_vendor is only needed when both encoders are available for auto-selection
23#[cfg(all(feature = "vaapi", feature = "nvidia", feature = "vulkan"))]
24use crate::waycap_vulkan::{detect_gpu_vendor, GpuVendor};
25#[cfg(all(feature = "vaapi", feature = "nvidia", feature = "egl"))]
26use crate::waycap_egl::{detect_gpu_vendor, GpuVendor};
27
28pub enum DynamicEncoder {
29    #[cfg(feature = "vaapi")]
30    Vaapi(VaapiEncoder),
31    #[cfg(feature = "nvidia")]
32    Nvenc(NvencEncoder),
33}
34
35impl DynamicEncoder {
36    pub(crate) fn new(
37        encoder_type: Option<VideoEncoderType>,
38        width: u32,
39        height: u32,
40        quality_preset: crate::types::config::QualityPreset,
41    ) -> crate::types::error::Result<DynamicEncoder> {
42        let encoder_type = match encoder_type {
43            Some(typ) => typ,
44            // Both available: detect GPU to pick
45            #[cfg(all(feature = "vaapi", feature = "nvidia"))]
46            None => match detect_gpu_vendor()? {
47                GpuVendor::NVIDIA => VideoEncoderType::H264Nvenc,
48                GpuVendor::AMD | GpuVendor::INTEL => VideoEncoderType::H264Vaapi,
49                GpuVendor::UNKNOWN => {
50                    return Err(WaycapError::Init(
51                        "Unknown/Unimplemented GPU vendor".to_string(),
52                    ));
53                }
54            },
55            // Only one available: use it directly
56            #[cfg(all(feature = "vaapi", not(feature = "nvidia")))]
57            None => VideoEncoderType::H264Vaapi,
58            #[cfg(all(feature = "nvidia", not(feature = "vaapi")))]
59            None => VideoEncoderType::H264Nvenc,
60        };
61        Ok(match encoder_type {
62            #[cfg(feature = "vaapi")]
63            VideoEncoderType::H264Vaapi => {
64                DynamicEncoder::Vaapi(VaapiEncoder::new(width, height, quality_preset)?)
65            }
66            #[cfg(feature = "nvidia")]
67            VideoEncoderType::H264Nvenc => {
68                DynamicEncoder::Nvenc(NvencEncoder::new(width, height, quality_preset)?)
69            }
70        })
71    }
72}
73
74impl VideoEncoder for DynamicEncoder {
75    type Output = EncodedVideoFrame;
76
77    fn reset(&mut self) -> Result<()> {
78        match self {
79            #[cfg(feature = "vaapi")]
80            DynamicEncoder::Vaapi(enc) => enc.reset(),
81            #[cfg(feature = "nvidia")]
82            DynamicEncoder::Nvenc(enc) => enc.reset(),
83        }
84    }
85
86    fn output(&mut self) -> Option<Receiver<Self::Output>> {
87        match self {
88            #[cfg(feature = "vaapi")]
89            DynamicEncoder::Vaapi(enc) => enc.output(),
90            #[cfg(feature = "nvidia")]
91            DynamicEncoder::Nvenc(enc) => enc.output(),
92        }
93    }
94
95    fn drop_processor(&mut self) {
96        match self {
97            #[cfg(feature = "vaapi")]
98            DynamicEncoder::Vaapi(enc) => enc.drop_processor(),
99            #[cfg(feature = "nvidia")]
100            DynamicEncoder::Nvenc(enc) => enc.drop_processor(),
101        }
102    }
103
104    fn drain(&mut self) -> Result<()> {
105        match self {
106            #[cfg(feature = "vaapi")]
107            DynamicEncoder::Vaapi(enc) => enc.drain(),
108            #[cfg(feature = "nvidia")]
109            DynamicEncoder::Nvenc(enc) => enc.drain(),
110        }
111    }
112
113    fn get_encoder(&self) -> &Option<encoder::Video> {
114        match self {
115            #[cfg(feature = "vaapi")]
116            DynamicEncoder::Vaapi(enc) => enc.get_encoder(),
117            #[cfg(feature = "nvidia")]
118            DynamicEncoder::Nvenc(enc) => enc.get_encoder(),
119        }
120    }
121}
122
123impl ProcessingThread for DynamicEncoder {
124    fn process(&mut self, frame: RawVideoFrame) -> Result<()> {
125        match self {
126            #[cfg(feature = "vaapi")]
127            DynamicEncoder::Vaapi(enc) => enc.process(frame),
128            #[cfg(feature = "nvidia")]
129            DynamicEncoder::Nvenc(enc) => enc.process(frame),
130        }
131    }
132
133    fn thread_setup(&mut self) -> Result<()> {
134        match self {
135            #[cfg(feature = "vaapi")]
136            DynamicEncoder::Vaapi(enc) => enc.thread_setup(),
137            #[cfg(feature = "nvidia")]
138            DynamicEncoder::Nvenc(enc) => enc.thread_setup(),
139        }
140    }
141
142    fn thread_teardown(&mut self) -> Result<()> {
143        match self {
144            #[cfg(feature = "vaapi")]
145            DynamicEncoder::Vaapi(enc) => enc.thread_teardown(),
146            #[cfg(feature = "nvidia")]
147            DynamicEncoder::Nvenc(enc) => enc.thread_teardown(),
148        }
149    }
150}
151
152impl PipewireSPA for DynamicEncoder {
153    #[allow(unreachable_code)]
154    fn get_spa_definition() -> Result<pipewire::spa::pod::Object> {
155        // Both available: detect GPU to pick the right format
156        #[cfg(all(feature = "vaapi", feature = "nvidia"))]
157        return match detect_gpu_vendor()? {
158            GpuVendor::NVIDIA => NvencEncoder::get_spa_definition(),
159            GpuVendor::AMD | GpuVendor::INTEL => VaapiEncoder::get_spa_definition(),
160            GpuVendor::UNKNOWN => Err(WaycapError::Init(
161                "Unknown/Unimplemented GPU vendor".to_string(),
162            )),
163        };
164        #[cfg(all(feature = "vaapi", not(feature = "nvidia")))]
165        return VaapiEncoder::get_spa_definition();
166        #[cfg(all(feature = "nvidia", not(feature = "vaapi")))]
167        return NvencEncoder::get_spa_definition();
168        unreachable!()
169    }
170}