1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! Data structures that represent video format information in virtio video devices.
6
7 use std::convert::{From, Into, TryFrom};
8 use std::fmt::{self, Display};
9 use std::io;
10
11 use base::error;
12 use data_model::Le32;
13 use enumn::N;
14
15 use crate::virtio::video::command::ReadCmdError;
16 use crate::virtio::video::protocol::*;
17 use crate::virtio::video::response::Response;
18 use crate::virtio::Writer;
19
20 #[derive(PartialEq, Eq, PartialOrd, Ord, N, Clone, Copy, Debug)]
21 #[repr(u32)]
22 pub enum Profile {
23 H264Baseline = VIRTIO_VIDEO_PROFILE_H264_BASELINE,
24 H264Main = VIRTIO_VIDEO_PROFILE_H264_MAIN,
25 H264Extended = VIRTIO_VIDEO_PROFILE_H264_EXTENDED,
26 H264High = VIRTIO_VIDEO_PROFILE_H264_HIGH,
27 H264High10 = VIRTIO_VIDEO_PROFILE_H264_HIGH10PROFILE,
28 H264High422 = VIRTIO_VIDEO_PROFILE_H264_HIGH422PROFILE,
29 H264High444PredictiveProfile = VIRTIO_VIDEO_PROFILE_H264_HIGH444PREDICTIVEPROFILE,
30 H264ScalableBaseline = VIRTIO_VIDEO_PROFILE_H264_SCALABLEBASELINE,
31 H264ScalableHigh = VIRTIO_VIDEO_PROFILE_H264_SCALABLEHIGH,
32 H264StereoHigh = VIRTIO_VIDEO_PROFILE_H264_STEREOHIGH,
33 H264MultiviewHigh = VIRTIO_VIDEO_PROFILE_H264_MULTIVIEWHIGH,
34 HevcMain = VIRTIO_VIDEO_PROFILE_HEVC_MAIN,
35 HevcMain10 = VIRTIO_VIDEO_PROFILE_HEVC_MAIN10,
36 HevcMainStillPicture = VIRTIO_VIDEO_PROFILE_HEVC_MAIN_STILL_PICTURE,
37 VP8Profile0 = VIRTIO_VIDEO_PROFILE_VP8_PROFILE0,
38 VP8Profile1 = VIRTIO_VIDEO_PROFILE_VP8_PROFILE1,
39 VP8Profile2 = VIRTIO_VIDEO_PROFILE_VP8_PROFILE2,
40 VP8Profile3 = VIRTIO_VIDEO_PROFILE_VP8_PROFILE3,
41 VP9Profile0 = VIRTIO_VIDEO_PROFILE_VP9_PROFILE0,
42 VP9Profile1 = VIRTIO_VIDEO_PROFILE_VP9_PROFILE1,
43 VP9Profile2 = VIRTIO_VIDEO_PROFILE_VP9_PROFILE2,
44 VP9Profile3 = VIRTIO_VIDEO_PROFILE_VP9_PROFILE3,
45 }
46 impl_try_from_le32_for_enumn!(Profile, "profile");
47
48 macro_rules! impl_libvda_conversion {
49 ( $( ( $x:ident, $y:ident ) ),* ) => {
50 pub fn from_libvda_profile(p: libvda::Profile) -> Option<Self> {
51 match p {
52 $(libvda::Profile::$x => Some(Self::$y),)*
53 _ => None
54 }
55 }
56
57 // TODO(alexlau): Remove this after encoder CL lands.
58 #[allow(dead_code)]
59 pub fn to_libvda_profile(&self) -> Option<libvda::Profile> {
60 match self {
61 $(Self::$y => Some(libvda::Profile::$x),)*
62 _ => None
63 }
64 }
65 }
66 }
67
68 impl Profile {
to_format(&self) -> Format69 pub fn to_format(&self) -> Format {
70 use Profile::*;
71 match self {
72 H264Baseline
73 | H264Main
74 | H264Extended
75 | H264High
76 | H264High10
77 | H264High422
78 | H264High444PredictiveProfile
79 | H264ScalableBaseline
80 | H264ScalableHigh
81 | H264StereoHigh
82 | H264MultiviewHigh => Format::H264,
83 HevcMain | HevcMain10 | HevcMainStillPicture => Format::HEVC,
84 VP8Profile0 | VP8Profile1 | VP8Profile2 | VP8Profile3 => Format::VP8,
85 VP9Profile0 | VP9Profile1 | VP9Profile2 | VP9Profile3 => Format::VP9,
86 }
87 }
88
89 impl_libvda_conversion!(
90 (H264ProfileBaseline, H264Baseline),
91 (H264ProfileMain, H264Main),
92 (H264ProfileExtended, H264Extended),
93 (H264ProfileHigh, H264High),
94 (H264ProfileHigh10Profile, H264High10),
95 (H264ProfileHigh422Profile, H264High422),
96 (
97 H264ProfileHigh444PredictiveProfile,
98 H264High444PredictiveProfile
99 ),
100 (H264ProfileScalableBaseline, H264ScalableBaseline),
101 (H264ProfileScalableHigh, H264ScalableHigh),
102 (H264ProfileStereoHigh, H264StereoHigh),
103 (H264ProfileMultiviewHigh, H264MultiviewHigh),
104 (HevcProfileMain, HevcMain),
105 (HevcProfileMain10, HevcMain10),
106 (HevcProfileMainStillPicture, HevcMainStillPicture),
107 (VP8, VP8Profile0),
108 (VP9Profile0, VP9Profile0),
109 (VP9Profile1, VP9Profile1),
110 (VP9Profile2, VP9Profile2),
111 (VP9Profile3, VP9Profile3)
112 );
113 }
114
115 #[derive(PartialEq, Eq, PartialOrd, Ord, N, Clone, Copy, Debug)]
116 #[repr(u32)]
117 pub enum Level {
118 H264_1_0 = VIRTIO_VIDEO_LEVEL_H264_1_0,
119 H264_1_1 = VIRTIO_VIDEO_LEVEL_H264_1_1,
120 H264_1_2 = VIRTIO_VIDEO_LEVEL_H264_1_2,
121 H264_1_3 = VIRTIO_VIDEO_LEVEL_H264_1_3,
122 H264_2_0 = VIRTIO_VIDEO_LEVEL_H264_2_0,
123 H264_2_1 = VIRTIO_VIDEO_LEVEL_H264_2_1,
124 H264_2_2 = VIRTIO_VIDEO_LEVEL_H264_2_2,
125 H264_3_0 = VIRTIO_VIDEO_LEVEL_H264_3_0,
126 H264_3_1 = VIRTIO_VIDEO_LEVEL_H264_3_1,
127 H264_3_2 = VIRTIO_VIDEO_LEVEL_H264_3_2,
128 H264_4_0 = VIRTIO_VIDEO_LEVEL_H264_4_0,
129 H264_4_1 = VIRTIO_VIDEO_LEVEL_H264_4_1,
130 H264_4_2 = VIRTIO_VIDEO_LEVEL_H264_4_2,
131 H264_5_0 = VIRTIO_VIDEO_LEVEL_H264_5_0,
132 H264_5_1 = VIRTIO_VIDEO_LEVEL_H264_5_1,
133 }
134 impl_try_from_le32_for_enumn!(Level, "level");
135
136 #[derive(PartialEq, Eq, PartialOrd, Ord, N, Clone, Copy, Debug)]
137 #[repr(u32)]
138 pub enum Format {
139 // Raw formats
140 NV12 = VIRTIO_VIDEO_FORMAT_NV12,
141 YUV420 = VIRTIO_VIDEO_FORMAT_YUV420,
142
143 // Bitstream formats
144 H264 = VIRTIO_VIDEO_FORMAT_H264,
145 HEVC = VIRTIO_VIDEO_FORMAT_HEVC,
146 VP8 = VIRTIO_VIDEO_FORMAT_VP8,
147 VP9 = VIRTIO_VIDEO_FORMAT_VP9,
148 }
149 impl_try_from_le32_for_enumn!(Format, "format");
150
151 impl Display for Format {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result152 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153 use Format::*;
154 match self {
155 NV12 => write!(f, "NV12"),
156 YUV420 => write!(f, "YUV420"),
157 H264 => write!(f, "H264"),
158 HEVC => write!(f, "HEVC"),
159 VP8 => write!(f, "VP8"),
160 VP9 => write!(f, "VP9"),
161 }
162 }
163 }
164
165 #[derive(Debug, Default, Copy, Clone)]
166 pub struct Crop {
167 pub left: u32,
168 pub top: u32,
169 pub width: u32,
170 pub height: u32,
171 }
172 impl_from_for_interconvertible_structs!(virtio_video_crop, Crop, left, top, width, height);
173
174 #[derive(Debug, Default, Clone, Copy)]
175 pub struct PlaneFormat {
176 pub plane_size: u32,
177 pub stride: u32,
178 }
179 impl_from_for_interconvertible_structs!(virtio_video_plane_format, PlaneFormat, plane_size, stride);
180
181 #[derive(Debug, Default, Clone, Copy)]
182 pub struct FormatRange {
183 pub min: u32,
184 pub max: u32,
185 pub step: u32,
186 }
187 impl_from_for_interconvertible_structs!(virtio_video_format_range, FormatRange, min, max, step);
188
189 #[derive(Debug, Default, Clone)]
190 pub struct FrameFormat {
191 pub width: FormatRange,
192 pub height: FormatRange,
193 pub bitrates: Vec<FormatRange>,
194 }
195
196 impl Response for FrameFormat {
write(&self, w: &mut Writer) -> Result<(), io::Error>197 fn write(&self, w: &mut Writer) -> Result<(), io::Error> {
198 w.write_obj(virtio_video_format_frame {
199 width: self.width.into(),
200 height: self.height.into(),
201 num_rates: Le32::from(self.bitrates.len() as u32),
202 ..Default::default()
203 })?;
204 w.write_iter(
205 self.bitrates
206 .iter()
207 .map(|r| Into::<virtio_video_format_range>::into(*r)),
208 )
209 }
210 }
211
212 #[derive(Debug, Clone)]
213 pub struct FormatDesc {
214 pub mask: u64,
215 pub format: Format,
216 pub frame_formats: Vec<FrameFormat>,
217 }
218
219 impl Response for FormatDesc {
write(&self, w: &mut Writer) -> Result<(), io::Error>220 fn write(&self, w: &mut Writer) -> Result<(), io::Error> {
221 w.write_obj(virtio_video_format_desc {
222 mask: self.mask.into(),
223 format: Le32::from(self.format as u32),
224 // ChromeOS only supports single-buffer mode.
225 planes_layout: Le32::from(VIRTIO_VIDEO_PLANES_LAYOUT_SINGLE_BUFFER),
226 // No alignment is required on boards that we currently support.
227 plane_align: Le32::from(0),
228 num_frames: Le32::from(self.frame_formats.len() as u32),
229 })?;
230 self.frame_formats.iter().try_for_each(|ff| ff.write(w))
231 }
232 }
233
clamp_size(size: u32, min: u32, step: u32) -> u32234 fn clamp_size(size: u32, min: u32, step: u32) -> u32 {
235 match step {
236 0 | 1 => size,
237 _ => {
238 let step_mod = (size - min) % step;
239 if step_mod == 0 {
240 size
241 } else {
242 size - step_mod + step
243 }
244 }
245 }
246 }
247
248 /// Parses a slice of valid frame formats and the desired resolution
249 /// and returns the closest available resolution.
find_closest_resolution( frame_formats: &[FrameFormat], desired_width: u32, desired_height: u32, ) -> (u32, u32)250 pub fn find_closest_resolution(
251 frame_formats: &[FrameFormat],
252 desired_width: u32,
253 desired_height: u32,
254 ) -> (u32, u32) {
255 for FrameFormat { width, height, .. } in frame_formats.iter() {
256 if desired_width < width.min || desired_width > width.max {
257 continue;
258 }
259 if desired_height < height.min || desired_height > height.max {
260 continue;
261 }
262 let allowed_width = clamp_size(desired_width, width.min, width.step);
263 let allowed_height = clamp_size(desired_height, height.min, height.step);
264 return (allowed_width, allowed_height);
265 }
266
267 // Return the resolution with maximum surface if nothing better is found.
268 match frame_formats
269 .iter()
270 .max_by_key(|format| format.width.max * format.height.max)
271 {
272 None => (0, 0),
273 Some(format) => (format.width.max, format.height.max),
274 }
275 }
276
277 /// A rectangle used to describe portions of a frame.
278 #[derive(Debug)]
279 pub struct Rect {
280 pub left: i32,
281 pub top: i32,
282 pub right: i32,
283 pub bottom: i32,
284 }
285