diff options
Diffstat (limited to 'devices/src/virtio/video/decoder/capability.rs')
-rw-r--r-- | devices/src/virtio/video/decoder/capability.rs | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/devices/src/virtio/video/decoder/capability.rs b/devices/src/virtio/video/decoder/capability.rs new file mode 100644 index 0000000..b6d09af --- /dev/null +++ b/devices/src/virtio/video/decoder/capability.rs @@ -0,0 +1,138 @@ +// Copyright 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! Capablities of the virtio video decoder device. + +use std::collections::BTreeMap; + +use crate::virtio::video::control::*; +use crate::virtio::video::format::*; + +fn from_input_format(fmt: &libvda::InputFormat, mask: u64) -> FormatDesc { + let format = match fmt.profile { + libvda::Profile::VP8 => Format::VP8, + libvda::Profile::VP9Profile0 => Format::VP9, + libvda::Profile::H264 => Format::H264, + }; + FormatDesc { + mask, + format, + frame_formats: vec![Default::default()], + } +} + +fn from_pixel_format( + fmt: &libvda::PixelFormat, + mask: u64, + width_range: FormatRange, + height_range: FormatRange, +) -> FormatDesc { + let format = match fmt { + libvda::PixelFormat::NV12 => Format::NV12, + libvda::PixelFormat::YV12 => Format::YUV420, + }; + + let frame_formats = vec![FrameFormat { + width: width_range, + height: height_range, + bitrates: Vec::new(), + }]; + + FormatDesc { + mask, + format, + frame_formats, + } +} + +pub struct Capability { + pub in_fmts: Vec<FormatDesc>, + pub out_fmts: Vec<FormatDesc>, + + // Stores supporterd profiles and levels for each format. + profiles: BTreeMap<Format, Vec<Profile>>, + levels: BTreeMap<Format, Vec<Level>>, +} + +impl Capability { + pub fn new(caps: &libvda::Capabilities) -> Self { + // Raise the first |# of supported raw formats|-th bits because we can assume that any + // combination of (a coded format, a raw format) is valid in Chrome. + let mask = !(u64::max_value() << caps.output_formats.len()); + let in_fmts = caps + .input_formats + .iter() + .map(|fmt| from_input_format(fmt, mask)) + .collect(); + + // Prepare {min, max} of {width, height}. + // While these values are associated with each input format in libvda, + // they are associated with each output format in virtio-video protocol. + // Thus, we compute max of min values and min of max values here. + let min_width = caps.input_formats.iter().map(|fmt| fmt.min_width).max(); + let max_width = caps.input_formats.iter().map(|fmt| fmt.max_width).min(); + let min_height = caps.input_formats.iter().map(|fmt| fmt.min_height).max(); + let max_height = caps.input_formats.iter().map(|fmt| fmt.max_height).min(); + let width_range = FormatRange { + min: min_width.unwrap_or(0), + max: max_width.unwrap_or(0), + step: 1, + }; + let height_range = FormatRange { + min: min_height.unwrap_or(0), + max: max_height.unwrap_or(0), + step: 1, + }; + + // Raise the first |# of supported coded formats|-th bits because we can assume that any + // combination of (a coded format, a raw format) is valid in Chrome. + let mask = !(u64::max_value() << caps.input_formats.len()); + let out_fmts = caps + .output_formats + .iter() + .map(|fmt| from_pixel_format(fmt, mask, width_range, height_range)) + .collect(); + + let mut profiles: BTreeMap<Format, Vec<Profile>> = Default::default(); + let mut levels: BTreeMap<Format, Vec<Level>> = Default::default(); + for fmt in caps.input_formats.iter() { + match fmt.profile { + libvda::Profile::VP8 => { + profiles.insert(Format::VP8, vec![Profile::VP8Profile0]); + } + libvda::Profile::VP9Profile0 => { + profiles.insert(Format::VP9, vec![Profile::VP9Profile0]); + } + libvda::Profile::H264 => { + profiles.insert(Format::H264, vec![Profile::H264Baseline]); + levels.insert(Format::H264, vec![Level::H264_1_0]); + } + }; + } + + Capability { + in_fmts, + out_fmts, + profiles, + levels, + } + } + + pub fn query_control(&self, t: &QueryCtrlType) -> Option<QueryCtrlResponse> { + use QueryCtrlType::*; + match *t { + Profile(fmt) => { + let profiles = self.profiles.get(&fmt)?; + Some(QueryCtrlResponse::Profile( + profiles.iter().copied().collect(), + )) + } + Level(fmt) => { + let levels = self.levels.get(&fmt)?; + Some(QueryCtrlResponse::Level(levels.iter().copied().collect())) + } + _ => None, + } + } +} |