1#[cfg(feature = "video-decoder")]
11pub mod decoder_adapter;
12
13use std::collections::BTreeMap;
14use std::os::fd::AsRawFd;
15use std::os::fd::BorrowedFd;
16use std::path::Path;
17use std::path::PathBuf;
18use std::rc::Rc;
19use std::sync::Arc;
20
21use anyhow::Context;
22use base::error;
23use base::Descriptor;
24use base::Event;
25use base::EventToken;
26use base::EventType;
27use base::MappedRegion;
28use base::MemoryMappingArena;
29use base::Protection;
30use base::WaitContext;
31use base::WorkerThread;
32use resources::address_allocator::AddressAllocator;
33use resources::AddressRange;
34use resources::Alloc;
35use sync::Mutex;
36use virtio_media::io::WriteToDescriptorChain;
37use virtio_media::poll::SessionPoller;
38use virtio_media::protocol::SgEntry;
39use virtio_media::protocol::V4l2Event;
40use virtio_media::protocol::VirtioMediaDeviceConfig;
41use virtio_media::GuestMemoryRange;
42use virtio_media::VirtioMediaDevice;
43use virtio_media::VirtioMediaDeviceRunner;
44use virtio_media::VirtioMediaEventQueue;
45use virtio_media::VirtioMediaGuestMemoryMapper;
46use virtio_media::VirtioMediaHostMemoryMapper;
47use vm_control::VmMemorySource;
48use vm_memory::GuestAddress;
49use vm_memory::GuestMemory;
50
51use crate::virtio::copy_config;
52use crate::virtio::device_constants::media::QUEUE_SIZES;
53#[cfg(feature = "video-decoder")]
54use crate::virtio::device_constants::video::VideoBackendType;
55use crate::virtio::DeviceType;
56use crate::virtio::Interrupt;
57use crate::virtio::Queue;
58use crate::virtio::Reader;
59use crate::virtio::SharedMemoryMapper;
60use crate::virtio::SharedMemoryRegion;
61use crate::virtio::VirtioDevice;
62use crate::virtio::Writer;
63
64struct EventQueue(Queue);
67
68impl VirtioMediaEventQueue for EventQueue {
69 fn send_event(&mut self, event: V4l2Event) {
71 let mut desc;
72
73 loop {
74 match self.0.pop() {
75 Some(d) => {
76 desc = d;
77 break;
78 }
79 None => {
80 if let Err(e) = self.0.event().wait() {
81 error!("could not obtain a descriptor to send event to: {:#}", e);
82 return;
83 }
84 }
85 }
86 }
87
88 if let Err(e) = match event {
89 V4l2Event::Error(event) => WriteToDescriptorChain::write_obj(&mut desc.writer, event),
90 V4l2Event::DequeueBuffer(event) => {
91 WriteToDescriptorChain::write_obj(&mut desc.writer, event)
92 }
93 V4l2Event::Event(event) => WriteToDescriptorChain::write_obj(&mut desc.writer, event),
94 } {
95 error!("failed to write event: {}", e);
96 }
97
98 self.0.add_used(desc);
99 self.0.trigger_interrupt();
100 }
101}
102
103#[derive(Clone)]
108struct ArcedMemoryMapper(Arc<Mutex<Box<dyn SharedMemoryMapper>>>);
109
110impl From<Box<dyn SharedMemoryMapper>> for ArcedMemoryMapper {
111 fn from(mapper: Box<dyn SharedMemoryMapper>) -> Self {
112 Self(Arc::new(Mutex::new(mapper)))
113 }
114}
115
116impl SharedMemoryMapper for ArcedMemoryMapper {
117 fn add_mapping(
118 &mut self,
119 source: VmMemorySource,
120 offset: u64,
121 prot: Protection,
122 cache: hypervisor::MemCacheType,
123 ) -> anyhow::Result<()> {
124 self.0.lock().add_mapping(source, offset, prot, cache)
125 }
126
127 fn remove_mapping(&mut self, offset: u64) -> anyhow::Result<()> {
128 self.0.lock().remove_mapping(offset)
129 }
130
131 fn as_raw_descriptor(&self) -> Option<base::RawDescriptor> {
132 self.0.lock().as_raw_descriptor()
133 }
134}
135
136struct HostMemoryMapper<M: SharedMemoryMapper> {
139 shm_mapper: M,
141 allocator: AddressAllocator,
143}
144
145impl<M: SharedMemoryMapper> VirtioMediaHostMemoryMapper for HostMemoryMapper<M> {
146 fn add_mapping(
147 &mut self,
148 buffer: BorrowedFd,
149 length: u64,
150 offset: u64,
151 rw: bool,
152 ) -> Result<u64, i32> {
153 let shm_offset = self
156 .allocator
157 .allocate(length, Alloc::FileBacked(offset), "".into())
158 .map_err(|_| libc::ENOMEM)?;
159
160 match self.shm_mapper.add_mapping(
161 VmMemorySource::Descriptor {
162 descriptor: buffer.try_clone_to_owned().map_err(|_| libc::EIO)?.into(),
163 offset: 0,
164 size: length,
165 },
166 shm_offset,
167 if rw {
168 Protection::read_write()
169 } else {
170 Protection::read()
171 },
172 hypervisor::MemCacheType::CacheCoherent,
173 ) {
174 Ok(()) => Ok(shm_offset),
175 Err(e) => {
176 base::error!("failed to map memory buffer: {:#}", e);
177 Err(libc::EINVAL)
178 }
179 }
180 }
181
182 fn remove_mapping(&mut self, offset: u64) -> Result<(), i32> {
183 let _ = self.allocator.release_containing(offset);
184
185 self.shm_mapper
186 .remove_mapping(offset)
187 .map_err(|_| libc::EINVAL)
188 }
189}
190
191struct GuestMemoryMapping {
195 arena: MemoryMappingArena,
196 start_offset: usize,
197}
198
199impl GuestMemoryMapping {
200 fn new(mem: &GuestMemory, sgs: &[SgEntry]) -> anyhow::Result<Self> {
201 let page_size = base::pagesize() as u64;
202 let page_mask = page_size - 1;
203
204 for sg in sgs.iter().skip(1) {
212 if sg.start & page_mask != 0 {
213 anyhow::bail!("non-initial SG entry start offset is not 0");
214 }
215 }
216 for sg in sgs.iter().take(sgs.len() - 1) {
217 if (sg.start + sg.len as u64) & page_mask != 0 {
218 anyhow::bail!("non-terminal SG entry with start + len != page_size");
219 }
220 }
221
222 let arena_size = sgs
224 .iter()
225 .fold(0, |size, sg| size + (sg.start & page_mask) + sg.len as u64)
226 .next_multiple_of(page_size);
228 let mut arena = MemoryMappingArena::new(arena_size as usize)?;
229
230 let mut pos = 0;
232 for region in sgs {
233 let region_first_page = region.start & !page_mask;
235 let len = region.start - region_first_page + region.len as u64;
236 let len = len.next_multiple_of(page_size) as usize;
238 let fd = mem.offset_region(region_first_page)?;
241 arena.add_fd_offset(pos, len, fd, region_first_page)?;
243
244 pos += len;
245 }
246
247 let start_offset = sgs
248 .first()
249 .map(|region| region.start & page_mask)
250 .unwrap_or(0) as usize;
251
252 Ok(GuestMemoryMapping {
253 arena,
254 start_offset,
255 })
256 }
257}
258
259impl GuestMemoryRange for GuestMemoryMapping {
260 fn as_ptr(&self) -> *const u8 {
261 unsafe { self.arena.as_ptr().add(self.start_offset) }
263 }
264
265 fn as_mut_ptr(&mut self) -> *mut u8 {
266 unsafe { self.arena.as_ptr().add(self.start_offset) }
268 }
269}
270
271struct GuestMemoryShadowMapping {
278 data: Vec<u8>,
280 mem: GuestMemory,
282 sgs: Vec<SgEntry>,
284 dirty: bool,
287}
288
289impl GuestMemoryShadowMapping {
290 fn new(mem: &GuestMemory, sgs: Vec<SgEntry>) -> anyhow::Result<Self> {
291 let total_size = sgs.iter().fold(0, |total, sg| total + sg.len as usize);
292 let mut data = vec![0u8; total_size];
293 let mut pos = 0;
294 for sg in &sgs {
295 mem.read_exact_at_addr(
296 &mut data[pos..pos + sg.len as usize],
297 GuestAddress(sg.start),
298 )?;
299 pos += sg.len as usize;
300 }
301
302 Ok(Self {
303 data,
304 mem: mem.clone(),
305 sgs,
306 dirty: false,
307 })
308 }
309}
310
311impl GuestMemoryRange for GuestMemoryShadowMapping {
312 fn as_ptr(&self) -> *const u8 {
313 self.data.as_ptr()
314 }
315
316 fn as_mut_ptr(&mut self) -> *mut u8 {
317 self.dirty = true;
318 self.data.as_mut_ptr()
319 }
320}
321
322impl Drop for GuestMemoryShadowMapping {
324 fn drop(&mut self) {
325 if !self.dirty {
327 return;
328 }
329
330 let mut pos = 0;
331 for sg in &self.sgs {
332 if let Err(e) = self.mem.write_all_at_addr(
333 &self.data[pos..pos + sg.len as usize],
334 GuestAddress(sg.start),
335 ) {
336 base::error!("failed to write back guest memory shadow mapping: {:#}", e);
337 }
338 pos += sg.len as usize;
339 }
340 }
341}
342
343enum GuestMemoryChunk {
345 Mapping(GuestMemoryMapping),
346 Shadow(GuestMemoryShadowMapping),
347}
348
349impl GuestMemoryRange for GuestMemoryChunk {
350 fn as_ptr(&self) -> *const u8 {
351 match self {
352 GuestMemoryChunk::Mapping(m) => m.as_ptr(),
353 GuestMemoryChunk::Shadow(s) => s.as_ptr(),
354 }
355 }
356
357 fn as_mut_ptr(&mut self) -> *mut u8 {
358 match self {
359 GuestMemoryChunk::Mapping(m) => m.as_mut_ptr(),
360 GuestMemoryChunk::Shadow(s) => s.as_mut_ptr(),
361 }
362 }
363}
364
365struct GuestMemoryMapper(GuestMemory);
371
372impl VirtioMediaGuestMemoryMapper for GuestMemoryMapper {
373 type GuestMemoryMapping = GuestMemoryChunk;
374
375 fn new_mapping(&self, sgs: Vec<SgEntry>) -> anyhow::Result<Self::GuestMemoryMapping> {
376 const MAPPING_THRESHOLD: usize = 0x400;
380 let total_size = sgs.iter().fold(0, |total, sg| total + sg.len as usize);
381
382 if total_size >= MAPPING_THRESHOLD {
383 GuestMemoryMapping::new(&self.0, &sgs).map(GuestMemoryChunk::Mapping)
384 } else {
385 GuestMemoryShadowMapping::new(&self.0, sgs).map(GuestMemoryChunk::Shadow)
386 }
387 }
388}
389
390#[derive(EventToken, Debug)]
391enum Token {
392 CommandQueue,
393 V4l2Session(u32),
394 Kill,
395}
396
397#[derive(Clone)]
399struct WaitContextPoller(Rc<WaitContext<Token>>);
400
401impl SessionPoller for WaitContextPoller {
402 fn add_session(&self, session: BorrowedFd, session_id: u32) -> Result<(), i32> {
403 self.0
404 .add_for_event(
405 &Descriptor(session.as_raw_fd()),
406 EventType::Read,
407 Token::V4l2Session(session_id),
408 )
409 .map_err(|e| e.errno())
410 }
411
412 fn remove_session(&self, session: BorrowedFd) {
413 let _ = self.0.delete(&Descriptor(session.as_raw_fd()));
414 }
415}
416
417struct Worker<D: VirtioMediaDevice<Reader, Writer>> {
419 runner: VirtioMediaDeviceRunner<Reader, Writer, D, WaitContextPoller>,
420 cmd_queue: Queue,
421 wait_ctx: Rc<WaitContext<Token>>,
422}
423
424impl<D> Worker<D>
425where
426 D: VirtioMediaDevice<Reader, Writer>,
427{
428 fn new(
430 device: D,
431 cmd_queue: Queue,
432 kill_evt: Event,
433 wait_ctx: Rc<WaitContext<Token>>,
434 ) -> anyhow::Result<Self> {
435 wait_ctx
436 .add_many(&[
437 (cmd_queue.event(), Token::CommandQueue),
438 (&kill_evt, Token::Kill),
439 ])
440 .context("when adding worker events to wait context")?;
441
442 Ok(Self {
443 runner: VirtioMediaDeviceRunner::new(device, WaitContextPoller(Rc::clone(&wait_ctx))),
444 cmd_queue,
445 wait_ctx,
446 })
447 }
448
449 fn run(&mut self) -> anyhow::Result<()> {
450 loop {
451 let wait_events = self.wait_ctx.wait().context("Wait error")?;
452
453 for wait_event in wait_events.iter() {
454 match wait_event.token {
455 Token::CommandQueue => {
456 let _ = self.cmd_queue.event().wait();
457 while let Some(mut desc) = self.cmd_queue.pop() {
458 self.runner
459 .handle_command(&mut desc.reader, &mut desc.writer);
460 self.cmd_queue.add_used(desc);
462 self.cmd_queue.trigger_interrupt();
463 }
464 }
465 Token::Kill => {
466 return Ok(());
467 }
468 Token::V4l2Session(session_id) => {
469 let session = match self.runner.sessions.get_mut(&session_id) {
470 Some(session) => session,
471 None => {
472 base::error!(
473 "received event for non-registered session {}",
474 session_id
475 );
476 continue;
477 }
478 };
479
480 if let Err(e) = self.runner.device.process_events(session) {
481 base::error!(
482 "error while processing events for session {}: {:#}",
483 session_id,
484 e
485 );
486 if let Some(session) = self.runner.sessions.remove(&session_id) {
487 self.runner.device.close_session(session);
488 }
489 }
490 }
491 }
492 }
493 }
494 }
495}
496
497struct CrosvmVirtioMediaDevice<
499 D: VirtioMediaDevice<Reader, Writer>,
500 F: Fn(EventQueue, GuestMemoryMapper, HostMemoryMapper<ArcedMemoryMapper>) -> anyhow::Result<D>,
501> {
502 create_device: F,
504 config: VirtioMediaDeviceConfig,
506
507 base_features: u64,
509 shm_mapper: Option<ArcedMemoryMapper>,
515 worker_thread: Option<WorkerThread<()>>,
517}
518
519impl<D, F> CrosvmVirtioMediaDevice<D, F>
520where
521 D: VirtioMediaDevice<Reader, Writer>,
522 F: Fn(EventQueue, GuestMemoryMapper, HostMemoryMapper<ArcedMemoryMapper>) -> anyhow::Result<D>,
523{
524 fn new(base_features: u64, config: VirtioMediaDeviceConfig, create_device: F) -> Self {
525 Self {
526 base_features,
527 config,
528 shm_mapper: None,
529 create_device,
530 worker_thread: None,
531 }
532 }
533}
534
535const HOST_MAPPER_RANGE: u64 = 1 << 32;
536
537impl<D, F> VirtioDevice for CrosvmVirtioMediaDevice<D, F>
538where
539 D: VirtioMediaDevice<Reader, Writer> + Send + 'static,
540 F: Fn(EventQueue, GuestMemoryMapper, HostMemoryMapper<ArcedMemoryMapper>) -> anyhow::Result<D>
541 + Send,
542{
543 fn keep_rds(&self) -> Vec<base::RawDescriptor> {
544 let mut keep_rds = Vec::new();
545
546 if let Some(fd) = self.shm_mapper.as_ref().and_then(|m| m.as_raw_descriptor()) {
547 keep_rds.push(fd);
548 }
549
550 keep_rds
551 }
552
553 fn device_type(&self) -> DeviceType {
554 DeviceType::Media
555 }
556
557 fn queue_max_sizes(&self) -> &[u16] {
558 QUEUE_SIZES
559 }
560
561 fn features(&self) -> u64 {
562 self.base_features
563 }
564
565 fn read_config(&self, offset: u64, data: &mut [u8]) {
566 copy_config(data, 0, self.config.as_ref(), offset);
567 }
568
569 fn activate(
570 &mut self,
571 mem: vm_memory::GuestMemory,
572 _interrupt: Interrupt,
573 mut queues: BTreeMap<usize, Queue>,
574 ) -> anyhow::Result<()> {
575 if queues.len() != QUEUE_SIZES.len() {
576 anyhow::bail!(
577 "wrong number of queues are passed: expected {}, actual {}",
578 queues.len(),
579 QUEUE_SIZES.len()
580 );
581 }
582
583 let cmd_queue = queues.remove(&0).context("missing queue 0")?;
584 let event_queue = EventQueue(queues.remove(&1).context("missing queue 1")?);
585
586 let shm_mapper = self
587 .shm_mapper
588 .clone()
589 .context("shared memory mapper was not specified")?;
590
591 let wait_ctx = WaitContext::new()?;
592 let device = (self.create_device)(
593 event_queue,
594 GuestMemoryMapper(mem),
595 HostMemoryMapper {
596 shm_mapper,
597 allocator: AddressAllocator::new(
598 AddressRange::from_start_and_end(0, HOST_MAPPER_RANGE - 1),
599 Some(base::pagesize() as u64),
600 None,
601 )?,
602 },
603 )?;
604
605 let worker_thread = WorkerThread::start("v_media_worker", move |e| {
606 let wait_ctx = Rc::new(wait_ctx);
607 let mut worker = match Worker::new(device, cmd_queue, e, wait_ctx) {
608 Ok(worker) => worker,
609 Err(e) => {
610 error!("failed to create virtio-media worker: {:#}", e);
611 return;
612 }
613 };
614 if let Err(e) = worker.run() {
615 error!("virtio_media worker exited with error: {:#}", e);
616 }
617 });
618
619 self.worker_thread = Some(worker_thread);
620 Ok(())
621 }
622
623 fn reset(&mut self) -> anyhow::Result<()> {
624 if let Some(worker_thread) = self.worker_thread.take() {
625 worker_thread.stop();
626 }
627
628 Ok(())
629 }
630
631 fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> {
632 Some(SharedMemoryRegion {
633 id: 0,
634 length: HOST_MAPPER_RANGE,
637 })
638 }
639
640 fn set_shared_memory_mapper(&mut self, mapper: Box<dyn SharedMemoryMapper>) {
641 self.shm_mapper = Some(ArcedMemoryMapper::from(mapper));
642 }
643}
644
645pub fn create_virtio_media_simple_capture_device(features: u64) -> Box<dyn VirtioDevice> {
650 use virtio_media::devices::SimpleCaptureDevice;
651 use virtio_media::v4l2r::ioctl::Capabilities;
652
653 let mut card = [0u8; 32];
654 let card_name = "simple_device";
655 card[0..card_name.len()].copy_from_slice(card_name.as_bytes());
656
657 let device = CrosvmVirtioMediaDevice::new(
658 features,
659 VirtioMediaDeviceConfig {
660 device_caps: (Capabilities::VIDEO_CAPTURE | Capabilities::STREAMING).bits(),
661 device_type: 0,
663 card,
664 },
665 |event_queue, _, host_mapper| Ok(SimpleCaptureDevice::new(event_queue, host_mapper)),
666 );
667
668 Box::new(device)
669}
670
671#[cfg(any(target_os = "android", target_os = "linux"))]
675pub fn create_virtio_media_v4l2_proxy_device<P: AsRef<Path>>(
676 features: u64,
677 device_path: P,
678) -> anyhow::Result<Box<dyn VirtioDevice>> {
679 use virtio_media::devices::V4l2ProxyDevice;
680 use virtio_media::v4l2r;
681 use virtio_media::v4l2r::ioctl::Capabilities;
682
683 let device = v4l2r::device::Device::open(
684 device_path.as_ref(),
685 v4l2r::device::DeviceConfig::new().non_blocking_dqbuf(),
686 )?;
687 let mut device_caps = device.caps().device_caps();
688
689 device_caps.remove(Capabilities::DEVICE_CAPS);
691
692 device_caps.remove(Capabilities::READWRITE);
694
695 let mut config = VirtioMediaDeviceConfig {
696 device_caps: device_caps.bits(),
697 device_type: 0,
699 card: Default::default(),
700 };
701 let card = &device.caps().card;
702 let name_slice = &card.as_bytes()[0..std::cmp::min(card.len(), config.card.len())];
703 config.card.as_mut_slice()[0..name_slice.len()].copy_from_slice(name_slice);
704 let device_path = PathBuf::from(device_path.as_ref());
705
706 let device = CrosvmVirtioMediaDevice::new(
707 features,
708 config,
709 move |event_queue, guest_mapper, host_mapper| {
710 let device =
711 V4l2ProxyDevice::new(device_path.clone(), event_queue, guest_mapper, host_mapper);
712
713 Ok(device)
714 },
715 );
716
717 Ok(Box::new(device))
718}
719
720#[cfg(feature = "video-decoder")]
724pub fn create_virtio_media_decoder_adapter_device(
725 features: u64,
726 _gpu_tube: base::Tube,
727 backend: VideoBackendType,
728) -> anyhow::Result<Box<dyn VirtioDevice>> {
729 use decoder_adapter::VirtioVideoAdapter;
730 use virtio_media::devices::video_decoder::VideoDecoder;
731 use virtio_media::v4l2r::ioctl::Capabilities;
732
733 #[cfg(feature = "ffmpeg")]
734 use crate::virtio::video::decoder::backend::ffmpeg::FfmpegDecoder;
735 #[cfg(feature = "vaapi")]
736 use crate::virtio::video::decoder::backend::vaapi::VaapiDecoder;
737 #[cfg(feature = "libvda")]
738 use crate::virtio::video::decoder::backend::vda::LibvdaDecoder;
739 use crate::virtio::video::decoder::DecoderBackend;
740
741 let mut card = [0u8; 32];
742 let card_name = format!("{backend:?} decoder adapter").to_lowercase();
743 card[0..card_name.len()].copy_from_slice(card_name.as_bytes());
744 let config = VirtioMediaDeviceConfig {
745 device_caps: (Capabilities::VIDEO_M2M_MPLANE | Capabilities::STREAMING).bits(),
746 device_type: 0,
748 card,
749 };
750
751 let create_device = move |event_queue, _, host_mapper: HostMemoryMapper<ArcedMemoryMapper>| {
752 let backend = match backend {
753 #[cfg(feature = "libvda")]
754 VideoBackendType::Libvda => {
755 LibvdaDecoder::new(libvda::decode::VdaImplType::Gavda)?.into_trait_object()
756 }
757 #[cfg(feature = "libvda")]
758 VideoBackendType::LibvdaVd => {
759 LibvdaDecoder::new(libvda::decode::VdaImplType::Gavd)?.into_trait_object()
760 }
761 #[cfg(feature = "vaapi")]
762 VideoBackendType::Vaapi => VaapiDecoder::new()?.into_trait_object(),
763 #[cfg(feature = "ffmpeg")]
764 VideoBackendType::Ffmpeg => FfmpegDecoder::new().into_trait_object(),
765 };
766
767 let adapter = VirtioVideoAdapter::new(backend);
768 let decoder = VideoDecoder::new(adapter, event_queue, host_mapper);
769
770 Ok(decoder)
771 };
772
773 Ok(Box::new(CrosvmVirtioMediaDevice::new(
774 features,
775 config,
776 create_device,
777 )))
778}