dav1ddec: Use downstream buffer pool for copying if video meta is not supported

And if strides are not matching either, which they usually don't for
dav1d because of its 128 pixel alignment requirements.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/661

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2134>
This commit is contained in:
Sebastian Dröge 2025-03-22 11:50:10 +02:00 committed by GStreamer Marge Bot
parent 230e612b8e
commit fc444ba5e7

View file

@ -467,20 +467,15 @@ impl Dav1dDec {
fn decoded_picture_as_buffer( fn decoded_picture_as_buffer(
&self, &self,
state_guard: &mut MutexGuard<Option<State>>, mut state_guard: MutexGuard<Option<State>>,
pic: &dav1d::Picture, pic: &dav1d::Picture,
output_state: gst_video::VideoCodecState<gst_video::video_codec_state::Readable>, output_state: gst_video::VideoCodecState<gst_video::video_codec_state::Readable>,
) -> Result<gst::Buffer, gst::FlowError> { codec_frame: &mut gst_video::VideoCodecFrame,
let mut offsets = vec![]; ) -> Result<(), gst::FlowError> {
let mut strides = vec![];
let mut acc_offset: usize = 0;
let state = state_guard.as_mut().ok_or(gst::FlowError::Flushing)?; let state = state_guard.as_mut().ok_or(gst::FlowError::Flushing)?;
let video_meta_supported = state.video_meta_supported; let video_meta_supported = state.video_meta_supported;
let info = output_state.info(); let info = output_state.info();
let mut out_buffer = gst::Buffer::new();
let mut_buffer = out_buffer.get_mut().unwrap();
let components = if info.is_yuv() { let components = if info.is_yuv() {
const YUV_COMPONENTS: [dav1d::PlanarImageComponent; 3] = [ const YUV_COMPONENTS: [dav1d::PlanarImageComponent; 3] = [
@ -497,66 +492,110 @@ impl Dav1dDec {
} else { } else {
unreachable!(); unreachable!();
}; };
for &component in components {
let dest_stride: u32 = info.stride()[component as usize].try_into().unwrap(); let mut offsets = [0; 4];
let mut strides = [0; 4];
let mut acc_offset = 0usize;
for (idx, &component) in components.iter().enumerate() {
let plane = pic.plane(component); let plane = pic.plane(component);
let (src_stride, height) = pic.plane_data_geometry(component); let src_stride = pic.stride(component);
let mem = if video_meta_supported || src_stride == dest_stride {
gst::Memory::from_slice(plane)
} else {
gst::trace!(
gst::CAT_PERFORMANCE,
imp = self,
"Copying decoded video frame component {:?}",
component
);
let src_slice = plane.as_ref(); let mem_size = plane.len();
let mem = gst::Memory::with_size((dest_stride * height) as usize);
let mut writable_mem = mem
.into_mapped_memory_writable()
.map_err(|_| gst::FlowError::Error)?;
let len = std::cmp::min(src_stride, dest_stride) as usize;
for (out_line, in_line) in writable_mem strides[idx] = src_stride as i32;
.as_mut_slice() offsets[idx] = acc_offset;
.chunks_exact_mut(dest_stride.try_into().unwrap())
.zip(src_slice.chunks_exact(src_stride.try_into().unwrap()))
{
out_line.copy_from_slice(&in_line[..len]);
}
writable_mem.into_memory()
};
let mem_size = mem.size();
mut_buffer.append_memory(mem);
strides.push(src_stride as i32);
offsets.push(acc_offset);
acc_offset += mem_size; acc_offset += mem_size;
} }
if video_meta_supported { let strides = &strides[..components.len()];
gst_video::VideoMeta::add_full( let offsets = &offsets[..components.len()];
out_buffer.get_mut().unwrap(),
gst_video::VideoFrameFlags::empty(), let strides_matching = info.offset() == offsets && info.stride() == strides;
info.format(),
info.width(), // We can forward decoded memory as-is if downstream supports the video meta
info.height(), // or the strides/offsets are matching with the default ones.
&offsets, let forward_memory = video_meta_supported || strides_matching;
&strides[..],
) drop(state_guard);
.unwrap();
} let mut out_buffer = forward_memory.then(gst::Buffer::new);
let mut_buffer = if forward_memory {
out_buffer.as_mut().unwrap().get_mut().unwrap()
} else {
self.obj().allocate_output_frame(codec_frame, None)?;
codec_frame
.output_buffer_mut()
.expect("output_buffer is set")
};
let duration = pic.duration() as u64; let duration = pic.duration() as u64;
if duration > 0 { if duration > 0 {
out_buffer mut_buffer.set_duration(duration.nseconds());
.get_mut()
.unwrap()
.set_duration(duration.nseconds());
} }
Ok(out_buffer) if forward_memory {
assert!(mut_buffer.size() == 0);
for &component in components {
let plane = pic.plane(component);
let mem = gst::Memory::from_slice(plane);
mut_buffer.append_memory(mem);
}
if video_meta_supported {
gst_video::VideoMeta::add_full(
mut_buffer,
gst_video::VideoFrameFlags::empty(),
info.format(),
info.width(),
info.height(),
offsets,
strides,
)
.unwrap();
}
assert!(codec_frame.output_buffer().is_none());
codec_frame.set_output_buffer(out_buffer.expect("out_buffer is set"));
} else {
gst::trace!(
gst::CAT_PERFORMANCE,
imp = self,
"Copying decoded video frame to output",
);
assert!(mut_buffer.size() > 0);
let mut vframe = gst_video::VideoFrameRef::from_buffer_ref_writable(mut_buffer, &info)
.expect("can map writable frame");
for (idx, &component) in components.iter().enumerate() {
let plane = pic.plane(component);
let (src_stride, src_height) = pic.plane_data_geometry(component);
let src_slice = plane.as_ref();
let dest_stride = vframe.plane_stride()[idx] as u32;
let dest_height = vframe.plane_height(idx as u32);
let dest_plane_data = vframe
.plane_data_mut(idx as u32)
.expect("can get plane data");
let chunk_len = std::cmp::min(src_stride, dest_stride) as usize;
if src_stride == dest_stride && src_height == dest_height {
dest_plane_data.copy_from_slice(src_slice);
} else {
for (out_line, in_line) in dest_plane_data
.chunks_exact_mut(dest_stride as usize)
.zip(src_slice.chunks_exact(src_stride as usize))
{
out_line.copy_from_slice(&in_line[..chunk_len]);
}
}
}
}
Ok(())
} }
fn handle_picture<'s>( fn handle_picture<'s>(
@ -576,10 +615,7 @@ impl Dav1dDec {
let frame = instance.frame(offset); let frame = instance.frame(offset);
if let Some(mut frame) = frame { if let Some(mut frame) = frame {
let output_buffer = self.decoded_picture_as_buffer(state_guard, pic, output_state, &mut frame)?;
self.decoded_picture_as_buffer(&mut state_guard, pic, output_state)?;
frame.set_output_buffer(output_buffer);
drop(state_guard);
instance.finish_frame(frame)?; instance.finish_frame(frame)?;
Ok(self.state.lock().unwrap()) Ok(self.state.lock().unwrap())
} else { } else {