videoencoder: Handle all matching force-keyunit events at once

Previously we only handled one event at a time, which could lead to the
following two suboptimal situations:
- frame 0 at 20ms, frame 1 at 40ms and two force-keyunit events at 10ms
  and 15ms. We would create a new keyframe for both of the frames.
- 100 force-keyunit events with running-time NONE would cause all
  following 100 frames to be made into a keyframe.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/684>
This commit is contained in:
Sebastian Dröge 2020-06-03 20:17:06 +03:00 committed by GStreamer Merge Bot
parent 9b1f1f431a
commit 401d56a6a7

View file

@ -1527,43 +1527,55 @@ gst_video_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
GST_OBJECT_LOCK (encoder); GST_OBJECT_LOCK (encoder);
if (priv->force_key_unit.head) { if (priv->force_key_unit.head) {
ForcedKeyUnitEvent *fevt = NULL;
GstClockTime running_time; GstClockTime running_time;
GList *l; GList *l;
GQueue matching_fevt = G_QUEUE_INIT;
running_time = running_time =
gst_segment_to_running_time (&encoder->output_segment, GST_FORMAT_TIME, gst_segment_to_running_time (&encoder->output_segment, GST_FORMAT_TIME,
cstart); cstart);
for (l = priv->force_key_unit.head; l; l = l->next) { for (l = priv->force_key_unit.head; l; l = l->next) {
ForcedKeyUnitEvent *tmp = l->data; ForcedKeyUnitEvent *fevt = l->data;
/* Skip pending keyunits */ /* Skip pending keyunits */
if (tmp->pending) if (fevt->pending)
continue; continue;
/* Simple case, keyunit ASAP */ /* Simple case, keyunit ASAP */
if (tmp->running_time == GST_CLOCK_TIME_NONE) { if (fevt->running_time == GST_CLOCK_TIME_NONE) {
fevt = tmp; g_queue_push_tail (&matching_fevt, fevt);
break; continue;
} }
/* Event for before this frame */ /* Event for before this frame */
if (tmp->running_time <= running_time) { if (fevt->running_time <= running_time) {
fevt = tmp; g_queue_push_tail (&matching_fevt, fevt);
break; continue;
} }
/* Otherwise all following events are in the future */
break;
} }
if (fevt) { if (matching_fevt.length > 0) {
fevt->frame_id = frame->system_frame_number; ForcedKeyUnitEvent *fevt;
gboolean all_headers = FALSE;
GST_DEBUG_OBJECT (encoder, GST_DEBUG_OBJECT (encoder,
"Forcing a key unit at running time %" GST_TIME_FORMAT, "Forcing a key unit at running time %" GST_TIME_FORMAT,
GST_TIME_ARGS (running_time)); GST_TIME_ARGS (running_time));
while ((fevt = g_queue_pop_head (&matching_fevt))) {
fevt->pending = TRUE;
fevt->frame_id = frame->system_frame_number;
if (fevt->all_headers)
all_headers = TRUE;
}
GST_VIDEO_CODEC_FRAME_SET_FORCE_KEYFRAME (frame); GST_VIDEO_CODEC_FRAME_SET_FORCE_KEYFRAME (frame);
if (fevt->all_headers) if (all_headers)
GST_VIDEO_CODEC_FRAME_SET_FORCE_KEYFRAME_HEADERS (frame); GST_VIDEO_CODEC_FRAME_SET_FORCE_KEYFRAME_HEADERS (frame);
fevt->pending = TRUE;
} }
} }
GST_OBJECT_UNLOCK (encoder); GST_OBJECT_UNLOCK (encoder);
@ -2267,48 +2279,58 @@ gst_video_encoder_send_key_unit_unlocked (GstVideoEncoder * encoder,
GstVideoEncoderPrivate *priv = encoder->priv; GstVideoEncoderPrivate *priv = encoder->priv;
GstClockTime stream_time, running_time; GstClockTime stream_time, running_time;
GstEvent *ev; GstEvent *ev;
ForcedKeyUnitEvent *fevt = NULL; GList *l;
GList *l, *fevt_link = NULL; GQueue matching_fevt = G_QUEUE_INIT;
ForcedKeyUnitEvent *fevt;
running_time = running_time =
gst_segment_to_running_time (&encoder->output_segment, GST_FORMAT_TIME, gst_segment_to_running_time (&encoder->output_segment, GST_FORMAT_TIME,
frame->pts); frame->pts);
GST_OBJECT_LOCK (encoder); GST_OBJECT_LOCK (encoder);
for (l = priv->force_key_unit.head; l; l = l->next) { for (l = priv->force_key_unit.head; l;) {
ForcedKeyUnitEvent *tmp = l->data; fevt = l->data;
/* Skip non-pending keyunits */ /* Skip non-pending keyunits */
if (!tmp->pending) if (!fevt->pending) {
l = l->next;
continue; continue;
}
/* Exact match using the frame id */ /* Exact match using the frame id */
if (frame->system_frame_number == tmp->frame_id) { if (frame->system_frame_number == fevt->frame_id) {
fevt_link = l; GList *next = l->next;
fevt = tmp; g_queue_push_tail (&matching_fevt, fevt);
break; g_queue_delete_link (&priv->force_key_unit, l);
l = next;
continue;
} }
/* Simple case, keyunit ASAP */ /* Simple case, keyunit ASAP */
if (tmp->running_time == GST_CLOCK_TIME_NONE) { if (fevt->running_time == GST_CLOCK_TIME_NONE) {
fevt_link = l; GList *next = l->next;
fevt = tmp; g_queue_push_tail (&matching_fevt, fevt);
break; g_queue_delete_link (&priv->force_key_unit, l);
l = next;
continue;
} }
/* Event for before this frame */ /* Event for before this frame */
if (tmp->running_time <= running_time) { if (fevt->running_time <= running_time) {
fevt_link = l; GList *next = l->next;
fevt = tmp; g_queue_push_tail (&matching_fevt, fevt);
break; g_queue_delete_link (&priv->force_key_unit, l);
l = next;
continue;
} }
/* Otherwise all following events are in the future */
break;
} }
if (fevt_link)
g_queue_delete_link (&priv->force_key_unit, fevt_link);
GST_OBJECT_UNLOCK (encoder); GST_OBJECT_UNLOCK (encoder);
if (fevt) { while ((fevt = g_queue_pop_head (&matching_fevt))) {
stream_time = stream_time =
gst_segment_to_stream_time (&encoder->output_segment, GST_FORMAT_TIME, gst_segment_to_stream_time (&encoder->output_segment, GST_FORMAT_TIME,
frame->pts); frame->pts);