summary refs log blame commit diff
path: root/devices/src/virtio/video/decoder/capability.rs
blob: b6d09af9b5961a45d832f77a9cc1465ac87e8b17 (plain) (tree)









































































































































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