• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::collections::btree_map::Entry;
6 use std::collections::BTreeMap;
7 use std::convert::TryFrom;
8 
9 use anyhow::anyhow;
10 use base::error;
11 use base::warn;
12 use base::AsRawDescriptor;
13 use base::IntoRawDescriptor;
14 use libvda::decode::Event as LibvdaEvent;
15 
16 use crate::virtio::video::decoder::backend::*;
17 use crate::virtio::video::decoder::Capability;
18 use crate::virtio::video::error::VideoError;
19 use crate::virtio::video::error::VideoResult;
20 use crate::virtio::video::format::*;
21 
22 /// Since libvda only accepts 32-bit timestamps, we are going to truncate the frame 64-bit timestamp
23 /// (of nanosecond granularity) to only keep seconds granularity. This would result in information
24 /// being lost on a regular client, but the Android C2 decoder only sends timestamps with second
25 /// granularity, so this approach is going to work there. However, this means that this backend is
26 /// very unlikely to work with any other guest software. We accept this fact because it is
27 /// impossible to use outside of ChromeOS anyway.
28 const TIMESTAMP_TRUNCATE_FACTOR: u64 = 1_000_000_000;
29 
30 impl TryFrom<Format> for libvda::Profile {
31     type Error = VideoError;
32 
try_from(format: Format) -> Result<Self, Self::Error>33     fn try_from(format: Format) -> Result<Self, Self::Error> {
34         Ok(match format {
35             Format::VP8 => libvda::Profile::VP8,
36             Format::VP9 => libvda::Profile::VP9Profile0,
37             Format::H264 => libvda::Profile::H264ProfileBaseline,
38             Format::Hevc => libvda::Profile::HevcProfileMain,
39             _ => {
40                 error!("specified format {} is not supported by VDA", format);
41                 return Err(VideoError::InvalidParameter);
42             }
43         })
44     }
45 }
46 
47 impl TryFrom<Format> for libvda::PixelFormat {
48     type Error = VideoError;
49 
try_from(format: Format) -> Result<Self, Self::Error>50     fn try_from(format: Format) -> Result<Self, Self::Error> {
51         Ok(match format {
52             Format::NV12 => libvda::PixelFormat::NV12,
53             _ => {
54                 error!("specified format {} is not supported by VDA", format);
55                 return Err(VideoError::InvalidParameter);
56             }
57         })
58     }
59 }
60 
61 impl From<&FramePlane> for libvda::FramePlane {
from(plane: &FramePlane) -> Self62     fn from(plane: &FramePlane) -> Self {
63         libvda::FramePlane {
64             offset: plane.offset as i32,
65             stride: plane.stride as i32,
66         }
67     }
68 }
69 
70 impl From<libvda::decode::Event> for DecoderEvent {
from(event: libvda::decode::Event) -> Self71     fn from(event: libvda::decode::Event) -> Self {
72         // We cannot use the From trait here since neither libvda::decode::Response
73         // no std::result::Result are defined in the current crate.
74         fn vda_response_to_result(resp: libvda::decode::Response) -> VideoResult<()> {
75             match resp {
76                 libvda::decode::Response::Success => Ok(()),
77                 resp => Err(VideoError::BackendFailure(anyhow!("VDA failure: {}", resp))),
78             }
79         }
80 
81         match event {
82             LibvdaEvent::ProvidePictureBuffers {
83                 min_num_buffers,
84                 width,
85                 height,
86                 visible_rect_left,
87                 visible_rect_top,
88                 visible_rect_right,
89                 visible_rect_bottom,
90             } => DecoderEvent::ProvidePictureBuffers {
91                 min_num_buffers,
92                 width,
93                 height,
94                 visible_rect: Rect {
95                     left: visible_rect_left,
96                     top: visible_rect_top,
97                     right: visible_rect_right,
98                     bottom: visible_rect_bottom,
99                 },
100             },
101             LibvdaEvent::PictureReady {
102                 buffer_id,
103                 bitstream_id,
104                 left,
105                 top,
106                 right,
107                 bottom,
108             } => DecoderEvent::PictureReady {
109                 picture_buffer_id: buffer_id,
110                 // Restore the truncated timestamp to its original value (hopefully).
111                 timestamp: TIMESTAMP_TRUNCATE_FACTOR.wrapping_mul(bitstream_id as u64),
112                 visible_rect: Rect {
113                     left,
114                     top,
115                     right,
116                     bottom,
117                 },
118             },
119             LibvdaEvent::NotifyEndOfBitstreamBuffer { bitstream_id } => {
120                 // We will patch the timestamp to the actual bitstream ID in `read_event`.
121                 DecoderEvent::NotifyEndOfBitstreamBuffer(bitstream_id as u32)
122             }
123             LibvdaEvent::NotifyError(resp) => DecoderEvent::NotifyError(
124                 VideoError::BackendFailure(anyhow!("VDA failure: {}", resp)),
125             ),
126             LibvdaEvent::ResetResponse(resp) => {
127                 DecoderEvent::ResetCompleted(vda_response_to_result(resp))
128             }
129             LibvdaEvent::FlushResponse(resp) => {
130                 DecoderEvent::FlushCompleted(vda_response_to_result(resp))
131             }
132         }
133     }
134 }
135 
136 // Used by DecoderSession::get_capabilities().
from_pixel_format( fmt: &libvda::PixelFormat, mask: u64, width_range: FormatRange, height_range: FormatRange, ) -> FormatDesc137 fn from_pixel_format(
138     fmt: &libvda::PixelFormat,
139     mask: u64,
140     width_range: FormatRange,
141     height_range: FormatRange,
142 ) -> FormatDesc {
143     let format = match fmt {
144         libvda::PixelFormat::NV12 => Format::NV12,
145         libvda::PixelFormat::YV12 => Format::YUV420,
146     };
147 
148     let frame_formats = vec![FrameFormat {
149         width: width_range,
150         height: height_range,
151         bitrates: Vec::new(),
152     }];
153 
154     FormatDesc {
155         mask,
156         format,
157         frame_formats,
158         plane_align: 1,
159     }
160 }
161 
162 pub struct VdaDecoderSession {
163     vda_session: libvda::decode::Session,
164     format: Option<libvda::PixelFormat>,
165     /// libvda can only handle 32-bit timestamps, so we will give it the buffer ID as a timestamp
166     /// and map it back to the actual timestamp using this table when a decoded frame is produced.
167     timestamp_to_resource_id: BTreeMap<u32, u32>,
168 }
169 
170 impl DecoderSession for VdaDecoderSession {
set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()>171     fn set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()> {
172         self.format = Some(libvda::PixelFormat::try_from(format)?);
173         Ok(self.vda_session.set_output_buffer_count(buffer_count)?)
174     }
175 
decode( &mut self, resource_id: u32, timestamp: u64, resource: GuestResourceHandle, offset: u32, bytes_used: u32, ) -> VideoResult<()>176     fn decode(
177         &mut self,
178         resource_id: u32,
179         timestamp: u64,
180         resource: GuestResourceHandle,
181         offset: u32,
182         bytes_used: u32,
183     ) -> VideoResult<()> {
184         let handle = match resource {
185             GuestResourceHandle::VirtioObject(handle) => handle,
186             _ => {
187                 return Err(VideoError::BackendFailure(anyhow!(
188                     "VDA backend only supports virtio object resources"
189                 )))
190             }
191         };
192 
193         // While the virtio-video driver handles timestamps as nanoseconds, Chrome assumes
194         // per-second timestamps coming. So, we need a conversion from nsec to sec. Note that this
195         // value should not be an unix time stamp but a frame number that the Android V4L2 C2
196         // decoder passes to the driver as a 32-bit integer in our implementation. So, overflow must
197         // not happen in this conversion.
198         let truncated_timestamp = (timestamp / TIMESTAMP_TRUNCATE_FACTOR) as u32;
199         self.timestamp_to_resource_id
200             .insert(truncated_timestamp, resource_id);
201 
202         if truncated_timestamp as u64 * TIMESTAMP_TRUNCATE_FACTOR != timestamp {
203             warn!("truncation of timestamp {} resulted in precision loss. Only send timestamps with second granularity to this backend.", timestamp);
204         }
205 
206         Ok(self.vda_session.decode(
207             truncated_timestamp as i32, // bitstream_id
208             // Steal the descriptor of the resource, as libvda will close it.
209             handle.desc.into_raw_descriptor(),
210             offset,
211             bytes_used,
212         )?)
213     }
214 
flush(&mut self) -> VideoResult<()>215     fn flush(&mut self) -> VideoResult<()> {
216         Ok(self.vda_session.flush()?)
217     }
218 
reset(&mut self) -> VideoResult<()>219     fn reset(&mut self) -> VideoResult<()> {
220         Ok(self.vda_session.reset()?)
221     }
222 
clear_output_buffers(&mut self) -> VideoResult<()>223     fn clear_output_buffers(&mut self) -> VideoResult<()> {
224         Ok(())
225     }
226 
event_pipe(&self) -> &dyn AsRawDescriptor227     fn event_pipe(&self) -> &dyn AsRawDescriptor {
228         self.vda_session.pipe()
229     }
230 
use_output_buffer( &mut self, picture_buffer_id: i32, resource: GuestResource, ) -> VideoResult<()>231     fn use_output_buffer(
232         &mut self,
233         picture_buffer_id: i32,
234         resource: GuestResource,
235     ) -> VideoResult<()> {
236         let handle = match resource.handle {
237             GuestResourceHandle::VirtioObject(handle) => handle,
238             _ => {
239                 return Err(VideoError::BackendFailure(anyhow!(
240                     "VDA backend only supports virtio object resources"
241                 )))
242             }
243         };
244         let vda_planes: Vec<libvda::FramePlane> = resource.planes.iter().map(Into::into).collect();
245 
246         Ok(self.vda_session.use_output_buffer(
247             picture_buffer_id,
248             self.format.ok_or(VideoError::BackendFailure(anyhow!(
249                 "set_output_parameters() must be called before use_output_buffer()"
250             )))?,
251             // Steal the descriptor of the resource, as libvda will close it.
252             handle.desc.into_raw_descriptor(),
253             &vda_planes,
254             handle.modifier,
255         )?)
256     }
257 
reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()>258     fn reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()> {
259         Ok(self.vda_session.reuse_output_buffer(picture_buffer_id)?)
260     }
261 
read_event(&mut self) -> VideoResult<DecoderEvent>262     fn read_event(&mut self) -> VideoResult<DecoderEvent> {
263         self.vda_session
264             .read_event()
265             .map(Into::into)
266             // Libvda returned the truncated timestamp that we gave it as the timestamp of this
267             // buffer. Replace it with the bitstream ID that was passed to `decode` for this
268             // resource.
269             .map(|mut e| {
270                 if let DecoderEvent::NotifyEndOfBitstreamBuffer(timestamp) = &mut e {
271                     let bitstream_id = self
272                         .timestamp_to_resource_id
273                         .remove(timestamp)
274                         .unwrap_or_else(|| {
275                             error!("timestamp {} not registered!", *timestamp);
276                             0
277                         });
278                     *timestamp = bitstream_id;
279                 }
280                 e
281             })
282             .map_err(Into::into)
283     }
284 }
285 
286 /// A VDA decoder backend that can be passed to `Decoder::new` in order to create a working decoder.
287 pub struct LibvdaDecoder(libvda::decode::VdaInstance);
288 
289 impl LibvdaDecoder {
290     /// Create a decoder backend instance that can be used to instantiate an decoder.
new(backend_type: libvda::decode::VdaImplType) -> VideoResult<Self>291     pub fn new(backend_type: libvda::decode::VdaImplType) -> VideoResult<Self> {
292         Ok(Self(libvda::decode::VdaInstance::new(backend_type)?))
293     }
294 }
295 
296 impl DecoderBackend for LibvdaDecoder {
297     type Session = VdaDecoderSession;
298 
new_session(&mut self, format: Format) -> VideoResult<Self::Session>299     fn new_session(&mut self, format: Format) -> VideoResult<Self::Session> {
300         let profile = libvda::Profile::try_from(format)?;
301 
302         Ok(VdaDecoderSession {
303             vda_session: self.0.open_session(profile).map_err(|e| {
304                 error!("failed to open a session for {:?}: {}", format, e);
305                 VideoError::InvalidOperation
306             })?,
307             format: None,
308             timestamp_to_resource_id: Default::default(),
309         })
310     }
311 
get_capabilities(&self) -> Capability312     fn get_capabilities(&self) -> Capability {
313         let caps = libvda::decode::VdaInstance::get_capabilities(&self.0);
314 
315         // Raise the first |# of supported raw formats|-th bits because we can assume that any
316         // combination of (a coded format, a raw format) is valid in Chrome.
317         let mask = !(u64::max_value() << caps.output_formats.len());
318 
319         let mut in_fmts = vec![];
320         let mut profiles: BTreeMap<Format, Vec<Profile>> = Default::default();
321         for fmt in caps.input_formats.iter() {
322             match Profile::from_libvda_profile(fmt.profile) {
323                 Some(profile) => {
324                     let format = profile.to_format();
325                     in_fmts.push(FormatDesc {
326                         mask,
327                         format,
328                         frame_formats: vec![FrameFormat {
329                             width: FormatRange {
330                                 min: fmt.min_width,
331                                 max: fmt.max_width,
332                                 step: 1,
333                             },
334                             height: FormatRange {
335                                 min: fmt.min_height,
336                                 max: fmt.max_height,
337                                 step: 1,
338                             },
339                             bitrates: Vec::new(),
340                         }],
341                         plane_align: 1,
342                     });
343                     match profiles.entry(format) {
344                         Entry::Occupied(mut e) => e.get_mut().push(profile),
345                         Entry::Vacant(e) => {
346                             e.insert(vec![profile]);
347                         }
348                     }
349                 }
350                 None => {
351                     warn!(
352                         "No virtio-video equivalent for libvda profile, skipping: {:?}",
353                         fmt.profile
354                     );
355                 }
356             }
357         }
358 
359         let levels: BTreeMap<Format, Vec<Level>> = if profiles.contains_key(&Format::H264) {
360             // We only support Level 1.0 for H.264.
361             vec![(Format::H264, vec![Level::H264_1_0])]
362                 .into_iter()
363                 .collect()
364         } else {
365             Default::default()
366         };
367 
368         // Prepare {min, max} of {width, height}.
369         // While these values are associated with each input format in libvda,
370         // they are associated with each output format in virtio-video protocol.
371         // Thus, we compute max of min values and min of max values here.
372         let min_width = caps.input_formats.iter().map(|fmt| fmt.min_width).max();
373         let max_width = caps.input_formats.iter().map(|fmt| fmt.max_width).min();
374         let min_height = caps.input_formats.iter().map(|fmt| fmt.min_height).max();
375         let max_height = caps.input_formats.iter().map(|fmt| fmt.max_height).min();
376         let width_range = FormatRange {
377             min: min_width.unwrap_or(0),
378             max: max_width.unwrap_or(0),
379             step: 1,
380         };
381         let height_range = FormatRange {
382             min: min_height.unwrap_or(0),
383             max: max_height.unwrap_or(0),
384             step: 1,
385         };
386 
387         // Raise the first |# of supported coded formats|-th bits because we can assume that any
388         // combination of (a coded format, a raw format) is valid in Chrome.
389         let mask = !(u64::max_value() << caps.input_formats.len());
390         let out_fmts = caps
391             .output_formats
392             .iter()
393             .map(|fmt| from_pixel_format(fmt, mask, width_range, height_range))
394             .collect();
395 
396         Capability::new(in_fmts, out_fmts, profiles, levels)
397     }
398 }
399