// 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,
}
}
}