waycap_rs/encoders/
dynamic_encoder.rs

1use crossbeam::channel::Receiver;
2use ffmpeg_next::codec::encoder;
3
4use crate::{
5    encoders::{
6        nvenc_encoder::NvencEncoder,
7        vaapi_encoder::VaapiEncoder,
8        video::{PipewireSPA, ProcessingThread},
9    },
10    types::{
11        config::VideoEncoder as VideoEncoderType,
12        error::{Result, WaycapError},
13        video_frame::{EncodedVideoFrame, RawVideoFrame},
14    },
15    waycap_egl::{EglContext, GpuVendor},
16    VideoEncoder,
17};
18
19pub enum DynamicEncoder {
20    Vaapi(VaapiEncoder),
21    Nvenc(NvencEncoder),
22}
23
24impl DynamicEncoder {
25    pub(crate) fn new(
26        encoder_type: Option<VideoEncoderType>,
27        width: u32,
28        height: u32,
29        quality_preset: crate::types::config::QualityPreset,
30    ) -> crate::types::error::Result<DynamicEncoder> {
31        let encoder_type = match encoder_type {
32            Some(typ) => typ,
33            None => {
34                // Dummy dimensions we just use this go get GPU vendor then drop it
35                let dummy_context = EglContext::new(100, 100)?;
36                match dummy_context.get_gpu_vendor() {
37                    GpuVendor::NVIDIA => VideoEncoderType::H264Nvenc,
38                    GpuVendor::AMD | GpuVendor::INTEL => VideoEncoderType::H264Vaapi,
39                    GpuVendor::UNKNOWN => {
40                        return Err(WaycapError::Init(
41                            "Unknown/Unimplemented GPU vendor".to_string(),
42                        ));
43                    }
44                }
45            }
46        };
47        Ok(match encoder_type {
48            VideoEncoderType::H264Nvenc => {
49                DynamicEncoder::Nvenc(NvencEncoder::new(width, height, quality_preset)?)
50            }
51            VideoEncoderType::H264Vaapi => {
52                DynamicEncoder::Vaapi(VaapiEncoder::new(width, height, quality_preset)?)
53            }
54        })
55    }
56}
57
58impl VideoEncoder for DynamicEncoder {
59    type Output = EncodedVideoFrame;
60
61    fn reset(&mut self) -> Result<()> {
62        match self {
63            DynamicEncoder::Vaapi(enc) => enc.reset(),
64            DynamicEncoder::Nvenc(enc) => enc.reset(),
65        }
66    }
67
68    fn output(&mut self) -> Option<Receiver<Self::Output>> {
69        match self {
70            DynamicEncoder::Vaapi(enc) => enc.output(),
71            DynamicEncoder::Nvenc(enc) => enc.output(),
72        }
73    }
74
75    fn drop_processor(&mut self) {
76        match self {
77            DynamicEncoder::Vaapi(enc) => enc.drop_processor(),
78            DynamicEncoder::Nvenc(enc) => enc.drop_processor(),
79        }
80    }
81
82    fn drain(&mut self) -> Result<()> {
83        match self {
84            DynamicEncoder::Vaapi(enc) => enc.drain(),
85            DynamicEncoder::Nvenc(enc) => enc.drain(),
86        }
87    }
88
89    fn get_encoder(&self) -> &Option<encoder::Video> {
90        match self {
91            DynamicEncoder::Vaapi(enc) => enc.get_encoder(),
92            DynamicEncoder::Nvenc(enc) => enc.get_encoder(),
93        }
94    }
95}
96
97impl ProcessingThread for DynamicEncoder {
98    fn process(&mut self, frame: RawVideoFrame) -> Result<()> {
99        match self {
100            DynamicEncoder::Vaapi(enc) => enc.process(frame),
101            DynamicEncoder::Nvenc(enc) => enc.process(frame),
102        }
103    }
104    fn thread_setup(&mut self) -> Result<()> {
105        match self {
106            DynamicEncoder::Vaapi(enc) => enc.thread_setup(),
107            DynamicEncoder::Nvenc(enc) => enc.thread_setup(),
108        }
109    }
110
111    fn thread_teardown(&mut self) -> Result<()> {
112        match self {
113            DynamicEncoder::Vaapi(enc) => enc.thread_teardown(),
114            DynamicEncoder::Nvenc(enc) => enc.thread_teardown(),
115        }
116    }
117}
118
119impl PipewireSPA for DynamicEncoder {
120    fn get_spa_definition() -> Result<pipewire::spa::pod::Object> {
121        let dummy_context = EglContext::new(100, 100)?;
122        match dummy_context.get_gpu_vendor() {
123            GpuVendor::NVIDIA => NvencEncoder::get_spa_definition(),
124            GpuVendor::AMD | GpuVendor::INTEL => VaapiEncoder::get_spa_definition(),
125            GpuVendor::UNKNOWN => Err(WaycapError::Init(
126                "Unknown/Unimplemented GPU vendor".to_string(),
127            )),
128        }
129    }
130}