1pub mod backend;
9
10use std::collections::BTreeMap;
11use std::collections::BTreeSet;
12
13use backend::*;
14use base::debug;
15use base::error;
16use base::info;
17use base::warn;
18use base::Tube;
19use base::WaitContext;
20use vm_memory::GuestMemory;
21
22use crate::virtio::video::async_cmd_desc_map::AsyncCmdDescMap;
23use crate::virtio::video::command::QueueType;
24use crate::virtio::video::command::VideoCmd;
25use crate::virtio::video::control::*;
26use crate::virtio::video::device::AsyncCmdResponse;
27use crate::virtio::video::device::AsyncCmdTag;
28use crate::virtio::video::device::Device;
29use crate::virtio::video::device::Token;
30use crate::virtio::video::device::VideoCmdResponseType;
31use crate::virtio::video::device::VideoEvtResponseType;
32use crate::virtio::video::error::VideoError;
33use crate::virtio::video::error::VideoResult;
34use crate::virtio::video::event::EvtType;
35use crate::virtio::video::event::VideoEvt;
36use crate::virtio::video::format::find_closest_resolution;
37use crate::virtio::video::format::Bitrate;
38use crate::virtio::video::format::BitrateMode;
39use crate::virtio::video::format::Format;
40use crate::virtio::video::format::FormatDesc;
41use crate::virtio::video::format::Level;
42use crate::virtio::video::format::PlaneFormat;
43use crate::virtio::video::format::Profile;
44use crate::virtio::video::params::Params;
45use crate::virtio::video::protocol;
46use crate::virtio::video::resource::*;
47use crate::virtio::video::response::CmdResponse;
48use crate::virtio::video::EosBufferManager;
49
50pub type InputBufferId = u32;
51pub type OutputBufferId = u32;
52
53#[derive(Debug)]
54struct QueuedInputResourceParams {
55 timestamp: u64,
56 in_queue: bool,
57}
58
59struct InputResource {
60 resource: GuestResource,
61 queue_params: Option<QueuedInputResourceParams>,
62}
63
64#[derive(Debug)]
65struct QueuedOutputResourceParams {
66 in_queue: bool,
67}
68
69struct OutputResource {
70 resource: GuestResource,
71 offset: u32,
72 queue_params: Option<QueuedOutputResourceParams>,
73}
74
75#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
76enum PendingCommand {
77 GetSrcParams { is_ext: bool },
80 GetDstParams { is_ext: bool },
81 Drain,
82 SrcQueueClear,
83 DstQueueClear,
84}
85
86struct Stream<T: EncoderSession> {
87 id: u32,
88 src_params: Params,
89 dst_params: Params,
90 dst_bitrate: Bitrate,
91 dst_profile: Profile,
92 dst_h264_level: Option<Level>,
93 force_keyframe: bool,
94
95 encoder_session: Option<T>,
96 received_input_buffers_event: bool,
97
98 src_resources: BTreeMap<u32, InputResource>,
99 encoder_input_buffer_ids: BTreeMap<InputBufferId, u32>,
100
101 dst_resources: BTreeMap<u32, OutputResource>,
102 encoder_output_buffer_ids: BTreeMap<OutputBufferId, u32>,
103
104 pending_commands: BTreeSet<PendingCommand>,
105 eos_manager: EosBufferManager,
106}
107
108impl<T: EncoderSession> Stream<T> {
109 fn new<E: Encoder<Session = T>>(
110 id: u32,
111 src_resource_type: ResourceType,
112 dst_resource_type: ResourceType,
113 desired_format: Format,
114 encoder: &EncoderDevice<E>,
115 ) -> VideoResult<Self> {
116 const MIN_BUFFERS: u32 = 1;
117 const MAX_BUFFERS: u32 = 342;
118 const DEFAULT_WIDTH: u32 = 640;
119 const DEFAULT_HEIGHT: u32 = 480;
120 const DEFAULT_BITRATE_TARGET: u32 = 6000;
121 const DEFAULT_BITRATE_PEAK: u32 = DEFAULT_BITRATE_TARGET * 2;
122 const DEFAULT_BITRATE: Bitrate = Bitrate::Vbr {
123 target: DEFAULT_BITRATE_TARGET,
124 peak: DEFAULT_BITRATE_PEAK,
125 };
126 const DEFAULT_BUFFER_SIZE: u32 = 2097152; const DEFAULT_FPS: u32 = 30;
128
129 let mut src_params = Params {
130 frame_rate: DEFAULT_FPS,
131 min_buffers: MIN_BUFFERS,
132 max_buffers: MAX_BUFFERS,
133 resource_type: src_resource_type,
134 ..Default::default()
135 };
136
137 let cros_capabilities = &encoder.cros_capabilities;
138
139 cros_capabilities
140 .populate_src_params(
141 &mut src_params,
142 Format::NV12,
143 DEFAULT_WIDTH,
144 DEFAULT_HEIGHT,
145 0,
146 )
147 .map_err(|_| VideoError::InvalidArgument)?;
148
149 let mut dst_params = Params {
150 resource_type: dst_resource_type,
151 frame_rate: DEFAULT_FPS,
152 frame_width: DEFAULT_WIDTH,
153 frame_height: DEFAULT_HEIGHT,
154 ..Default::default()
155 };
156
157 cros_capabilities
161 .populate_dst_params(&mut dst_params, desired_format, DEFAULT_BUFFER_SIZE)
162 .map_err(|_| VideoError::InvalidArgument)?;
163 let dest_format = dst_params.format.ok_or(VideoError::InvalidArgument)?;
166
167 let dst_profile = cros_capabilities
168 .get_default_profile(&dest_format)
169 .ok_or(VideoError::InvalidArgument)?;
170
171 let dst_h264_level = if dest_format == Format::H264 {
172 Some(Level::H264_1_0)
173 } else {
174 None
175 };
176
177 Ok(Self {
178 id,
179 src_params,
180 dst_params,
181 dst_bitrate: DEFAULT_BITRATE,
182 dst_profile,
183 dst_h264_level,
184 force_keyframe: false,
185 encoder_session: None,
186 received_input_buffers_event: false,
187 src_resources: Default::default(),
188 encoder_input_buffer_ids: Default::default(),
189 dst_resources: Default::default(),
190 encoder_output_buffer_ids: Default::default(),
191 pending_commands: Default::default(),
192 eos_manager: EosBufferManager::new(id),
193 })
194 }
195
196 fn has_encode_session(&self) -> bool {
197 self.encoder_session.is_some()
198 }
199
200 fn set_encode_session<U: Encoder<Session = T>>(
201 &mut self,
202 encoder: &mut U,
203 wait_ctx: &WaitContext<Token>,
204 ) -> VideoResult<()> {
205 if self.encoder_session.is_some() {
206 error!(
207 "stream {}: tried to add encode session when one already exists.",
208 self.id
209 );
210 return Err(VideoError::InvalidOperation);
211 }
212
213 let new_session = encoder
214 .start_session(SessionConfig {
215 src_params: self.src_params.clone(),
216 dst_params: self.dst_params.clone(),
217 dst_profile: self.dst_profile,
218 dst_bitrate: self.dst_bitrate,
219 dst_h264_level: self.dst_h264_level,
220 frame_rate: self.dst_params.frame_rate,
221 })
222 .map_err(|_| VideoError::InvalidOperation)?;
223
224 let event_pipe = new_session.event_pipe();
225
226 wait_ctx
227 .add(event_pipe, Token::Event { id: self.id })
228 .map_err(|e| {
229 error!(
230 "stream {}: failed to add FD to poll context: {}",
231 self.id, e
232 );
233 VideoError::InvalidOperation
234 })?;
235 self.encoder_session.replace(new_session);
236 self.received_input_buffers_event = false;
237 Ok(())
238 }
239
240 fn clear_encode_session(&mut self, wait_ctx: &WaitContext<Token>) -> VideoResult<()> {
241 if let Some(session) = self.encoder_session.take() {
242 let event_pipe = session.event_pipe();
243 wait_ctx.delete(event_pipe).map_err(|e| {
244 error!(
245 "stream: {}: failed to remove fd from poll context: {}",
246 self.id, e
247 );
248 VideoError::InvalidOperation
249 })?;
250 }
251 Ok(())
252 }
253
254 fn require_input_buffers(
255 &mut self,
256 input_count: u32,
257 input_frame_width: u32,
258 input_frame_height: u32,
259 output_buffer_size: u32,
260 ) -> Option<Vec<VideoEvtResponseType>> {
261 self.src_params.min_buffers = input_count;
265 self.src_params.max_buffers = 32;
266 self.src_params.frame_width = input_frame_width;
267 self.src_params.frame_height = input_frame_height;
268 self.dst_params.plane_formats[0].plane_size = output_buffer_size;
269 self.received_input_buffers_event = true;
270
271 let mut responses = vec![];
272
273 let pending_get_src_params = if self
275 .pending_commands
276 .remove(&PendingCommand::GetSrcParams { is_ext: false })
277 {
278 Some(false)
279 } else if self
280 .pending_commands
281 .remove(&PendingCommand::GetSrcParams { is_ext: true })
282 {
283 Some(true)
284 } else {
285 None
286 };
287 if let Some(is_ext) = pending_get_src_params {
288 responses.push(VideoEvtResponseType::AsyncCmd(
289 AsyncCmdResponse::from_response(
290 AsyncCmdTag::GetParams {
291 stream_id: self.id,
292 queue_type: QueueType::Input,
293 },
294 CmdResponse::GetParams {
295 queue_type: QueueType::Input,
296 params: self.src_params.clone(),
297 is_ext,
298 },
299 ),
300 ));
301 }
302 let pending_get_dst_params = if self
303 .pending_commands
304 .remove(&PendingCommand::GetDstParams { is_ext: false })
305 {
306 Some(false)
307 } else if self
308 .pending_commands
309 .remove(&PendingCommand::GetDstParams { is_ext: true })
310 {
311 Some(true)
312 } else {
313 None
314 };
315 if let Some(is_ext) = pending_get_dst_params {
316 responses.push(VideoEvtResponseType::AsyncCmd(
317 AsyncCmdResponse::from_response(
318 AsyncCmdTag::GetParams {
319 stream_id: self.id,
320 queue_type: QueueType::Output,
321 },
322 CmdResponse::GetParams {
323 queue_type: QueueType::Output,
324 params: self.dst_params.clone(),
325 is_ext,
326 },
327 ),
328 ));
329 }
330
331 if !responses.is_empty() {
332 Some(responses)
333 } else {
334 None
335 }
336 }
337
338 fn processed_input_buffer(
339 &mut self,
340 input_buffer_id: InputBufferId,
341 ) -> Option<Vec<VideoEvtResponseType>> {
342 let resource_id = *match self.encoder_input_buffer_ids.get(&input_buffer_id) {
343 Some(id) => id,
344 None => {
345 warn!("Received processed input buffer event for input buffer id {}, but missing resource, ResourceDestroyAll?", input_buffer_id);
346 return None;
347 }
348 };
349
350 let resource = match self.src_resources.get_mut(&resource_id) {
351 Some(r) => r,
352 None => {
353 error!(
354 "Received processed input buffer event but missing resource with id {}",
355 resource_id
356 );
357 return None;
358 }
359 };
360
361 let queue_params = match resource.queue_params.take() {
362 Some(p) => p,
363 None => {
364 error!(
365 "Received processed input buffer event but resource with id {} was not queued.",
366 resource_id
367 );
368 return None;
369 }
370 };
371
372 if !queue_params.in_queue {
373 return None;
375 }
376
377 let tag = AsyncCmdTag::Queue {
378 stream_id: self.id,
379 queue_type: QueueType::Input,
380 resource_id,
381 };
382
383 let resp = CmdResponse::ResourceQueue {
384 timestamp: queue_params.timestamp,
385 flags: 0,
386 size: 0,
387 };
388
389 Some(vec![VideoEvtResponseType::AsyncCmd(
390 AsyncCmdResponse::from_response(tag, resp),
391 )])
392 }
393
394 fn processed_output_buffer(
395 &mut self,
396 output_buffer_id: OutputBufferId,
397 bytesused: u32,
398 keyframe: bool,
399 timestamp: u64,
400 ) -> Option<Vec<VideoEvtResponseType>> {
401 let resource_id = *match self.encoder_output_buffer_ids.get(&output_buffer_id) {
402 Some(id) => id,
403 None => {
404 warn!("Received processed output buffer event for output buffer id {}, but missing resource, ResourceDestroyAll?", output_buffer_id);
405 return None;
406 }
407 };
408
409 let resource = match self.dst_resources.get_mut(&resource_id) {
410 Some(r) => r,
411 None => {
412 error!(
413 "Received processed output buffer event but missing resource with id {}",
414 resource_id
415 );
416 return None;
417 }
418 };
419
420 let queue_params = match resource.queue_params.take() {
421 Some(p) => p,
422 None => {
423 error!("Received processed output buffer event but resource with id {} was not queued.", resource_id);
424 return None;
425 }
426 };
427
428 if !queue_params.in_queue {
429 return None;
431 }
432
433 let tag = AsyncCmdTag::Queue {
434 stream_id: self.id,
435 queue_type: QueueType::Output,
436 resource_id,
437 };
438
439 let resp = CmdResponse::ResourceQueue {
440 timestamp,
441 flags: if keyframe {
445 protocol::VIRTIO_VIDEO_BUFFER_FLAG_IFRAME
446 } else {
447 0
448 },
449 size: bytesused,
450 };
451
452 Some(vec![VideoEvtResponseType::AsyncCmd(
453 AsyncCmdResponse::from_response(tag, resp),
454 )])
455 }
456
457 fn flush_response(&mut self, flush_done: bool) -> Option<Vec<VideoEvtResponseType>> {
458 let command_response = if flush_done {
459 CmdResponse::NoData
460 } else {
461 error!("Flush could not be completed for stream {}", self.id);
462 VideoError::InvalidOperation.into()
463 };
464
465 let mut async_responses = vec![];
466
467 if self.pending_commands.remove(&PendingCommand::Drain) {
469 async_responses.push(VideoEvtResponseType::AsyncCmd(
470 AsyncCmdResponse::from_response(
471 AsyncCmdTag::Drain { stream_id: self.id },
472 command_response.clone(),
473 ),
474 ));
475 }
476
477 if self.pending_commands.remove(&PendingCommand::SrcQueueClear) {
478 async_responses.push(VideoEvtResponseType::AsyncCmd(
479 AsyncCmdResponse::from_response(
480 AsyncCmdTag::Clear {
481 stream_id: self.id,
482 queue_type: QueueType::Input,
483 },
484 command_response.clone(),
485 ),
486 ));
487 }
488
489 if self.pending_commands.remove(&PendingCommand::DstQueueClear) {
490 async_responses.push(VideoEvtResponseType::AsyncCmd(
491 AsyncCmdResponse::from_response(
492 AsyncCmdTag::Clear {
493 stream_id: self.id,
494 queue_type: QueueType::Output,
495 },
496 command_response,
497 ),
498 ));
499 }
500
501 self.eos_manager.try_complete_eos(async_responses)
503 }
504
505 #[allow(clippy::unnecessary_wraps)]
506 fn notify_error(&self, error: VideoError) -> Option<Vec<VideoEvtResponseType>> {
507 error!(
508 "Received encoder error event for stream {}: {}",
509 self.id, error
510 );
511 Some(vec![VideoEvtResponseType::Event(VideoEvt {
512 typ: EvtType::Error,
513 stream_id: self.id,
514 })])
515 }
516}
517
518pub struct EncoderDevice<T: Encoder> {
519 cros_capabilities: EncoderCapabilities,
520 encoder: T,
521 streams: BTreeMap<u32, Stream<T::Session>>,
522 resource_bridge: Tube,
523 mem: GuestMemory,
524}
525
526impl<T: Encoder> EncoderDevice<T> {
527 pub fn new(backend: T, resource_bridge: Tube, mem: GuestMemory) -> VideoResult<Self> {
529 Ok(Self {
530 cros_capabilities: backend.query_capabilities()?,
531 encoder: backend,
532 streams: Default::default(),
533 resource_bridge,
534 mem,
535 })
536 }
537
538 #[allow(clippy::unnecessary_wraps)]
539 fn query_capabilities(&self, queue_type: QueueType) -> VideoResult<VideoCmdResponseType> {
540 let descs = match queue_type {
541 QueueType::Input => self.cros_capabilities.input_format_descs.clone(),
542 QueueType::Output => self.cros_capabilities.output_format_descs.clone(),
543 };
544 Ok(VideoCmdResponseType::Sync(CmdResponse::QueryCapability(
545 descs,
546 )))
547 }
548
549 fn stream_create(
550 &mut self,
551 stream_id: u32,
552 desired_format: Format,
553 src_resource_type: ResourceType,
554 dst_resource_type: ResourceType,
555 ) -> VideoResult<VideoCmdResponseType> {
556 if self.streams.contains_key(&stream_id) {
557 return Err(VideoError::InvalidStreamId(stream_id));
558 }
559 let new_stream = Stream::new(
560 stream_id,
561 src_resource_type,
562 dst_resource_type,
563 desired_format,
564 self,
565 )?;
566
567 self.streams.insert(stream_id, new_stream);
568 Ok(VideoCmdResponseType::Sync(CmdResponse::NoData))
569 }
570
571 fn stream_destroy(&mut self, stream_id: u32) -> VideoResult<VideoCmdResponseType> {
572 let mut stream = self
573 .streams
574 .remove(&stream_id)
575 .ok_or(VideoError::InvalidStreamId(stream_id))?;
576 if let Some(session) = stream.encoder_session.take() {
578 if let Err(e) = self.encoder.stop_session(session) {
579 error!("Failed to stop encode session {}: {}", stream_id, e);
580 }
581 }
582 Ok(VideoCmdResponseType::Sync(CmdResponse::NoData))
583 }
584
585 fn stream_drain(&mut self, stream_id: u32) -> VideoResult<VideoCmdResponseType> {
586 let stream = self
587 .streams
588 .get_mut(&stream_id)
589 .ok_or(VideoError::InvalidStreamId(stream_id))?;
590 match stream.encoder_session {
591 Some(ref mut session) => {
592 if stream.pending_commands.contains(&PendingCommand::Drain) {
593 error!("A pending Drain command already exists.");
594 return Err(VideoError::InvalidOperation);
595 }
596 stream.pending_commands.insert(PendingCommand::Drain);
597
598 if !stream
599 .pending_commands
600 .contains(&PendingCommand::SrcQueueClear)
601 && !stream
602 .pending_commands
603 .contains(&PendingCommand::DstQueueClear)
604 {
605 if let Err(e) = session.flush() {
608 error!("Flush failed for stream id {}: {}", stream_id, e);
609 }
610 }
611 Ok(VideoCmdResponseType::Async(AsyncCmdTag::Drain {
612 stream_id,
613 }))
614 }
615 None => {
616 Ok(VideoCmdResponseType::Sync(CmdResponse::NoData))
618 }
619 }
620 }
621
622 fn resource_create(
623 &mut self,
624 wait_ctx: &WaitContext<Token>,
625 stream_id: u32,
626 queue_type: QueueType,
627 resource_id: u32,
628 plane_offsets: Vec<u32>,
629 plane_entries: Vec<Vec<UnresolvedResourceEntry>>,
630 ) -> VideoResult<VideoCmdResponseType> {
631 let stream = self
632 .streams
633 .get_mut(&stream_id)
634 .ok_or(VideoError::InvalidStreamId(stream_id))?;
635
636 if !stream.has_encode_session() {
637 stream.set_encode_session(&mut self.encoder, wait_ctx)?;
640 }
641
642 let num_planes = plane_offsets.len();
643
644 let entries = if plane_entries.len() != 1 {
646 return Err(VideoError::InvalidArgument);
647 } else {
648 plane_entries.first().unwrap()
650 };
651
652 match queue_type {
653 QueueType::Input => {
654 if num_planes < 1 {
658 return Err(VideoError::InvalidParameter);
659 }
660
661 if stream.src_resources.contains_key(&resource_id) {
662 debug!("Replacing source resource with id {}", resource_id);
663 }
664
665 let resource = match stream.src_params.resource_type {
666 ResourceType::VirtioObject => {
667 if entries.len() != 1 {
669 return Err(VideoError::InvalidArgument);
670 }
671 GuestResource::from_virtio_object_entry(
672 entries.first().unwrap().object(),
677 &self.resource_bridge,
678 &stream.src_params,
679 )
680 .map_err(|_| VideoError::InvalidArgument)?
681 }
682 ResourceType::GuestPages => GuestResource::from_virtio_guest_mem_entry(
683 unsafe {
686 std::slice::from_raw_parts(
687 entries.as_ptr() as *const protocol::virtio_video_mem_entry,
688 entries.len(),
689 )
690 },
691 &self.mem,
692 &stream.src_params,
693 )
694 .map_err(|_| VideoError::InvalidArgument)?,
695 };
696
697 stream.src_resources.insert(
698 resource_id,
699 InputResource {
700 resource,
701 queue_params: None,
702 },
703 );
704 }
705 QueueType::Output => {
706 if num_planes != 1 {
708 return Err(VideoError::InvalidParameter);
709 }
710
711 if stream.dst_resources.contains_key(&resource_id) {
712 debug!("Replacing dest resource with id {}", resource_id);
713 }
714
715 let resource = match stream.dst_params.resource_type {
716 ResourceType::VirtioObject => {
717 if entries.len() != 1 {
719 return Err(VideoError::InvalidArgument);
720 }
721 GuestResource::from_virtio_object_entry(
722 entries.first().unwrap().object(),
727 &self.resource_bridge,
728 &stream.dst_params,
729 )
730 .map_err(|_| VideoError::InvalidArgument)?
731 }
732 ResourceType::GuestPages => GuestResource::from_virtio_guest_mem_entry(
733 unsafe {
736 std::slice::from_raw_parts(
737 entries.as_ptr() as *const protocol::virtio_video_mem_entry,
738 entries.len(),
739 )
740 },
741 &self.mem,
742 &stream.dst_params,
743 )
744 .map_err(|_| VideoError::InvalidArgument)?,
745 };
746
747 let offset = plane_offsets[0];
748 stream.dst_resources.insert(
749 resource_id,
750 OutputResource {
751 resource,
752 offset,
753 queue_params: None,
754 },
755 );
756 }
757 }
758
759 Ok(VideoCmdResponseType::Sync(CmdResponse::NoData))
760 }
761
762 fn resource_queue(
763 &mut self,
764 stream_id: u32,
765 queue_type: QueueType,
766 resource_id: u32,
767 timestamp: u64,
768 data_sizes: Vec<u32>,
769 ) -> VideoResult<VideoCmdResponseType> {
770 let stream = self
771 .streams
772 .get_mut(&stream_id)
773 .ok_or(VideoError::InvalidStreamId(stream_id))?;
774
775 let encoder_session = match stream.encoder_session {
776 Some(ref mut e) => e,
777 None => {
778 error!("Encoder session did not exist at resource_queue.");
781 return Err(VideoError::InvalidOperation);
782 }
783 };
784
785 match queue_type {
786 QueueType::Input => {
787 if data_sizes.is_empty() {
791 return Err(VideoError::InvalidParameter);
792 }
793
794 let src_resource = stream.src_resources.get_mut(&resource_id).ok_or(
795 VideoError::InvalidResourceId {
796 stream_id,
797 resource_id,
798 },
799 )?;
800
801 let force_keyframe = std::mem::replace(&mut stream.force_keyframe, false);
802
803 match encoder_session.encode(
804 src_resource
805 .resource
806 .try_clone()
807 .map_err(|_| VideoError::InvalidArgument)?,
808 timestamp,
809 force_keyframe,
810 ) {
811 Ok(input_buffer_id) => {
812 if let Some(last_resource_id) = stream
813 .encoder_input_buffer_ids
814 .insert(input_buffer_id, resource_id)
815 {
816 error!(
817 "encoder input id {} was already mapped to resource id {}",
818 input_buffer_id, last_resource_id
819 );
820 return Err(VideoError::InvalidOperation);
821 }
822 let queue_params = QueuedInputResourceParams {
823 timestamp,
824 in_queue: true,
825 };
826 if let Some(last_queue_params) =
827 src_resource.queue_params.replace(queue_params)
828 {
829 if last_queue_params.in_queue {
830 error!(
831 "resource {} was already queued ({:?})",
832 resource_id, last_queue_params
833 );
834 return Err(VideoError::InvalidOperation);
835 }
836 }
837 }
838 Err(e) => {
839 error!("encode failed: {}", e);
841 return Err(VideoError::InvalidOperation);
842 }
843 }
844 Ok(VideoCmdResponseType::Async(AsyncCmdTag::Queue {
845 stream_id,
846 queue_type: QueueType::Input,
847 resource_id,
848 }))
849 }
850 QueueType::Output => {
851 if data_sizes.len() != 1 {
853 return Err(VideoError::InvalidParameter);
854 }
855
856 let dst_resource = stream.dst_resources.get_mut(&resource_id).ok_or(
857 VideoError::InvalidResourceId {
858 stream_id,
859 resource_id,
860 },
861 )?;
862
863 let buffer_size = dst_resource.resource.planes[0].size as u32;
867
868 if stream.eos_manager.try_reserve_eos_buffer(resource_id) {
873 return Ok(VideoCmdResponseType::Async(AsyncCmdTag::Queue {
874 stream_id,
875 queue_type: QueueType::Output,
876 resource_id,
877 }));
878 }
879
880 match encoder_session.use_output_buffer(
881 dst_resource
882 .resource
883 .handle
884 .try_clone()
885 .map_err(|_| VideoError::InvalidParameter)?,
886 dst_resource.offset,
887 buffer_size,
888 ) {
889 Ok(output_buffer_id) => {
890 if let Some(last_resource_id) = stream
891 .encoder_output_buffer_ids
892 .insert(output_buffer_id, resource_id)
893 {
894 error!(
895 "encoder output id {} was already mapped to resource id {}",
896 output_buffer_id, last_resource_id
897 );
898 }
899 let queue_params = QueuedOutputResourceParams { in_queue: true };
900 if let Some(last_queue_params) =
901 dst_resource.queue_params.replace(queue_params)
902 {
903 if last_queue_params.in_queue {
904 error!(
905 "resource {} was already queued ({:?})",
906 resource_id, last_queue_params
907 );
908 }
909 }
910 }
911 Err(e) => {
912 error!("use_output_buffer failed: {}", e);
913 return Err(VideoError::InvalidOperation);
914 }
915 }
916 Ok(VideoCmdResponseType::Async(AsyncCmdTag::Queue {
917 stream_id,
918 queue_type: QueueType::Output,
919 resource_id,
920 }))
921 }
922 }
923 }
924
925 fn resource_destroy_all(&mut self, stream_id: u32) -> VideoResult<VideoCmdResponseType> {
926 let stream = self
927 .streams
928 .get_mut(&stream_id)
929 .ok_or(VideoError::InvalidStreamId(stream_id))?;
930 stream.src_resources.clear();
931 stream.encoder_input_buffer_ids.clear();
932 stream.dst_resources.clear();
933 stream.encoder_output_buffer_ids.clear();
934 stream.eos_manager.reset();
935 Ok(VideoCmdResponseType::Sync(CmdResponse::NoData))
936 }
937
938 fn queue_clear(
939 &mut self,
940 stream_id: u32,
941 queue_type: QueueType,
942 ) -> VideoResult<VideoCmdResponseType> {
943 let stream = self
955 .streams
956 .get_mut(&stream_id)
957 .ok_or(VideoError::InvalidStreamId(stream_id))?;
958
959 match queue_type {
960 QueueType::Input => {
961 for src_resource in stream.src_resources.values_mut() {
962 if let Some(ref mut queue_params) = src_resource.queue_params {
963 queue_params.in_queue = false;
964 }
965 }
966 }
967 QueueType::Output => {
968 for dst_resource in stream.dst_resources.values_mut() {
969 if let Some(ref mut queue_params) = dst_resource.queue_params {
970 queue_params.in_queue = false;
971 }
972 }
973 stream.eos_manager.reset();
974 }
975 }
976 Ok(VideoCmdResponseType::Sync(CmdResponse::NoData))
977 }
978
979 fn get_params(
980 &mut self,
981 stream_id: u32,
982 queue_type: QueueType,
983 is_ext: bool,
984 ) -> VideoResult<VideoCmdResponseType> {
985 let stream = self
986 .streams
987 .get_mut(&stream_id)
988 .ok_or(VideoError::InvalidStreamId(stream_id))?;
989
990 if stream.encoder_session.is_some() && !stream.received_input_buffers_event {
991 let pending_command = match queue_type {
995 QueueType::Input => PendingCommand::GetSrcParams { is_ext },
996 QueueType::Output => PendingCommand::GetDstParams { is_ext },
997 };
998
999 if !stream.pending_commands.insert(pending_command) {
1000 error!("Pending get params call already exists.");
1002 return Err(VideoError::InvalidOperation);
1003 }
1004
1005 Ok(VideoCmdResponseType::Async(AsyncCmdTag::GetParams {
1006 stream_id,
1007 queue_type,
1008 }))
1009 } else {
1010 let params = match queue_type {
1011 QueueType::Input => stream.src_params.clone(),
1012 QueueType::Output => stream.dst_params.clone(),
1013 };
1014 Ok(VideoCmdResponseType::Sync(CmdResponse::GetParams {
1015 queue_type,
1016 params,
1017 is_ext,
1018 }))
1019 }
1020 }
1021
1022 fn set_params(
1023 &mut self,
1024 wait_ctx: &WaitContext<Token>,
1025 stream_id: u32,
1026 queue_type: QueueType,
1027 format: Option<Format>,
1028 frame_width: u32,
1029 frame_height: u32,
1030 frame_rate: u32,
1031 plane_formats: Vec<PlaneFormat>,
1032 resource_type: Option<ResourceType>,
1033 ) -> VideoResult<VideoCmdResponseType> {
1034 let stream = self
1035 .streams
1036 .get_mut(&stream_id)
1037 .ok_or(VideoError::InvalidStreamId(stream_id))?;
1038
1039 let mut create_session = stream.encoder_session.is_none();
1040 let src_resources_queued = !stream.src_resources.is_empty();
1043 let dst_resources_queued = !stream.dst_resources.is_empty();
1044
1045 if stream.dst_params.frame_rate != frame_rate {
1053 stream.src_params.frame_rate = frame_rate;
1054 stream.dst_params.frame_rate = frame_rate;
1055 if let Some(ref mut encoder_session) = stream.encoder_session {
1056 if !(src_resources_queued || dst_resources_queued) {
1057 create_session = true;
1058 } else if let Err(e) = encoder_session.request_encoding_params_change(
1059 stream.dst_bitrate,
1060 stream.dst_params.frame_rate,
1061 ) {
1062 error!("failed to dynamically request framerate change: {}", e);
1063 return Err(VideoError::InvalidOperation);
1064 }
1065 }
1066 }
1067
1068 match queue_type {
1069 QueueType::Input => {
1070 if stream.src_params.frame_width != frame_width
1071 || stream.src_params.frame_height != frame_height
1072 || stream.src_params.format != format
1073 || stream.src_params.plane_formats != plane_formats
1074 || resource_type
1075 .map(|resource_type| stream.src_params.resource_type != resource_type)
1076 .unwrap_or(false)
1077 {
1078 if src_resources_queued {
1079 return Err(VideoError::InvalidOperation);
1081 }
1082
1083 let desired_format =
1084 format.or(stream.src_params.format).unwrap_or(Format::NV12);
1085 self.cros_capabilities.populate_src_params(
1086 &mut stream.src_params,
1087 desired_format,
1088 frame_width,
1089 frame_height,
1090 plane_formats.first().map(|fmt| fmt.stride).unwrap_or(0),
1091 )?;
1092
1093 stream.dst_params.frame_width = frame_width;
1094 stream.dst_params.frame_height = frame_height;
1095
1096 if let Some(resource_type) = resource_type {
1097 stream.src_params.resource_type = resource_type;
1098 }
1099
1100 create_session = true
1101 }
1102 }
1103 QueueType::Output => {
1104 if stream.dst_params.format != format
1105 || stream.dst_params.plane_formats != plane_formats
1106 || resource_type
1107 .map(|resource_type| stream.dst_params.resource_type != resource_type)
1108 .unwrap_or(false)
1109 {
1110 if dst_resources_queued {
1111 return Err(VideoError::InvalidOperation);
1113 }
1114
1115 let desired_format =
1116 format.or(stream.dst_params.format).unwrap_or(Format::H264);
1117
1118 if plane_formats.len() != 1 {
1120 return Err(VideoError::InvalidArgument);
1121 }
1122
1123 self.cros_capabilities.populate_dst_params(
1124 &mut stream.dst_params,
1125 desired_format,
1126 plane_formats[0].plane_size,
1127 )?;
1128
1129 let new_format = stream
1131 .dst_params
1132 .format
1133 .ok_or(VideoError::InvalidArgument)?;
1134
1135 stream.dst_profile = self
1138 .cros_capabilities
1139 .get_default_profile(&new_format)
1140 .ok_or(VideoError::InvalidArgument)?;
1141
1142 if new_format == Format::H264 {
1143 stream.dst_h264_level = Some(Level::H264_1_0);
1144 } else {
1145 stream.dst_h264_level = None;
1146 }
1147
1148 if let Some(resource_type) = resource_type {
1149 stream.dst_params.resource_type = resource_type;
1150 }
1151
1152 create_session = true;
1153 }
1154 }
1155 }
1156
1157 if create_session {
1158 if stream.encoder_session.is_some() {
1163 stream.clear_encode_session(wait_ctx)?;
1164 if !stream.received_input_buffers_event {
1165 warn!(
1176 "New encoder session being created while waiting for RequireInputBuffers."
1177 )
1178 }
1179 }
1180 stream.set_encode_session(&mut self.encoder, wait_ctx)?;
1181 }
1182 Ok(VideoCmdResponseType::Sync(CmdResponse::NoData))
1183 }
1184
1185 fn query_control(&self, query_ctrl_type: QueryCtrlType) -> VideoResult<VideoCmdResponseType> {
1186 let query_ctrl_response = match query_ctrl_type {
1187 QueryCtrlType::Profile(format) => match self.cros_capabilities.get_profiles(&format) {
1188 Some(profiles) => QueryCtrlResponse::Profile(profiles.clone()),
1189 None => {
1190 return Err(VideoError::UnsupportedControl(CtrlType::Profile));
1191 }
1192 },
1193 QueryCtrlType::Level(format) => {
1194 match format {
1195 Format::H264 => QueryCtrlResponse::Level(vec![
1196 Level::H264_1_0,
1197 Level::H264_1_1,
1198 Level::H264_1_2,
1199 Level::H264_1_3,
1200 Level::H264_2_0,
1201 Level::H264_2_1,
1202 Level::H264_2_2,
1203 Level::H264_3_0,
1204 Level::H264_3_1,
1205 Level::H264_3_2,
1206 Level::H264_4_0,
1207 Level::H264_4_1,
1208 Level::H264_4_2,
1209 Level::H264_5_0,
1210 Level::H264_5_1,
1211 ]),
1212 _ => {
1213 return Err(VideoError::UnsupportedControl(CtrlType::Level));
1215 }
1216 }
1217 }
1218 };
1219
1220 Ok(VideoCmdResponseType::Sync(CmdResponse::QueryControl(
1221 query_ctrl_response,
1222 )))
1223 }
1224
1225 fn get_control(
1226 &self,
1227 stream_id: u32,
1228 ctrl_type: CtrlType,
1229 ) -> VideoResult<VideoCmdResponseType> {
1230 let stream = self
1231 .streams
1232 .get(&stream_id)
1233 .ok_or(VideoError::InvalidStreamId(stream_id))?;
1234 let ctrl_val = match ctrl_type {
1235 CtrlType::BitrateMode => CtrlVal::BitrateMode(stream.dst_bitrate.mode()),
1236 CtrlType::Bitrate => CtrlVal::Bitrate(stream.dst_bitrate.target()),
1237 CtrlType::BitratePeak => CtrlVal::BitratePeak(match stream.dst_bitrate {
1238 Bitrate::Vbr { peak, .. } => peak,
1239 Bitrate::Cbr { target } => target,
1241 }),
1242 CtrlType::Profile => CtrlVal::Profile(stream.dst_profile),
1243 CtrlType::Level => {
1244 let format = stream
1245 .dst_params
1246 .format
1247 .ok_or(VideoError::InvalidArgument)?;
1248 match format {
1249 Format::H264 => CtrlVal::Level(stream.dst_h264_level.ok_or_else(|| {
1250 error!("H264 level not set");
1251 VideoError::InvalidArgument
1252 })?),
1253 _ => {
1254 return Err(VideoError::UnsupportedControl(ctrl_type));
1255 }
1256 }
1257 }
1258 CtrlType::ForceKeyframe => return Err(VideoError::UnsupportedControl(ctrl_type)),
1260 CtrlType::PrependSpsPpsToIdr => CtrlVal::PrependSpsPpsToIdr(true),
1263 };
1264 Ok(VideoCmdResponseType::Sync(CmdResponse::GetControl(
1265 ctrl_val,
1266 )))
1267 }
1268
1269 fn set_control(
1270 &mut self,
1271 wait_ctx: &WaitContext<Token>,
1272 stream_id: u32,
1273 ctrl_val: CtrlVal,
1274 ) -> VideoResult<VideoCmdResponseType> {
1275 let stream = self
1276 .streams
1277 .get_mut(&stream_id)
1278 .ok_or(VideoError::InvalidStreamId(stream_id))?;
1279 let mut recreate_session = false;
1280 let resources_queued = !stream.src_resources.is_empty() || !stream.dst_resources.is_empty();
1281
1282 match ctrl_val {
1283 CtrlVal::BitrateMode(bitrate_mode) => {
1284 if stream.dst_bitrate.mode() != bitrate_mode {
1285 if resources_queued {
1286 error!("set control called for bitrate mode but already encoding.");
1287 return Err(VideoError::InvalidOperation);
1288 }
1289 stream.dst_bitrate = match bitrate_mode {
1290 BitrateMode::Cbr => Bitrate::Cbr {
1291 target: stream.dst_bitrate.target(),
1292 },
1293 BitrateMode::Vbr => Bitrate::Vbr {
1294 target: stream.dst_bitrate.target(),
1295 peak: stream.dst_bitrate.target(),
1296 },
1297 };
1298 recreate_session = true;
1299 }
1300 }
1301 CtrlVal::Bitrate(bitrate) => {
1302 if stream.dst_bitrate.target() != bitrate {
1303 let mut new_bitrate = stream.dst_bitrate;
1304 match &mut new_bitrate {
1305 Bitrate::Cbr { target } | Bitrate::Vbr { target, .. } => *target = bitrate,
1306 }
1307 if let Some(ref mut encoder_session) = stream.encoder_session {
1308 if let Err(e) = encoder_session.request_encoding_params_change(
1309 new_bitrate,
1310 stream.dst_params.frame_rate,
1311 ) {
1312 error!("failed to dynamically request target bitrate change: {}", e);
1313 return Err(VideoError::InvalidOperation);
1314 }
1315 }
1316 stream.dst_bitrate = new_bitrate;
1317 }
1318 }
1319 CtrlVal::BitratePeak(bitrate) => {
1320 match stream.dst_bitrate {
1321 Bitrate::Vbr { peak, .. } => {
1322 if peak != bitrate {
1323 let new_bitrate = Bitrate::Vbr {
1324 target: stream.dst_bitrate.target(),
1325 peak: bitrate,
1326 };
1327 if let Some(ref mut encoder_session) = stream.encoder_session {
1328 if let Err(e) = encoder_session.request_encoding_params_change(
1329 new_bitrate,
1330 stream.dst_params.frame_rate,
1331 ) {
1332 error!(
1333 "failed to dynamically request peak bitrate change: {}",
1334 e
1335 );
1336 return Err(VideoError::InvalidOperation);
1337 }
1338 }
1339 stream.dst_bitrate = new_bitrate;
1340 }
1341 }
1342 Bitrate::Cbr { .. } => {}
1345 }
1346 }
1347 CtrlVal::Profile(profile) => {
1348 if stream.dst_profile != profile {
1349 if resources_queued {
1350 error!("set control called for profile but already encoding.");
1351 return Err(VideoError::InvalidOperation);
1352 }
1353 let format = stream
1354 .dst_params
1355 .format
1356 .ok_or(VideoError::InvalidArgument)?;
1357 if format != profile.to_format() {
1358 error!(
1359 "specified profile does not correspond to the selected format ({})",
1360 format
1361 );
1362 return Err(VideoError::InvalidOperation);
1363 }
1364 stream.dst_profile = profile;
1365 recreate_session = true;
1366 }
1367 }
1368 CtrlVal::Level(level) => {
1369 if stream.dst_h264_level != Some(level) {
1370 if resources_queued {
1371 error!("set control called for level but already encoding.");
1372 return Err(VideoError::InvalidOperation);
1373 }
1374 let format = stream
1375 .dst_params
1376 .format
1377 .ok_or(VideoError::InvalidArgument)?;
1378 if format != Format::H264 {
1379 error!(
1380 "set control called for level but format is not H264 ({})",
1381 format
1382 );
1383 return Err(VideoError::InvalidOperation);
1384 }
1385 stream.dst_h264_level = Some(level);
1386 recreate_session = true;
1387 }
1388 }
1389 CtrlVal::ForceKeyframe => {
1390 stream.force_keyframe = true;
1391 }
1392 CtrlVal::PrependSpsPpsToIdr(prepend_sps_pps_to_idr) => {
1393 if !prepend_sps_pps_to_idr {
1397 return Err(VideoError::InvalidOperation);
1398 }
1399 }
1400 }
1401
1402 if recreate_session && stream.encoder_session.is_some() {
1404 stream.clear_encode_session(wait_ctx)?;
1405 stream.set_encode_session(&mut self.encoder, wait_ctx)?;
1406 }
1407
1408 Ok(VideoCmdResponseType::Sync(CmdResponse::SetControl))
1409 }
1410}
1411
1412impl<T: Encoder> Device for EncoderDevice<T> {
1413 fn process_cmd(
1414 &mut self,
1415 req: VideoCmd,
1416 wait_ctx: &WaitContext<Token>,
1417 ) -> (
1418 VideoCmdResponseType,
1419 Option<(u32, Vec<VideoEvtResponseType>)>,
1420 ) {
1421 let mut event_ret = None;
1422 let cmd_response = match req {
1423 VideoCmd::QueryCapability { queue_type } => self.query_capabilities(queue_type),
1424 VideoCmd::StreamCreate {
1425 stream_id,
1426 coded_format: desired_format,
1427 input_resource_type,
1428 output_resource_type,
1429 } => self.stream_create(
1430 stream_id,
1431 desired_format,
1432 input_resource_type,
1433 output_resource_type,
1434 ),
1435 VideoCmd::StreamDestroy { stream_id } => self.stream_destroy(stream_id),
1436 VideoCmd::StreamDrain { stream_id } => self.stream_drain(stream_id),
1437 VideoCmd::ResourceCreate {
1438 stream_id,
1439 queue_type,
1440 resource_id,
1441 plane_offsets,
1442 plane_entries,
1443 } => self.resource_create(
1444 wait_ctx,
1445 stream_id,
1446 queue_type,
1447 resource_id,
1448 plane_offsets,
1449 plane_entries,
1450 ),
1451 VideoCmd::ResourceQueue {
1452 stream_id,
1453 queue_type,
1454 resource_id,
1455 timestamp,
1456 data_sizes,
1457 } => {
1458 let resp =
1459 self.resource_queue(stream_id, queue_type, resource_id, timestamp, data_sizes);
1460
1461 if resp.is_ok() && queue_type == QueueType::Output {
1462 if let Some(stream) = self.streams.get_mut(&stream_id) {
1463 if stream.eos_manager.client_awaits_eos {
1466 info!(
1467 "stream {}: using queued buffer as EOS for pending flush",
1468 stream_id
1469 );
1470 event_ret = match stream.eos_manager.try_complete_eos(vec![]) {
1471 Some(eos_resps) => Some((stream_id, eos_resps)),
1472 None => {
1473 error!("stream {}: try_get_eos_buffer() should have returned a valid response. This is a bug.", stream_id);
1474 Some((
1475 stream_id,
1476 vec![VideoEvtResponseType::Event(VideoEvt {
1477 typ: EvtType::Error,
1478 stream_id,
1479 })],
1480 ))
1481 }
1482 };
1483 }
1484 } else {
1485 error!(
1486 "stream {}: the stream ID should be valid here. This is a bug.",
1487 stream_id
1488 );
1489 event_ret = Some((
1490 stream_id,
1491 vec![VideoEvtResponseType::Event(VideoEvt {
1492 typ: EvtType::Error,
1493 stream_id,
1494 })],
1495 ));
1496 }
1497 }
1498
1499 resp
1500 }
1501 VideoCmd::ResourceDestroyAll { stream_id, .. } => self.resource_destroy_all(stream_id),
1502 VideoCmd::QueueClear {
1503 stream_id,
1504 queue_type,
1505 } => self.queue_clear(stream_id, queue_type),
1506 VideoCmd::GetParams {
1507 stream_id,
1508 queue_type,
1509 is_ext,
1510 } => self.get_params(stream_id, queue_type, is_ext),
1511 VideoCmd::SetParams {
1512 stream_id,
1513 queue_type,
1514 params:
1515 Params {
1516 format,
1517 frame_width,
1518 frame_height,
1519 frame_rate,
1520 plane_formats,
1521 resource_type,
1522 ..
1523 },
1524 is_ext,
1525 } => self.set_params(
1526 wait_ctx,
1527 stream_id,
1528 queue_type,
1529 format,
1530 frame_width,
1531 frame_height,
1532 frame_rate,
1533 plane_formats,
1534 if is_ext { Some(resource_type) } else { None },
1535 ),
1536 VideoCmd::QueryControl { query_ctrl_type } => self.query_control(query_ctrl_type),
1537 VideoCmd::GetControl {
1538 stream_id,
1539 ctrl_type,
1540 } => self.get_control(stream_id, ctrl_type),
1541 VideoCmd::SetControl {
1542 stream_id,
1543 ctrl_val,
1544 } => self.set_control(wait_ctx, stream_id, ctrl_val),
1545 };
1546 let cmd_ret = match cmd_response {
1547 Ok(r) => r,
1548 Err(e) => {
1549 error!("returning error response: {}", &e);
1550 VideoCmdResponseType::Sync(e.into())
1551 }
1552 };
1553 (cmd_ret, event_ret)
1554 }
1555
1556 fn process_event(
1557 &mut self,
1558 _desc_map: &mut AsyncCmdDescMap,
1559 stream_id: u32,
1560 _wait_ctx: &WaitContext<Token>,
1561 ) -> Option<Vec<VideoEvtResponseType>> {
1562 let stream = match self.streams.get_mut(&stream_id) {
1563 Some(s) => s,
1564 None => {
1565 error!("Received event for missing stream id {}", stream_id);
1567 return None;
1568 }
1569 };
1570
1571 let encoder_session = match stream.encoder_session {
1572 Some(ref mut s) => s,
1573 None => {
1574 error!(
1575 "Received event for missing encoder session of stream id {}",
1576 stream_id
1577 );
1578 return None;
1579 }
1580 };
1581
1582 let event = match encoder_session.read_event() {
1583 Ok(e) => e,
1584 Err(e) => {
1585 error!("Failed to read event for stream id {}: {}", stream_id, e);
1586 return None;
1587 }
1588 };
1589
1590 match event {
1591 EncoderEvent::RequireInputBuffers {
1592 input_count,
1593 input_frame_width,
1594 input_frame_height,
1595 output_buffer_size,
1596 } => stream.require_input_buffers(
1597 input_count,
1598 input_frame_width,
1599 input_frame_height,
1600 output_buffer_size,
1601 ),
1602 EncoderEvent::ProcessedInputBuffer {
1603 id: input_buffer_id,
1604 } => stream.processed_input_buffer(input_buffer_id),
1605 EncoderEvent::ProcessedOutputBuffer {
1606 id: output_buffer_id,
1607 bytesused,
1608 keyframe,
1609 timestamp,
1610 } => stream.processed_output_buffer(output_buffer_id, bytesused, keyframe, timestamp),
1611 EncoderEvent::FlushResponse { flush_done } => stream.flush_response(flush_done),
1612 EncoderEvent::NotifyError { error } => stream.notify_error(error),
1613 }
1614 }
1615}
1616
1617#[derive(Debug)]
1618pub enum EncoderEvent {
1619 RequireInputBuffers {
1620 input_count: u32,
1621 input_frame_width: u32,
1622 input_frame_height: u32,
1623 output_buffer_size: u32,
1624 },
1625 ProcessedInputBuffer {
1626 id: InputBufferId,
1627 },
1628 ProcessedOutputBuffer {
1629 id: OutputBufferId,
1630 bytesused: u32,
1631 keyframe: bool,
1632 timestamp: u64,
1633 },
1634 FlushResponse {
1635 flush_done: bool,
1636 },
1637 #[allow(dead_code)]
1638 NotifyError {
1639 error: VideoError,
1640 },
1641}
1642
1643#[derive(Debug)]
1644pub struct SessionConfig {
1645 pub src_params: Params,
1646 pub dst_params: Params,
1647 pub dst_profile: Profile,
1648 pub dst_bitrate: Bitrate,
1649 pub dst_h264_level: Option<Level>,
1650 pub frame_rate: u32,
1651}
1652
1653#[derive(Clone)]
1654pub struct EncoderCapabilities {
1655 pub input_format_descs: Vec<FormatDesc>,
1656 pub output_format_descs: Vec<FormatDesc>,
1657 pub coded_format_profiles: BTreeMap<Format, Vec<Profile>>,
1658}
1659
1660impl EncoderCapabilities {
1661 pub fn populate_src_params(
1662 &self,
1663 src_params: &mut Params,
1664 desired_format: Format,
1665 desired_width: u32,
1666 desired_height: u32,
1667 mut stride: u32,
1668 ) -> VideoResult<()> {
1669 let format_desc = self
1670 .input_format_descs
1671 .iter()
1672 .find(|&format_desc| format_desc.format == desired_format)
1673 .unwrap_or(
1674 self.input_format_descs
1675 .first()
1676 .ok_or(VideoError::InvalidFormat)?,
1677 );
1678
1679 let (allowed_width, allowed_height) =
1680 find_closest_resolution(&format_desc.frame_formats, desired_width, desired_height);
1681
1682 if stride == 0 {
1683 stride = allowed_width;
1684 }
1685
1686 let plane_formats =
1687 PlaneFormat::get_plane_layout(format_desc.format, stride, allowed_height)
1688 .ok_or(VideoError::InvalidFormat)?;
1689
1690 src_params.frame_width = allowed_width;
1691 src_params.frame_height = allowed_height;
1692 src_params.format = Some(format_desc.format);
1693 src_params.plane_formats = plane_formats;
1694 Ok(())
1695 }
1696
1697 pub fn populate_dst_params(
1698 &self,
1699 dst_params: &mut Params,
1700 desired_format: Format,
1701 buffer_size: u32,
1702 ) -> VideoResult<()> {
1703 let format_desc = self
1705 .output_format_descs
1706 .iter()
1707 .find(move |&format_desc| format_desc.format == desired_format)
1708 .unwrap_or(
1709 self.output_format_descs
1710 .first()
1711 .ok_or(VideoError::InvalidFormat)?,
1712 );
1713 dst_params.format = Some(format_desc.format);
1714
1715 dst_params.plane_formats = vec![PlaneFormat {
1718 plane_size: buffer_size,
1719 stride: 0,
1720 }];
1721 Ok(())
1722 }
1723
1724 pub fn get_profiles(&self, coded_format: &Format) -> Option<&Vec<Profile>> {
1725 self.coded_format_profiles.get(coded_format)
1726 }
1727
1728 pub fn get_default_profile(&self, coded_format: &Format) -> Option<Profile> {
1729 let profiles = self.get_profiles(coded_format)?;
1730 match profiles.first() {
1731 None => {
1732 error!("Format {} exists but no available profiles.", coded_format);
1733 None
1734 }
1735 Some(profile) => Some(*profile),
1736 }
1737 }
1738}