oximedia_transcode/
codec_dispatch.rs1use crate::{Result, TranscodeError};
19use oximedia_codec::traits::VideoEncoder;
20#[cfg(feature = "mjpeg")]
21use oximedia_codec::CodecError;
22use oximedia_core::CodecId;
23
24#[derive(Debug, Clone)]
26pub struct VideoEncoderParams {
27 pub width: u32,
29 pub height: u32,
31 pub quality: u8,
35}
36
37impl VideoEncoderParams {
38 pub fn new(width: u32, height: u32, quality: u8) -> Result<Self> {
44 if width == 0 || height == 0 {
45 return Err(TranscodeError::InvalidInput(
46 "width and height must be non-zero".into(),
47 ));
48 }
49 Ok(Self {
50 width,
51 height,
52 quality,
53 })
54 }
55}
56
57pub fn make_video_encoder(
65 codec_id: CodecId,
66 params: &VideoEncoderParams,
67) -> Result<Box<dyn VideoEncoder>> {
68 match codec_id {
69 CodecId::Mjpeg => make_mjpeg_encoder(params),
70 CodecId::Apv => make_apv_encoder(params),
71 other => Err(TranscodeError::Unsupported(format!(
72 "codec {other:?} is not handled by codec_dispatch; \
73 use the stream-copy pipeline for this codec"
74 ))),
75 }
76}
77
78#[cfg(feature = "mjpeg")]
81fn make_mjpeg_encoder(params: &VideoEncoderParams) -> Result<Box<dyn VideoEncoder>> {
82 use oximedia_codec::{MjpegConfig, MjpegEncoder};
83
84 let config = MjpegConfig::new(params.width, params.height)
85 .map_err(|e| TranscodeError::CodecError(e.to_string()))?
86 .with_quality(params.quality);
87
88 let encoder = MjpegEncoder::new(config)
89 .map_err(|e: CodecError| TranscodeError::CodecError(e.to_string()))?;
90
91 Ok(Box::new(encoder))
92}
93
94#[cfg(not(feature = "mjpeg"))]
95fn make_mjpeg_encoder(_params: &VideoEncoderParams) -> Result<Box<dyn VideoEncoder>> {
96 Err(TranscodeError::Unsupported(
97 "MJPEG support requires the `mjpeg` feature of oximedia-codec".into(),
98 ))
99}
100
101#[cfg(feature = "apv")]
104fn make_apv_encoder(params: &VideoEncoderParams) -> Result<Box<dyn VideoEncoder>> {
105 use oximedia_codec::{ApvConfig, ApvEncoder};
106
107 let config = ApvConfig::new(params.width, params.height)
108 .map_err(|e| TranscodeError::CodecError(e.to_string()))?
109 .with_qp(params.quality);
110
111 let encoder = ApvEncoder::new(config).map_err(|e| TranscodeError::CodecError(e.to_string()))?;
112
113 Ok(Box::new(encoder))
114}
115
116#[cfg(not(feature = "apv"))]
117fn make_apv_encoder(_params: &VideoEncoderParams) -> Result<Box<dyn VideoEncoder>> {
118 Err(TranscodeError::Unsupported(
119 "APV support requires the `apv` feature of oximedia-codec".into(),
120 ))
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_params_new_valid() {
129 let p = VideoEncoderParams::new(1920, 1080, 85);
130 assert!(p.is_ok());
131 let p = p.expect("valid params");
132 assert_eq!(p.width, 1920);
133 assert_eq!(p.height, 1080);
134 assert_eq!(p.quality, 85);
135 }
136
137 #[test]
138 fn test_params_zero_width() {
139 assert!(VideoEncoderParams::new(0, 1080, 85).is_err());
140 }
141
142 #[test]
143 fn test_params_zero_height() {
144 assert!(VideoEncoderParams::new(1920, 0, 85).is_err());
145 }
146
147 #[test]
148 fn test_unsupported_codec() {
149 let p = VideoEncoderParams::new(320, 240, 30).expect("valid");
150 let result = make_video_encoder(CodecId::Vp9, &p);
151 assert!(result.is_err());
152 if let Err(e) = result {
154 assert!(matches!(e, TranscodeError::Unsupported(_)));
155 }
156 }
157
158 #[cfg(feature = "mjpeg")]
159 #[test]
160 fn test_make_mjpeg_encoder() {
161 let p = VideoEncoderParams::new(320, 240, 85).expect("valid");
162 let enc = make_video_encoder(CodecId::Mjpeg, &p);
163 assert!(enc.is_ok(), "MJPEG encoder should build");
164 let enc = enc.expect("ok");
165 assert_eq!(enc.codec(), CodecId::Mjpeg);
166 }
167
168 #[cfg(feature = "apv")]
169 #[test]
170 fn test_make_apv_encoder() {
171 let p = VideoEncoderParams::new(320, 240, 22).expect("valid");
172 let enc = make_video_encoder(CodecId::Apv, &p);
173 assert!(enc.is_ok(), "APV encoder should build");
174 let enc = enc.expect("ok");
175 assert_eq!(enc.codec(), CodecId::Apv);
176 }
177
178 #[cfg(not(feature = "mjpeg"))]
179 #[test]
180 fn test_mjpeg_disabled() {
181 let p = VideoEncoderParams::new(320, 240, 85).expect("valid");
182 let result = make_video_encoder(CodecId::Mjpeg, &p);
183 assert!(matches!(result, Err(TranscodeError::Unsupported(_))));
184 }
185
186 #[cfg(not(feature = "apv"))]
187 #[test]
188 fn test_apv_disabled() {
189 let p = VideoEncoderParams::new(320, 240, 22).expect("valid");
190 let result = make_video_encoder(CodecId::Apv, &p);
191 assert!(matches!(result, Err(TranscodeError::Unsupported(_))));
192 }
193}