mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-09-03 18:23:49 +00:00
fmp4mux: Fix handling of stream with late single GOP
If a GOP starts after current chunk/fragment end draining should still happen for the other streams. So handle this situation gracefully and reset the late_gop state when done. This also fixes GAP buffer handling for video streams that require DTS. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2236>
This commit is contained in:
parent
cd45732f7c
commit
c3740cd6a9
1 changed files with 25 additions and 7 deletions
|
@ -261,6 +261,8 @@ struct Stream {
|
||||||
fragment_filled: bool,
|
fragment_filled: bool,
|
||||||
/// Whether a whole chunk is queued.
|
/// Whether a whole chunk is queued.
|
||||||
chunk_filled: bool,
|
chunk_filled: bool,
|
||||||
|
// First GOP starts after the end of chunk/fragment.
|
||||||
|
late_gop: bool,
|
||||||
|
|
||||||
/// Current position (DTS, or PTS for intra-only) to prevent
|
/// Current position (DTS, or PTS for intra-only) to prevent
|
||||||
/// timestamps from going backwards when queueing new buffers
|
/// timestamps from going backwards when queueing new buffers
|
||||||
|
@ -536,7 +538,7 @@ impl FMP4Mux {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a buffer is valid according to the stream configuration.
|
/// Checks if a buffer is valid according to the stream configuration.
|
||||||
fn check_buffer(buffer: &gst::BufferRef, stream: &Stream) -> Result<(), gst::FlowError> {
|
fn check_buffer(buffer: &mut gst::Buffer, stream: &Stream) -> Result<(), gst::FlowError> {
|
||||||
let Stream {
|
let Stream {
|
||||||
sinkpad,
|
sinkpad,
|
||||||
delta_frames,
|
delta_frames,
|
||||||
|
@ -547,9 +549,19 @@ impl FMP4Mux {
|
||||||
return Err(gst_base::AGGREGATOR_FLOW_NEED_DATA);
|
return Err(gst_base::AGGREGATOR_FLOW_NEED_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
if delta_frames.requires_dts() && buffer.dts().is_none() {
|
if buffer.dts().is_none() && delta_frames.requires_dts() {
|
||||||
gst::error!(CAT, obj = sinkpad, "Require DTS for video streams");
|
// For gap buffer simply set the missing DTS to PTS.
|
||||||
return Err(gst::FlowError::Error);
|
if buffer.flags().contains(gst::BufferFlags::GAP)
|
||||||
|
&& buffer.flags().contains(gst::BufferFlags::DROPPABLE)
|
||||||
|
&& buffer.size() == 0
|
||||||
|
{
|
||||||
|
let pts = buffer.pts();
|
||||||
|
buffer.make_mut().set_dts(pts);
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
gst::error!(CAT, obj = sinkpad, "Require DTS for video streams");
|
||||||
|
return Err(gst::FlowError::Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if buffer.pts().is_none() {
|
if buffer.pts().is_none() {
|
||||||
|
@ -592,7 +604,7 @@ impl FMP4Mux {
|
||||||
let Some(mut buffer) = stream.sinkpad.pop_buffer() else {
|
let Some(mut buffer) = stream.sinkpad.pop_buffer() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
Self::check_buffer(&buffer, stream)?;
|
Self::check_buffer(&mut buffer, stream)?;
|
||||||
|
|
||||||
let segment = match stream.sinkpad.segment().downcast::<gst::ClockTime>().ok() {
|
let segment = match stream.sinkpad.segment().downcast::<gst::ClockTime>().ok() {
|
||||||
Some(segment) => segment,
|
Some(segment) => segment,
|
||||||
|
@ -1618,6 +1630,7 @@ impl FMP4Mux {
|
||||||
"Stream's first GOP starting after this fragment"
|
"Stream's first GOP starting after this fragment"
|
||||||
);
|
);
|
||||||
stream.fragment_filled = true;
|
stream.fragment_filled = true;
|
||||||
|
stream.late_gop = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1668,6 +1681,7 @@ impl FMP4Mux {
|
||||||
"Stream's first GOP starting after this chunk"
|
"Stream's first GOP starting after this chunk"
|
||||||
);
|
);
|
||||||
stream.chunk_filled = true;
|
stream.chunk_filled = true;
|
||||||
|
stream.late_gop = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1743,6 +1757,7 @@ impl FMP4Mux {
|
||||||
"Stream's first GOP starting after this fragment"
|
"Stream's first GOP starting after this fragment"
|
||||||
);
|
);
|
||||||
stream.fragment_filled = true;
|
stream.fragment_filled = true;
|
||||||
|
stream.late_gop = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1956,6 +1971,7 @@ impl FMP4Mux {
|
||||||
timeout
|
timeout
|
||||||
|| need_new_header
|
|| need_new_header
|
||||||
|| stream.sinkpad.is_eos()
|
|| stream.sinkpad.is_eos()
|
||||||
|
|| stream.late_gop
|
||||||
|| stream
|
|| stream
|
||||||
.queued_gops
|
.queued_gops
|
||||||
.get(1)
|
.get(1)
|
||||||
|
@ -1965,7 +1981,8 @@ impl FMP4Mux {
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut gops = Vec::with_capacity(stream.queued_gops.len());
|
let mut gops = Vec::with_capacity(stream.queued_gops.len());
|
||||||
if stream.queued_gops.is_empty() {
|
if stream.queued_gops.is_empty() || stream.late_gop {
|
||||||
|
stream.late_gop = false;
|
||||||
return Ok(gops);
|
return Ok(gops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3119,7 +3136,7 @@ impl FMP4Mux {
|
||||||
}
|
}
|
||||||
|
|
||||||
if interleaved_buffers.is_empty() {
|
if interleaved_buffers.is_empty() {
|
||||||
assert!(at_eos);
|
// Either at EOS or only gap buffers drained.
|
||||||
return Ok((caps, None));
|
return Ok((caps, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3612,6 +3629,7 @@ impl FMP4Mux {
|
||||||
queued_gops: VecDeque::new(),
|
queued_gops: VecDeque::new(),
|
||||||
fragment_filled: false,
|
fragment_filled: false,
|
||||||
chunk_filled: false,
|
chunk_filled: false,
|
||||||
|
late_gop: false,
|
||||||
current_position: gst::ClockTime::MIN_SIGNED,
|
current_position: gst::ClockTime::MIN_SIGNED,
|
||||||
running_time_utc_time_mapping: None,
|
running_time_utc_time_mapping: None,
|
||||||
extra_header_data: None,
|
extra_header_data: None,
|
||||||
|
|
Loading…
Reference in a new issue