summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorJan Schmidt <jan@centricular.com>2017-04-05 15:36:06 +1000
committerJan Schmidt <jan@centricular.com>2019-01-30 14:53:02 +1100
commitd4594c5c755e815540f2d5f1bdc5b83c293dcbca (patch)
treee966a72a75879fd49a85b8e0dd21b5e404460d70 /ext
parente75e6375310d9985f821f9e495384d7fb990a147 (diff)
downloadgstreamer-plugins-bad-d4594c5c755e815540f2d5f1bdc5b83c293dcbca.tar.gz
openh264dec: Handle B frame decoding
Support out-of-order decoding in OpenH264 by tracking our internal frame reference in the OpenH264 timestamp field. Drain any pending frames at EOS.
Diffstat (limited to 'ext')
-rw-r--r--ext/openh264/gstopenh264dec.cpp100
1 files changed, 57 insertions, 43 deletions
diff --git a/ext/openh264/gstopenh264dec.cpp b/ext/openh264/gstopenh264dec.cpp
index 303ce87dd..d4b2da88e 100644
--- a/ext/openh264/gstopenh264dec.cpp
+++ b/ext/openh264/gstopenh264dec.cpp
@@ -206,78 +206,92 @@ gst_openh264dec_handle_frame (GstVideoDecoder * decoder,
guint i;
guint8 *p;
guint row_stride, component_width, component_height, src_width, row;
- gboolean at_eos = (frame == NULL);
- if (frame) {
+ if (frame == NULL) {
+#if OPENH264_VERSION_CHECK (1,9)
+ /* Called with no videoframe for EOS logic. Drain out */
+ int end_of_stream = 1;
+ memset (&dst_buf_info, 0, sizeof (SBufferInfo));
+
+ openh264dec->decoder->SetOption (DECODER_OPTION_END_OF_STREAM,
+ &end_of_stream);
+ ret = openh264dec->decoder->FlushFrame (yuvdata, &dst_buf_info);
+
+ if (ret != dsErrorFree || dst_buf_info.iBufferStatus != 1) {
+ GST_DEBUG_OBJECT (decoder, "No more frames to retrieve at EOS");
+ return GST_FLOW_EOS;
+ }
+#else
+ return GST_FLOW_EOS;
+#endif
+ } else {
if (!gst_buffer_map (frame->input_buffer, &map_info, GST_MAP_READ)) {
GST_ERROR_OBJECT (openh264dec, "Cannot map input buffer!");
gst_video_codec_frame_unref (frame);
return GST_FLOW_ERROR;
}
- GST_LOG_OBJECT (openh264dec, "handle frame, %d",
+ GST_LOG_OBJECT (openh264dec, "handle frame, 1st NAL type %d",
map_info.size > 4 ? map_info.data[4] & 0x1f : -1);
memset (&dst_buf_info, 0, sizeof (SBufferInfo));
+ /* Use the unsigned long long OpenH264 timestamp to store the system_frame_number
+ * to track the original frame through any OpenH264 reordering */
+ dst_buf_info.uiInBsTimeStamp = frame->system_frame_number;
+
+ GST_LOG_OBJECT (decoder, "Submitting frame with PTS %" GST_TIME_FORMAT
+ " and frame ref %" G_GUINT64_FORMAT,
+ GST_TIME_ARGS (frame->pts), (guint64) frame->system_frame_number);
+
ret =
openh264dec->decoder->DecodeFrame2 (map_info.data, map_info.size,
yuvdata, &dst_buf_info);
+ gst_buffer_unmap (frame->input_buffer, &map_info);
- if (ret == dsNoParamSets) {
+ if (ret != dsErrorFree) {
+ /* Request a key unit from upstream */
GST_DEBUG_OBJECT (openh264dec, "Requesting a key unit");
gst_pad_push_event (GST_VIDEO_DECODER_SINK_PAD (decoder),
gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE,
FALSE, 0));
- }
- if (ret != dsErrorFree && ret != dsNoParamSets) {
- GST_DEBUG_OBJECT (openh264dec, "Requesting a key unit");
- gst_pad_push_event (GST_VIDEO_DECODER_SINK_PAD (decoder),
- gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE,
- FALSE, 0));
GST_LOG_OBJECT (openh264dec, "error decoding nal, return code: %d", ret);
- }
+ gst_video_codec_frame_unref (frame);
- gst_buffer_unmap (frame->input_buffer, &map_info);
- if (ret != dsErrorFree)
- return gst_video_decoder_drop_frame (decoder, frame);
+ /* Get back the frame that was reported as errored */
+ frame =
+ gst_video_decoder_get_frame (decoder, dst_buf_info.uiOutYuvTimeStamp);
+ if (frame) {
+ GST_LOG_OBJECT (decoder,
+ "Dropping errored frame ref %" G_GUINT64_FORMAT,
+ (guint64) dst_buf_info.uiOutYuvTimeStamp);
+ return gst_video_decoder_drop_frame (decoder, frame);
+ }
+ return GST_FLOW_OK;
+ }
gst_video_codec_frame_unref (frame);
frame = NULL;
- } else {
- memset (&dst_buf_info, 0, sizeof (SBufferInfo));
- ret = openh264dec->decoder->DecodeFrame2 (NULL, 0, yuvdata, &dst_buf_info);
- if (ret != dsErrorFree) {
- return GST_FLOW_EOS;
+
+ /* No output available yet */
+ if (dst_buf_info.iBufferStatus != 1) {
+ GST_LOG_OBJECT (decoder, "No buffer decoded yet");
+ return GST_FLOW_OK;
}
}
- /* No output available yet */
- if (dst_buf_info.iBufferStatus != 1) {
- if (at_eos)
- return GST_FLOW_EOS;
-
- gst_video_codec_frame_unref (frame);
- return GST_FLOW_OK;
- }
+ GST_LOG_OBJECT (decoder, "Got back frame with frame ref %" G_GUINT64_FORMAT,
+ (guint64) dst_buf_info.uiOutYuvTimeStamp);
- /* FIXME: openh264 has no way for us to get a connection
- * between the input and output frames, we just have to
- * guess based on the input. Fortunately openh264 can
- * only do baseline profile. */
- frame = gst_video_decoder_get_oldest_frame (decoder);
+ /* OpenH264 lets us pass an int reference through
+ * so we can retrieve the input frame now */
+ frame = gst_video_decoder_get_frame (decoder, dst_buf_info.uiOutYuvTimeStamp);
if (!frame) {
- /* Can only happen in finish() */
- return GST_FLOW_EOS;
- }
-
- {
- GstClockTime pts = dst_buf_info.uiOutYuvTimeStamp;
- if (pts != frame->pts) {
- GST_DEBUG_OBJECT (decoder, "Got output PTS %" GST_TIME_FORMAT
- " but expected %" GST_TIME_FORMAT, GST_TIME_ARGS (pts),
- GST_TIME_ARGS (frame->pts));
- }
+ /* Where did our frame go? This is a reference tracking error. */
+ GST_WARNING_OBJECT (decoder,
+ "Failed to look up frame ref %" G_GUINT64_FORMAT,
+ (guint64) dst_buf_info.uiOutYuvTimeStamp);
+ return GST_FLOW_OK;
}
actual_width = dst_buf_info.UsrData.sSystemBuffer.iWidth;