devices/virtio/video/encoder/backend/
vda.rs1use std::collections::btree_map::Entry;
6use std::collections::BTreeMap;
7
8use anyhow::anyhow;
9use anyhow::Context;
10use base::error;
11use base::warn;
12use base::AsRawDescriptor;
13use base::IntoRawDescriptor;
14use libvda::encode::EncodeCapabilities;
15use libvda::encode::VeaImplType;
16use libvda::encode::VeaInstance;
17
18use super::*;
19use crate::virtio::video::encoder::*;
20use crate::virtio::video::error::VideoError;
21use crate::virtio::video::error::VideoResult;
22use crate::virtio::video::format::Bitrate;
23use crate::virtio::video::format::Format;
24use crate::virtio::video::format::FormatDesc;
25use crate::virtio::video::format::FormatRange;
26use crate::virtio::video::format::FrameFormat;
27use crate::virtio::video::format::Level;
28use crate::virtio::video::format::Profile;
29use crate::virtio::video::resource::GuestResource;
30use crate::virtio::video::resource::GuestResourceHandle;
31
32impl From<Bitrate> for libvda::encode::Bitrate {
33 fn from(bitrate: Bitrate) -> Self {
34 libvda::encode::Bitrate {
35 mode: match bitrate {
36 Bitrate::Vbr { .. } => libvda::encode::BitrateMode::VBR,
37 Bitrate::Cbr { .. } => libvda::encode::BitrateMode::CBR,
38 },
39 target: bitrate.target(),
40 peak: match &bitrate {
41 Bitrate::Cbr { .. } => 0,
43 Bitrate::Vbr { peak, .. } => *peak,
44 },
45 }
46 }
47}
48
49pub struct LibvdaEncoder {
52 instance: VeaInstance,
53 capabilities: EncoderCapabilities,
54}
55
56impl LibvdaEncoder {
57 pub fn new() -> VideoResult<Self> {
58 let instance = VeaInstance::new(VeaImplType::Gavea)?;
59
60 let EncodeCapabilities {
61 input_formats,
62 output_formats,
63 } = instance.get_capabilities();
64
65 if input_formats.is_empty() || output_formats.is_empty() {
66 error!("No input or output formats.");
67 return Err(VideoError::InvalidFormat);
68 }
69
70 let input_format_descs: Vec<FormatDesc> = input_formats
71 .iter()
72 .map(|input_format| {
73 let format = match input_format {
74 libvda::PixelFormat::NV12 => Format::NV12,
75 libvda::PixelFormat::YV12 => Format::YUV420,
76 };
77
78 FormatDesc {
83 mask: !(u64::MAX << output_formats.len()),
84 format,
85 frame_formats: vec![FrameFormat {
86 width: FormatRange {
87 min: 2,
88 max: 4096,
89 step: 1,
90 },
91 height: FormatRange {
92 min: 2,
93 max: 4096,
94 step: 1,
95 },
96 bitrates: vec![FormatRange {
97 min: 0,
98 max: 8000,
99 step: 1,
100 }],
101 }],
102 plane_align: 1,
103 }
104 })
105 .collect();
106
107 if !input_format_descs
108 .iter()
109 .any(|fd| fd.format == Format::NV12)
110 {
111 error!("libvda encoder does not support NV12.");
113 return Err(VideoError::InvalidFormat);
114 }
115
116 struct ParsedFormat {
117 profiles: Vec<Profile>,
118 max_width: u32,
119 max_height: u32,
120 }
121 let mut parsed_formats: BTreeMap<Format, ParsedFormat> = BTreeMap::new();
122
123 for output_format in output_formats.iter() {
124 let libvda::encode::OutputProfile {
127 profile: libvda_profile,
128 max_width,
129 max_height,
130 ..
131 } = output_format;
132
133 let profile = match Profile::from_libvda_profile(*libvda_profile) {
134 Some(p) => p,
135 None => {
136 warn!("Skipping unsupported libvda profile: {:?}", libvda_profile);
137 continue;
138 }
139 };
140
141 match parsed_formats.entry(profile.to_format()) {
142 Entry::Occupied(mut occupied_entry) => {
143 let parsed_format = occupied_entry.get_mut();
144 parsed_format.profiles.push(profile);
145 parsed_format.max_width = std::cmp::min(*max_width, parsed_format.max_width);
149 parsed_format.max_height = std::cmp::min(*max_height, parsed_format.max_height);
150 }
151 Entry::Vacant(vacant_entry) => {
152 vacant_entry.insert(ParsedFormat {
153 profiles: vec![profile],
154 max_width: *max_width,
155 max_height: *max_height,
156 });
157 }
158 }
159 }
160
161 let mut output_format_descs = vec![];
162 let mut coded_format_profiles = BTreeMap::new();
163 for (format, parsed_format) in parsed_formats.into_iter() {
164 let ParsedFormat {
165 mut profiles,
166 max_width,
167 max_height,
168 } = parsed_format;
169
170 output_format_descs.push(FormatDesc {
171 mask: !(u64::MAX << output_formats.len()),
172 format,
173 frame_formats: vec![FrameFormat {
174 width: FormatRange {
175 min: 2,
176 max: max_width,
177 step: 1,
178 },
179 height: FormatRange {
180 min: 2,
181 max: max_height,
182 step: 1,
183 },
184 bitrates: vec![FormatRange {
185 min: 0,
186 max: 8000,
187 step: 1,
188 }],
189 }],
190 plane_align: 1,
191 });
192
193 profiles.sort_unstable();
194 coded_format_profiles.insert(format, profiles);
195 }
196
197 Ok(LibvdaEncoder {
198 instance,
199 capabilities: EncoderCapabilities {
200 input_format_descs,
201 output_format_descs,
202 coded_format_profiles,
203 },
204 })
205 }
206}
207
208impl Encoder for LibvdaEncoder {
209 type Session = LibvdaEncoderSession;
210
211 fn query_capabilities(&self) -> VideoResult<EncoderCapabilities> {
212 Ok(self.capabilities.clone())
213 }
214
215 fn start_session(&mut self, config: SessionConfig) -> VideoResult<LibvdaEncoderSession> {
216 if config.dst_params.format.is_none() {
217 return Err(VideoError::InvalidArgument);
218 }
219
220 let input_format = match config
221 .src_params
222 .format
223 .ok_or(VideoError::InvalidArgument)?
224 {
225 Format::NV12 => libvda::PixelFormat::NV12,
226 Format::YUV420 => libvda::PixelFormat::YV12,
227 unsupported_format => {
228 error!("Unsupported libvda format: {}", unsupported_format);
229 return Err(VideoError::InvalidArgument);
230 }
231 };
232
233 let output_profile = match config.dst_profile.to_libvda_profile() {
234 Some(p) => p,
235 None => {
236 error!("Unsupported libvda profile");
237 return Err(VideoError::InvalidArgument);
238 }
239 };
240
241 let config = libvda::encode::Config {
242 input_format,
243 input_visible_width: config.src_params.frame_width,
244 input_visible_height: config.src_params.frame_height,
245 output_profile,
246 bitrate: config.dst_bitrate.into(),
247 initial_framerate: if config.frame_rate == 0 {
248 None
249 } else {
250 Some(config.frame_rate)
251 },
252 h264_output_level: config.dst_h264_level.map(|level| {
253 match level {
255 Level::H264_1_0 => 10,
256 Level::H264_1_1 => 11,
257 Level::H264_1_2 => 12,
258 Level::H264_1_3 => 13,
259 Level::H264_2_0 => 20,
260 Level::H264_2_1 => 21,
261 Level::H264_2_2 => 22,
262 Level::H264_3_0 => 30,
263 Level::H264_3_1 => 31,
264 Level::H264_3_2 => 32,
265 Level::H264_4_0 => 40,
266 Level::H264_4_1 => 41,
267 Level::H264_4_2 => 42,
268 Level::H264_5_0 => 50,
269 Level::H264_5_1 => 51,
270 }
271 }),
272 };
273
274 let session = self.instance.open_session(config)?;
275
276 Ok(LibvdaEncoderSession {
277 session,
278 next_input_buffer_id: 1,
279 next_output_buffer_id: 1,
280 })
281 }
282
283 fn stop_session(&mut self, _session: LibvdaEncoderSession) -> VideoResult<()> {
284 Ok(())
286 }
287}
288
289pub struct LibvdaEncoderSession {
290 session: libvda::encode::Session,
291 next_input_buffer_id: InputBufferId,
292 next_output_buffer_id: OutputBufferId,
293}
294
295impl EncoderSession for LibvdaEncoderSession {
296 fn encode(
297 &mut self,
298 resource: GuestResource,
299 timestamp: u64,
300 force_keyframe: bool,
301 ) -> VideoResult<InputBufferId> {
302 let input_buffer_id = self.next_input_buffer_id;
303 let desc = match resource.handle {
304 GuestResourceHandle::VirtioObject(handle) => handle.desc,
305 _ => {
306 return Err(VideoError::BackendFailure(anyhow!(
307 "VDA backend only supports virtio object resources"
308 )))
309 }
310 };
311
312 let libvda_planes = resource
313 .planes
314 .iter()
315 .map(|plane| libvda::FramePlane {
316 offset: plane.offset as i32,
317 stride: plane.stride as i32,
318 })
319 .collect::<Vec<_>>();
320
321 self.session.encode(
322 input_buffer_id as i32,
323 desc.into_raw_descriptor(),
325 &libvda_planes,
326 timestamp as i64,
327 force_keyframe,
328 )?;
329
330 self.next_input_buffer_id = self.next_input_buffer_id.wrapping_add(1);
331
332 Ok(input_buffer_id)
333 }
334
335 fn use_output_buffer(
336 &mut self,
337 resource: GuestResourceHandle,
338 offset: u32,
339 size: u32,
340 ) -> VideoResult<OutputBufferId> {
341 let output_buffer_id = self.next_output_buffer_id;
342 let desc = match resource {
343 GuestResourceHandle::VirtioObject(handle) => handle.desc,
344 _ => {
345 return Err(VideoError::BackendFailure(anyhow!(
346 "VDA backend only supports virtio object resources"
347 )))
348 }
349 };
350
351 self.session.use_output_buffer(
352 output_buffer_id as i32,
353 desc.into_raw_descriptor(),
355 offset,
356 size,
357 )?;
358
359 self.next_output_buffer_id = self.next_output_buffer_id.wrapping_add(1);
360
361 Ok(output_buffer_id)
362 }
363
364 fn flush(&mut self) -> VideoResult<()> {
365 self.session
366 .flush()
367 .context("while flushing")
368 .map_err(VideoError::BackendFailure)
369 }
370
371 fn request_encoding_params_change(
372 &mut self,
373 bitrate: Bitrate,
374 framerate: u32,
375 ) -> VideoResult<()> {
376 self.session
377 .request_encoding_params_change(bitrate.into(), framerate)
378 .context("while requesting encoder parameter change")
379 .map_err(VideoError::BackendFailure)
380 }
381
382 fn event_pipe(&self) -> &dyn AsRawDescriptor {
383 self.session.pipe()
384 }
385
386 fn read_event(&mut self) -> VideoResult<EncoderEvent> {
387 let event = self.session.read_event()?;
388
389 use libvda::encode::Event::*;
390 let encoder_event = match event {
391 RequireInputBuffers {
392 input_count,
393 input_frame_width,
394 input_frame_height,
395 output_buffer_size,
396 } => EncoderEvent::RequireInputBuffers {
397 input_count,
398 input_frame_width,
399 input_frame_height,
400 output_buffer_size,
401 },
402 ProcessedInputBuffer(id) => EncoderEvent::ProcessedInputBuffer { id: id as u32 },
403 ProcessedOutputBuffer {
404 output_buffer_id,
405 payload_size,
406 key_frame,
407 timestamp,
408 ..
409 } => EncoderEvent::ProcessedOutputBuffer {
410 id: output_buffer_id as u32,
411 bytesused: payload_size,
412 keyframe: key_frame,
413 timestamp: timestamp as u64,
414 },
415 FlushResponse { flush_done } => EncoderEvent::FlushResponse { flush_done },
416 NotifyError(err) => EncoderEvent::NotifyError {
417 error: VideoError::BackendFailure(anyhow!(err)),
418 },
419 };
420
421 Ok(encoder_event)
422 }
423}