summary refs log tree commit diff
path: root/devices/src/virtio/video/decoder/capability.rs
blob: b6d09af9b5961a45d832f77a9cc1465ac87e8b17 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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,
        }
    }
}