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