]> git.nihav.org Git - nihav-player.git/commitdiff
hwdec-vaapi: handle copying data into kodaed chroma planes master
authorKostya Shishkov <kostya.shiskov@gmail.com>
Fri, 27 Jun 2025 05:38:12 +0000 (07:38 +0200)
committerKostya Shishkov <kostya.shiskov@gmail.com>
Fri, 27 Jun 2025 05:38:12 +0000 (07:38 +0200)
For, say, 720x408 frame hardware decoder may report chroma plane width as 368
while destination frame chroma width is merely 360. Assuming width <= stride
leads to panic during copying.

hwdec-vaapi/src/lib.rs
nihed-cros-libva/src/drmprime.rs [new file with mode: 0644]
nihed-cros-libva/src/lib.rs
nihed-cros-libva/src/picture.rs
sndplay/src/demux.rs
videoplayer/src/main.rs

index 40e14f445fc17e24793419ae76b82cc1d2b2eba0..9dca7b17b69407b2b01d3d3fa2331647568b3dce 100644 (file)
@@ -273,6 +273,7 @@ struct VaapiInternals {
 pub struct VaapiH264Decoder {
     info:           NACodecInfoRef,
     vaapi:          Option<VaapiInternals>,
+    needs_derive:   bool,
     spses:          Vec<SeqParameterSet>,
     ppses:          Vec<PicParameterSet>,
     frame_refs:     FrameRefs,
@@ -283,8 +284,7 @@ pub struct VaapiH264Decoder {
     tb_den:         u32,
 }
 
-#[cfg(not(target_arch="x86_64"))]
-fn copy_luma(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+fn copy_luma_default(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
     for (dline, sline) in dst.chunks_mut(dstride)
             .zip(src.chunks(sstride))
             .take(h) {
@@ -292,6 +292,10 @@ fn copy_luma(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usiz
     }
 }
 #[cfg(not(target_arch="x86_64"))]
+fn copy_luma(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    copy_luma_default(dst, dstride, src, sstride, w, h);
+}
+#[cfg(not(target_arch="x86_64"))]
 fn deint_chroma(frm: NASimpleVideoFrame<u8>, src: &[u8], sstride: usize) {
     let mut uoff = frm.offset[1];
     let mut voff = frm.offset[2];
@@ -309,6 +313,10 @@ fn deint_chroma(frm: NASimpleVideoFrame<u8>, src: &[u8], sstride: usize) {
 use std::arch::asm;
 #[cfg(target_arch="x86_64")]
 fn copy_luma(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    if !is_x86_feature_detected!("avx") {
+        copy_luma_default(dst, dstride, src, sstride, w, h);
+        return;
+    }
     if dst.as_ptr().align_offset(32) == 0 && src.as_ptr().align_offset(32) == 0 &&
             (w % 64) == 0 && ((dstride | sstride) % 32) == 0 {
         unsafe {
@@ -340,18 +348,19 @@ fn copy_luma(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usiz
             );
         }
     } else {
+        let copy_len = dstride.min(w);
         for (dline, sline) in dst.chunks_mut(dstride)
                 .zip(src.chunks(sstride))
                 .take(h) {
-            dline[..w].copy_from_slice(&sline[..w]);
+            dline[..copy_len].copy_from_slice(&sline[..copy_len]);
         }
     }
 }
 #[cfg(target_arch="x86_64")]
 fn deint_chroma(frm: NASimpleVideoFrame<u8>, src: &[u8], sstride: usize) {
     unsafe {
-        let width = frm.width[1];
-        let height = frm.height[1];
+        let width = (frm.width[1] + 7) & !7;
+        let height = (frm.height[1] + 7) & !7;
         let dst = frm.data.as_mut_ptr();
         let udst = dst.add(frm.offset[1]);
         let vdst = dst.add(frm.offset[2]);
@@ -416,12 +425,12 @@ fn deint_chroma(frm: NASimpleVideoFrame<u8>, src: &[u8], sstride: usize) {
     }
 }
 
-fn fill_frame(ifmt: VAImageFormat, pic: &Picture<PictureSync>, frm: &mut NABufferType) -> DecoderResult<()> {
+fn fill_frame(ifmt: VAImageFormat, pic: &Picture<PictureSync>, frm: &mut NABufferType, needs_derive: bool) -> DecoderResult<()> {
     let mut vbuf = frm.get_vbuf().unwrap();
     let (w, h) = pic.surface_size();
     //let cur_ts = pic.timestamp();
 
-    let img = Image::new(pic, ifmt, w, h, true).expect("get image");
+    let img = Image::new(pic, ifmt, w, h, !needs_derive).expect("get image");
 
     let iimg = img.image();
     let imgdata: &[u8] = img.as_ref();
@@ -429,13 +438,22 @@ fn fill_frame(ifmt: VAImageFormat, pic: &Picture<PictureSync>, frm: &mut NABuffe
     match iimg.format.fourcc().map_err(|_| DecoderError::InvalidData)? {
         VAFourcc::NV12 => {
             let frm = NASimpleVideoFrame::from_video_buf(&mut vbuf).unwrap();
-            validate!(iimg.width == (frm.width[0] as u16));
-            validate!(iimg.height == (frm.height[0] as u16));
+            validate!(iimg.width  == (((frm.width[0]  + 15) & !15) as u16));
+            validate!(iimg.height == (((frm.height[0] + 15) & !15) as u16));
 
-            copy_luma(&mut frm.data[frm.offset[0]..], frm.stride[0], &imgdata[iimg.offsets[0] as usize..], iimg.pitches[0] as usize, frm.width[0], frm.height[0]);
+            copy_luma(&mut frm.data[frm.offset[0]..], frm.stride[0], &imgdata[iimg.offsets[0] as usize..], iimg.pitches[0] as usize, (frm.width[0] + 15) & !15, (frm.height[0] + 15) & !15);
 
             deint_chroma(frm, &imgdata[iimg.offsets[1] as usize..], iimg.pitches[1] as usize);
         },
+        VAFourcc::YV12 => {
+            let frm = NASimpleVideoFrame::from_video_buf(&mut vbuf).unwrap();
+            validate!(iimg.width  == (((frm.width[0]  + 15) & !15) as u16));
+            validate!(iimg.height == (((frm.height[0] + 15) & !15) as u16));
+
+            copy_luma(&mut frm.data[frm.offset[0]..], frm.stride[0], &imgdata[iimg.offsets[0] as usize..], iimg.pitches[0] as usize, (frm.width[0] + 15) & !15, (frm.height[0] + 15) & !15);
+            copy_luma(&mut frm.data[frm.offset[2]..], frm.stride[2], &imgdata[iimg.offsets[1] as usize..], iimg.pitches[1] as usize, (frm.width[1] + 15) & !15, (frm.height[1] + 15) & !15);
+            copy_luma(&mut frm.data[frm.offset[1]..], frm.stride[1], &imgdata[iimg.offsets[2] as usize..], iimg.pitches[2] as usize, (frm.width[2] + 15) & !15, (frm.height[2] + 15) & !15);
+        },
         _ => unimplemented!(),
     };
     Ok(())
@@ -446,6 +464,7 @@ impl Default for VaapiH264Decoder {
         Self {
             info:           NACodecInfoRef::default(),
             vaapi:          None,
+            needs_derive:   false,
             spses:          Vec::with_capacity(1),
             ppses:          Vec::with_capacity(4),
             frame_refs:     FrameRefs::new(),
@@ -477,10 +496,10 @@ impl VaapiH264Decoder {
                 let _compatibility      = br.read_byte()?;
                 let _level              = br.read_byte()?;
                 let b                   = br.read_byte()?;
-                validate!((b & 0xFC) == 0xFC);
+                //validate!((b & 0xFC) == 0xFC);
                 self.nal_len            = (b & 3) + 1;
                 let b                   = br.read_byte()?;
-                validate!((b & 0xE0) == 0xE0);
+                //validate!((b & 0xE0) == 0xE0);
                 let num_sps = (b & 0x1F) as usize;
                 for _ in 0..num_sps {
                     let len             = br.read_u16be()? as usize;
@@ -584,6 +603,10 @@ println!("no decoding support for this profile");
                 return Err(DecoderError::Bug);
             }
 
+            let needs_derive= if let Ok(vendor) = display.query_vendor_string() {
+                    vendor.contains("Kaby Lake")
+                } else { false };
+
             let config = display.create_config(vec![
                     VAConfigAttrib { type_: VAConfigAttribType::VAConfigAttribRTFormat, value: RTFormat::YUV420.into() },
                 ], va_profile, VAEntrypoint::VAEntrypointVLD).map_err(|_| {
@@ -606,8 +629,9 @@ println!("config creation failed!");
             }
 
             self.vaapi = Some(VaapiInternals { display, context, ref_pics, surfaces, ifmt });
+            self.needs_derive = needs_derive;
 
-            let vinfo = NAVideoInfo::new(width, height, false, YUV420_FORMAT);
+            let vinfo = NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, YUV420_FORMAT);
             self.info = NACodecInfo::new_ref(info.get_name(), NACodecTypeInfo::Video(vinfo), info.get_extradata()).into_ref();
             self.out_frm = alloc_video_buffer(vinfo, 4)?;
 
@@ -1033,7 +1057,7 @@ panic!("ran out of free surfaces");
                 let is_ref = frm.is_ref;
                 let ftype = frm.ftype;
                 if let Ok(pic) = frm.pic.sync() {
-                    let _ = fill_frame(vactx.ifmt, &pic, &mut self.out_frm);
+                    let _ = fill_frame(vactx.ifmt, &pic, &mut self.out_frm, self.needs_derive);
 
                     if !is_ref {
                         if let Ok(surf) = pic.take_surface() {
@@ -1066,7 +1090,7 @@ panic!("ran out of free surfaces");
                 let is_ref = frm.is_ref;
                 let ftype = frm.ftype;
                 if let Ok(pic) = frm.pic.sync() {
-                    let _ = fill_frame(vactx.ifmt, &pic, &mut self.out_frm);
+                    let _ = fill_frame(vactx.ifmt, &pic, &mut self.out_frm, self.needs_derive);
 
                     if !is_ref {
                         if let Ok(surf) = pic.take_surface() {
diff --git a/nihed-cros-libva/src/drmprime.rs b/nihed-cros-libva/src/drmprime.rs
new file mode 100644 (file)
index 0000000..2089137
--- /dev/null
@@ -0,0 +1,128 @@
+/// Kernel DRM buffer memory type.
+pub const VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM: u32 =      0x10000000;
+/// DRM PRIME memory type (old version)
+///
+/// This supports only single objects with restricted memory layout.
+/// Used with VASurfaceAttribExternalBuffers.
+pub const VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME: u32 =        0x20000000;
+/// DRM PRIME memory type
+///
+/// Used with VADRMPRIMESurfaceDescriptor.
+pub const VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2: u32 =      0x40000000;
+/// DRM PRIME3 memory type
+///
+/// Used with VADRMPRIME3SurfaceDescriptor.
+pub const VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_3: u32 =      0x08000000;
+
+/// Surface object description.
+#[derive(Debug,Clone,Copy,Default)]
+#[repr(C)]
+pub struct VADRMPrimeObject {
+    /// DRM PRIME file descriptor for this object.
+    pub fd: i32,
+    /// Total size of this object (may include regions which are not part of the surface).
+    pub size: u32,
+    /// Format modifier applied to this object.
+    pub drm_format_modifier: u64,
+}
+
+/// Surface layer description.
+#[derive(Debug,Clone,Copy,Default)]
+#[repr(C)]
+pub struct VADRMPrimeLayer {
+    /// DRM format fourcc of this layer (DRM_FOURCC_*).
+    pub drm_format: u32,
+    /// Number of planes in this layer.
+    pub num_planes: u32,
+    /// Index in the objects array of the object containing each plane.
+    pub object_index: [u32; 4],
+    /// Offset within the object of each plane.
+    pub offset: [u32; 4],
+    /// Pitch of each plane.
+    pub pitch: [u32; 4],
+}
+
+/**
+ * External buffer descriptor for a DRM PRIME surface.
+ *
+ * For export, call vaExportSurfaceHandle() with mem_type set to
+ * VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 and pass a pointer to an
+ * instance of this structure to fill.
+ * If VA_EXPORT_SURFACE_SEPARATE_LAYERS is specified on export, each
+ * layer will contain exactly one plane.  For example, an NV12
+ * surface will be exported as two layers, one of DRM_FORMAT_R8 and
+ * one of DRM_FORMAT_GR88.
+ * If VA_EXPORT_SURFACE_COMPOSED_LAYERS is specified on export,
+ * there will be exactly one layer.
+ *
+ * For import, call vaCreateSurfaces() with the MemoryType attribute
+ * set to VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 and the
+ * ExternalBufferDescriptor attribute set to point to an array of
+ * num_surfaces instances of this structure.
+ * The number of planes which need to be provided for a given layer
+ * is dependent on both the format and the format modifier used for
+ * the objects containing it.  For example, the format DRM_FORMAT_RGBA
+ * normally requires one plane, but with the format modifier
+ * I915_FORMAT_MOD_Y_TILED_CCS it requires two planes - the first
+ * being the main data plane and the second containing the color
+ * control surface.
+ * Note that a given driver may only support a subset of possible
+ * representations of a particular format.  For example, it may only
+ * support NV12 surfaces when they are contained within a single DRM
+ * object, and therefore fail to create such surfaces if the two
+ * planes are in different DRM objects.
+ * Note that backend driver will retrieve the resource represent by fd,
+ * and a valid surface ID is generated. Backend driver will not close
+ * the file descriptor. Application should handle the release of the fd.
+ * releasing the fd will not impact the existence of the surface.
+ */
+#[derive(Debug,Clone,Copy,Default)]
+#[repr(C)]
+pub struct VADRMPRIMESurfaceDescriptor {
+    /// Pixel format fourcc of the whole surface (VA_FOURCC_*).
+    pub fourcc: u32,
+    /// Width of the surface in pixels.
+    pub width: u32,
+    /// Height of the surface in pixels.
+    pub height: u32,
+    /// Number of distinct DRM objects making up the surface.
+    pub num_objects: u32,
+    /// Description of each object.
+    pub objects: [VADRMPrimeObject; 4],
+    /// Number of layers making up the surface.
+    pub num_layers: u32,
+    /// Description of each layer in the surface.
+    pub layers: [VADRMPrimeLayer; 4],
+}
+
+/**
+ * External buffer descriptor for a DRM PRIME surface with flags
+ *
+ * This structure is an extention for VADRMPRIMESurfaceDescriptor,
+ * it has the same behavior as if used with VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2.
+ *
+ * The field "flags" is added, see "Surface external buffer descriptor flags".
+ * To use this structure, use VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_3 instead.
+ */
+#[derive(Debug,Clone,Copy,Default)]
+#[repr(C)]
+pub struct VADRMPRIME3SurfaceDescriptor {
+    /// Pixel format fourcc of the whole surface (VA_FOURCC_*).
+    pub fourcc: u32,
+    /// Width of the surface in pixels.
+    pub width: u32,
+    /// Height of the surface in pixels.
+    pub height: u32,
+    /// Number of distinct DRM objects making up the surface.
+    pub num_objects: u32,
+    /// Description of each object.
+    pub objects: [VADRMPrimeObject; 4],
+    /// Number of layers making up the surface.
+    pub num_layers: u32,
+    /// Description of each layer in the surface.
+    pub layers: [VADRMPrimeLayer; 4],
+    /// flags. See "Surface external buffer descriptor flags".
+    pub flags: u32,
+    /// reserved bytes, must be zero
+    reserved: [u32; 8 - 1],
+}
index a095c1c35e69b268195251c003417a989106672f..2d3dbd5369c0cb85710058cc3307ec61a31679b0 100644 (file)
@@ -14,6 +14,7 @@ pub mod buffer;
 mod config;
 mod context;
 mod display;
+mod drmprime;
 mod formats;
 mod generic_value;
 mod image;
@@ -34,6 +35,7 @@ pub use buffer::*;
 pub use config::*;
 pub use context::*;
 pub use display::*;
+pub use drmprime::*;
 pub use formats::*;
 pub use generic_value::*;
 pub use image::*;
index 0d28b517edace6d979468f410464572a9e62f5b5..7dd579019fc59f52b8ef6351b68d644501a331b7 100644 (file)
@@ -12,6 +12,7 @@ use crate::bindings;
 use crate::buffer::Buffer;
 use crate::context::Context;
 use crate::display::Display;
+use crate::drmprime::*;
 use crate::status::*;
 use crate::surface::Surface;
 
@@ -244,6 +245,21 @@ impl<S: PictureState> Picture<S> {
     pub fn surface_size(&self) -> (u32, u32) {
         self.inner.surface.borrow().size()
     }
+
+    /// Returns DRM Prime surface descriptor.
+    pub fn surface_descriptor(&self) -> Option<VADRMPRIMESurfaceDescriptor> {
+        unsafe {
+            let mut desc = VADRMPRIMESurfaceDescriptor::default();
+            let display = self.inner.context.display().handle();
+            let handle = self.inner.surface.borrow().id();
+            let ret = crate::bindings::vaExportSurfaceHandle(display, handle, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, crate::constants::VA_EXPORT_SURFACE_READ_ONLY, &mut desc as *mut VADRMPRIMESurfaceDescriptor as *mut ::std::os::raw::c_void);
+            if ret == 0 {
+                Some(desc)
+            } else {
+                None
+            }
+        }
+    }
 }
 
 impl<S: PictureReclaimableSurface> Picture<S> {
index c35167c97ae7bbae7803fe054b833f3b5974a2cc..d050bceb2ff592695575d1220927328547ddaea3 100644 (file)
@@ -97,7 +97,8 @@ impl<'a> DemuxerObject<'a> {
                     let mut pkts = Vec::new();
                     for stream in dmx.get_streams() {
                         if let Some(pcreate) = reg.pkt_reg.find_packetiser(stream.get_info().get_name()) {
-                            let packetiser = (pcreate)();
+                            let mut packetiser = (pcreate)();
+                            packetiser.attach_stream(stream);
                             pkts.push(Some(packetiser));
                         } else {
                             pkts.push(None);
index 82fb56ffeca8c8aafcdcf4489d019a2ef069625a..94dea167822348a6f0a400bc9245f38d43d2e5f6 100644 (file)
@@ -320,7 +320,7 @@ fn try_display(disp_queue: &mut DispQueue, canvas: &mut Canvas<Window>, osd: &mu
 
             disp_queue.move_start();
             if !disp_queue.is_empty() {
-                return Some((disp_queue.first_ts - ctime).saturating_sub(2));
+                return Some(disp_queue.first_ts.saturating_sub(ctime).saturating_sub(2));
             } else {
                 return None;
             }
@@ -549,6 +549,12 @@ impl Player {
                             self.acontrol.set_volume(self.volume);
                         }
                     },
+                    Keycode::Equals if keymod.contains(Mod::RSHIFTMOD) || keymod.contains(Mod::LSHIFTMOD) => {
+                        self.volume = (self.volume + 10).min(MAX_VOLUME);
+                        if !self.mute {
+                            self.acontrol.set_volume(self.volume);
+                        }
+                    },
                     Keycode::Minus | Keycode::KpMinus => {
                         self.volume = self.volume.saturating_sub(10);
                         if !self.mute {