1use std::convert::TryFrom;
8use std::convert::TryInto;
9use std::io;
10
11use base::error;
12use data_model::Le32;
13use enumn::N;
14use remain::sorted;
15use thiserror::Error as ThisError;
16
17use crate::virtio::video::control::*;
18use crate::virtio::video::format::*;
19use crate::virtio::video::params::Params;
20use crate::virtio::video::protocol::*;
21use crate::virtio::video::resource::ResourceType;
22use crate::virtio::video::resource::UnresolvedResourceEntry;
23use crate::virtio::Reader;
24
25#[sorted]
27#[derive(Debug, ThisError)]
28pub enum ReadCmdError {
29 #[error("invalid argument passed to command")]
31 InvalidArgument,
32 #[error("invalid command type: {0}")]
34 InvalidCmdType(u32),
35 #[error("failed to read object: {0}")]
37 IoError(#[from] io::Error),
38 #[error("unsupported control type: {0}")]
40 UnsupportedCtrlType(u32),
41}
42
43#[derive(PartialEq, Eq, PartialOrd, Ord, N, Clone, Copy, Debug)]
44#[repr(u32)]
45pub enum QueueType {
46 Input = VIRTIO_VIDEO_QUEUE_TYPE_INPUT,
47 Output = VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT,
48}
49impl_try_from_le32_for_enumn!(QueueType, "queue_type");
50
51#[derive(Debug)]
52pub enum VideoCmd {
53 QueryCapability {
54 queue_type: QueueType,
55 },
56 StreamCreate {
57 stream_id: u32,
58 coded_format: Format,
59 input_resource_type: ResourceType,
60 output_resource_type: ResourceType,
61 },
62 StreamDestroy {
63 stream_id: u32,
64 },
65 StreamDrain {
66 stream_id: u32,
67 },
68 ResourceCreate {
69 stream_id: u32,
70 queue_type: QueueType,
71 resource_id: u32,
72 plane_offsets: Vec<u32>,
73 plane_entries: Vec<Vec<UnresolvedResourceEntry>>,
77 },
78 ResourceQueue {
79 stream_id: u32,
80 queue_type: QueueType,
81 resource_id: u32,
82 timestamp: u64,
83 data_sizes: Vec<u32>,
84 },
85 ResourceDestroyAll {
86 stream_id: u32,
87 queue_type: QueueType,
88 },
89 QueueClear {
90 stream_id: u32,
91 queue_type: QueueType,
92 },
93 GetParams {
94 stream_id: u32,
95 queue_type: QueueType,
96 is_ext: bool,
98 },
99 SetParams {
100 stream_id: u32,
101 queue_type: QueueType,
102 params: Params,
103 is_ext: bool,
105 },
106 QueryControl {
107 query_ctrl_type: QueryCtrlType,
108 },
109 GetControl {
110 stream_id: u32,
111 ctrl_type: CtrlType,
112 },
113 SetControl {
114 stream_id: u32,
115 ctrl_val: CtrlVal,
116 },
117}
118
119impl VideoCmd {
120 pub fn from_reader(r: &mut Reader) -> Result<Self, ReadCmdError> {
122 use self::ReadCmdError::*;
123 use self::VideoCmd::*;
124
125 let hdr = r.read_obj::<virtio_video_cmd_hdr>()?;
129
130 Ok(match hdr.type_.into() {
131 VIRTIO_VIDEO_CMD_QUERY_CAPABILITY => {
132 let virtio_video_query_capability { queue_type, .. } = r.read_obj()?;
133 QueryCapability {
134 queue_type: queue_type.try_into()?,
135 }
136 }
137 VIRTIO_VIDEO_CMD_STREAM_CREATE => {
138 let virtio_video_stream_create {
139 in_mem_type,
140 out_mem_type,
141 coded_format,
142 ..
143 } = r.read_obj()?;
144
145 let input_resource_type = match in_mem_type.into() {
146 VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT => ResourceType::VirtioObject,
147 VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES => ResourceType::GuestPages,
148 m => {
149 error!("Unsupported input resource memory type 0x{:x}!", m);
150 return Err(InvalidArgument);
151 }
152 };
153
154 let output_resource_type = match out_mem_type.into() {
155 VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT => ResourceType::VirtioObject,
156 VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES => ResourceType::GuestPages,
157 m => {
158 error!("Unsupported output resource memory type 0x{:x}!", m);
159 return Err(InvalidArgument);
160 }
161 };
162
163 StreamCreate {
164 stream_id: hdr.stream_id.into(),
165 coded_format: coded_format.try_into()?,
166 input_resource_type,
167 output_resource_type,
168 }
169 }
170 VIRTIO_VIDEO_CMD_STREAM_DESTROY => {
171 let virtio_video_stream_destroy { .. } = r.read_obj()?;
172 StreamDestroy {
173 stream_id: hdr.stream_id.into(),
174 }
175 }
176 VIRTIO_VIDEO_CMD_STREAM_DRAIN => {
177 let virtio_video_stream_drain { .. } = r.read_obj()?;
178 StreamDrain {
179 stream_id: hdr.stream_id.into(),
180 }
181 }
182 VIRTIO_VIDEO_CMD_RESOURCE_CREATE => {
183 let virtio_video_resource_create {
184 queue_type,
185 resource_id,
186 planes_layout,
187 num_planes,
188 plane_offsets,
189 num_entries,
190 } = r.read_obj()?;
191
192 let planes_layout = Into::<u32>::into(planes_layout);
194 if planes_layout != VIRTIO_VIDEO_PLANES_LAYOUT_SINGLE_BUFFER {
195 error!("Only single-planar formats are supported for now");
196 return Err(InvalidArgument);
197 }
198
199 let num_planes = Into::<u32>::into(num_planes) as usize;
200 if num_planes > plane_offsets.len() {
201 error!(
202 "num_planes is {} but shall not exceed {}",
203 num_planes,
204 plane_offsets.len(),
205 );
206 return Err(InvalidArgument);
207 }
208 if planes_layout == VIRTIO_VIDEO_PLANES_LAYOUT_SINGLE_BUFFER && num_planes != 1 {
209 error!(
210 "Single-planar format specified but num_planes is {}",
211 num_planes
212 );
213 return Err(InvalidArgument);
214 }
215
216 let plane_offsets = plane_offsets[0..num_planes]
217 .iter()
218 .map(|x| Into::<u32>::into(*x))
219 .collect::<Vec<u32>>();
220
221 let plane_entries = (0..num_planes)
223 .map(|i| {
224 let num_entries: u32 = num_entries[i].into();
225 (0..num_entries)
226 .map(|_| r.read_obj::<UnresolvedResourceEntry>())
227 .collect::<Result<Vec<_>, _>>()
228 })
229 .collect::<Result<Vec<_>, _>>()?;
230
231 ResourceCreate {
232 stream_id: hdr.stream_id.into(),
233 queue_type: queue_type.try_into()?,
234 resource_id: resource_id.into(),
235 plane_offsets,
236 plane_entries,
237 }
238 }
239 VIRTIO_VIDEO_CMD_RESOURCE_QUEUE => {
240 let virtio_video_resource_queue {
241 queue_type,
242 resource_id,
243 timestamp,
244 num_data_sizes,
245 data_sizes,
246 ..
247 } = r.read_obj()?;
248
249 let num_data_sizes: u32 = num_data_sizes.into();
250 if num_data_sizes as usize > data_sizes.len() {
251 return Err(InvalidArgument);
252 }
253 let data_sizes = data_sizes[0..num_data_sizes as usize]
254 .iter()
255 .map(|x| Into::<u32>::into(*x))
256 .collect::<Vec<u32>>();
257 ResourceQueue {
258 stream_id: hdr.stream_id.into(),
259 queue_type: queue_type.try_into()?,
260 resource_id: resource_id.into(),
261 timestamp: timestamp.into(),
262 data_sizes,
263 }
264 }
265 VIRTIO_VIDEO_CMD_RESOURCE_DESTROY_ALL => {
266 let virtio_video_resource_destroy_all { queue_type, .. } = r.read_obj()?;
267 ResourceDestroyAll {
268 stream_id: hdr.stream_id.into(),
269 queue_type: queue_type.try_into()?,
270 }
271 }
272 VIRTIO_VIDEO_CMD_QUEUE_CLEAR => {
273 let virtio_video_queue_clear { queue_type, .. } = r.read_obj()?;
274 QueueClear {
275 stream_id: hdr.stream_id.into(),
276 queue_type: queue_type.try_into()?,
277 }
278 }
279 VIRTIO_VIDEO_CMD_GET_PARAMS => {
280 let virtio_video_get_params { queue_type, .. } = r.read_obj()?;
281 GetParams {
282 stream_id: hdr.stream_id.into(),
283 queue_type: queue_type.try_into()?,
284 is_ext: false,
285 }
286 }
287 VIRTIO_VIDEO_CMD_SET_PARAMS => {
288 let virtio_video_set_params { params } = r.read_obj()?;
289 SetParams {
290 stream_id: hdr.stream_id.into(),
291 queue_type: params.queue_type.try_into()?,
292 params: params.try_into()?,
293 is_ext: false,
294 }
295 }
296 VIRTIO_VIDEO_CMD_QUERY_CONTROL => {
297 let body = r.read_obj::<virtio_video_query_control>()?;
298 let query_ctrl_type = match body.control.into() {
299 VIRTIO_VIDEO_CONTROL_PROFILE => QueryCtrlType::Profile(
300 r.read_obj::<virtio_video_query_control_profile>()?
301 .format
302 .try_into()?,
303 ),
304 VIRTIO_VIDEO_CONTROL_LEVEL => QueryCtrlType::Level(
305 r.read_obj::<virtio_video_query_control_level>()?
306 .format
307 .try_into()?,
308 ),
309 t => {
310 return Err(ReadCmdError::UnsupportedCtrlType(t));
311 }
312 };
313 QueryControl { query_ctrl_type }
314 }
315 VIRTIO_VIDEO_CMD_GET_CONTROL => {
316 let virtio_video_get_control { control, .. } = r.read_obj()?;
317 let ctrl_type = match control.into() {
318 VIRTIO_VIDEO_CONTROL_BITRATE => CtrlType::Bitrate,
319 VIRTIO_VIDEO_CONTROL_BITRATE_PEAK => CtrlType::BitratePeak,
320 VIRTIO_VIDEO_CONTROL_BITRATE_MODE => CtrlType::BitrateMode,
321 VIRTIO_VIDEO_CONTROL_PROFILE => CtrlType::Profile,
322 VIRTIO_VIDEO_CONTROL_LEVEL => CtrlType::Level,
323 VIRTIO_VIDEO_CONTROL_FORCE_KEYFRAME => CtrlType::ForceKeyframe,
324 VIRTIO_VIDEO_CONTROL_PREPEND_SPSPPS_TO_IDR => CtrlType::PrependSpsPpsToIdr,
325 t => {
326 return Err(ReadCmdError::UnsupportedCtrlType(t));
327 }
328 };
329 GetControl {
330 stream_id: hdr.stream_id.into(),
331 ctrl_type,
332 }
333 }
334 VIRTIO_VIDEO_CMD_SET_CONTROL => {
335 let virtio_video_set_control { control, .. } = r.read_obj()?;
336 let ctrl_val = match control.into() {
337 VIRTIO_VIDEO_CONTROL_BITRATE => CtrlVal::Bitrate(
338 r.read_obj::<virtio_video_control_val_bitrate>()?
339 .bitrate
340 .into(),
341 ),
342 VIRTIO_VIDEO_CONTROL_BITRATE_PEAK => CtrlVal::BitratePeak(
343 r.read_obj::<virtio_video_control_val_bitrate_peak>()?
344 .bitrate_peak
345 .into(),
346 ),
347 VIRTIO_VIDEO_CONTROL_BITRATE_MODE => CtrlVal::BitrateMode(
348 r.read_obj::<virtio_video_control_val_bitrate_mode>()?
349 .bitrate_mode
350 .try_into()?,
351 ),
352 VIRTIO_VIDEO_CONTROL_PROFILE => CtrlVal::Profile(
353 r.read_obj::<virtio_video_control_val_profile>()?
354 .profile
355 .try_into()?,
356 ),
357 VIRTIO_VIDEO_CONTROL_LEVEL => CtrlVal::Level(
358 r.read_obj::<virtio_video_control_val_level>()?
359 .level
360 .try_into()?,
361 ),
362 VIRTIO_VIDEO_CONTROL_FORCE_KEYFRAME => CtrlVal::ForceKeyframe,
363 VIRTIO_VIDEO_CONTROL_PREPEND_SPSPPS_TO_IDR => CtrlVal::PrependSpsPpsToIdr(
364 r.read_obj::<virtio_video_control_val_prepend_spspps_to_idr>()?
365 .prepend_spspps_to_idr
366 != 0,
367 ),
368 t => {
369 return Err(ReadCmdError::UnsupportedCtrlType(t));
370 }
371 };
372 SetControl {
373 stream_id: hdr.stream_id.into(),
374 ctrl_val,
375 }
376 }
377 VIRTIO_VIDEO_CMD_GET_PARAMS_EXT => {
378 let virtio_video_get_params_ext { queue_type, .. } = r.read_obj()?;
379 GetParams {
380 stream_id: hdr.stream_id.into(),
381 queue_type: queue_type.try_into()?,
382 is_ext: true,
383 }
384 }
385 VIRTIO_VIDEO_CMD_SET_PARAMS_EXT => {
386 let virtio_video_set_params_ext { params } = r.read_obj()?;
387 SetParams {
388 stream_id: hdr.stream_id.into(),
389 queue_type: params.base.queue_type.try_into()?,
390 params: params.try_into()?,
391 is_ext: true,
392 }
393 }
394 _ => return Err(ReadCmdError::InvalidCmdType(hdr.type_.into())),
395 })
396 }
397}