rtph265depay: Add request-keyframe property

Similar to the H264 depayloader, this property will request a new keyframe
when packet loss is detected.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8343>
This commit is contained in:
Julian Bouzas 2025-01-22 12:44:51 -05:00 committed by GStreamer Marge Bot
parent ca08f4220a
commit 4400caeaef
2 changed files with 56 additions and 7 deletions

View File

@ -40,11 +40,13 @@ GST_DEBUG_CATEGORY_STATIC (rtph265depay_debug);
#define DEFAULT_STREAM_FORMAT GST_H265_STREAM_FORMAT_BYTESTREAM
#define DEFAULT_ACCESS_UNIT FALSE
#define DEFAULT_WAIT_FOR_KEYFRAME FALSE
#define DEFAULT_REQUEST_KEYFRAME FALSE
enum
{
PROP_0,
PROP_WAIT_FOR_KEYFRAME
PROP_WAIT_FOR_KEYFRAME,
PROP_REQUEST_KEYFRAME,
};
@ -137,6 +139,9 @@ gst_rtp_h265_depay_set_property (GObject * object, guint prop_id,
case PROP_WAIT_FOR_KEYFRAME:
self->wait_for_keyframe = g_value_get_boolean (value);
break;
case PROP_REQUEST_KEYFRAME:
self->request_keyframe = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -153,6 +158,9 @@ gst_rtp_h265_depay_get_property (GObject * object, guint prop_id,
case PROP_WAIT_FOR_KEYFRAME:
g_value_set_boolean (value, self->wait_for_keyframe);
break;
case PROP_REQUEST_KEYFRAME:
g_value_set_boolean (value, self->request_keyframe);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -191,6 +199,19 @@ gst_rtp_h265_depay_class_init (GstRtpH265DepayClass * klass)
DEFAULT_WAIT_FOR_KEYFRAME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpH265Depay:request-keyframe:
*
* Request new keyframe when packet loss is detected.
*
* Since: 1.26
*/
g_object_class_install_property (gobject_class, PROP_REQUEST_KEYFRAME,
g_param_spec_boolean ("request-keyframe", "Request Keyframe",
"Request new keyframe when packet loss is detected",
DEFAULT_REQUEST_KEYFRAME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_static_pad_template (gstelement_class,
&gst_rtp_h265_depay_src_template);
gst_element_class_add_static_pad_template (gstelement_class,
@ -227,6 +248,7 @@ gst_rtp_h265_depay_init (GstRtpH265Depay * rtph265depay)
rtph265depay->pps = g_ptr_array_new_with_free_func (
(GDestroyNotify) gst_buffer_unref);
rtph265depay->wait_for_keyframe = DEFAULT_WAIT_FOR_KEYFRAME;
rtph265depay->request_keyframe = DEFAULT_REQUEST_KEYFRAME;
}
static void
@ -235,6 +257,7 @@ gst_rtp_h265_depay_reset (GstRtpH265Depay * rtph265depay, gboolean hard)
gst_adapter_clear (rtph265depay->adapter);
rtph265depay->wait_start = TRUE;
rtph265depay->waiting_for_keyframe = rtph265depay->wait_for_keyframe;
rtph265depay->requesting_keyframe = FALSE;
gst_adapter_clear (rtph265depay->picture_adapter);
rtph265depay->picture_start = FALSE;
rtph265depay->last_keyframe = FALSE;
@ -1256,8 +1279,10 @@ gst_rtp_h265_depay_handle_nal (GstRtpH265Depay * rtph265depay, GstBuffer * nal,
/* add to adapter */
gst_buffer_unmap (nal, &map);
if (!rtph265depay->picture_start && start && out_keyframe)
if (!rtph265depay->picture_start && start && out_keyframe) {
rtph265depay->waiting_for_keyframe = FALSE;
rtph265depay->requesting_keyframe = FALSE;
}
GST_DEBUG_OBJECT (depayload, "adding NAL to picture adapter");
gst_adapter_push (rtph265depay->picture_adapter, nal);
@ -1276,7 +1301,17 @@ gst_rtp_h265_depay_handle_nal (GstRtpH265Depay * rtph265depay, GstBuffer * nal,
}
if (outbuf) {
if (!rtph265depay->waiting_for_keyframe) {
/* Request a new keyframe if we are waiting for one */
if (rtph265depay->waiting_for_keyframe &&
!rtph265depay->requesting_keyframe && rtph265depay->request_keyframe) {
rtph265depay->requesting_keyframe = TRUE;
GST_INFO_OBJECT (depayload, "Requesting keyframe while waiting for one");
gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE,
TRUE, 0));
}
if (!rtph265depay->waiting_for_keyframe || !rtph265depay->wait_for_keyframe) {
GST_INFO_OBJECT (depayload, "Pushing frame %" GST_PTR_FORMAT, outbuf);
gst_rtp_h265_depay_push (rtph265depay, outbuf, out_keyframe,
out_timestamp, marker);
@ -1333,24 +1368,27 @@ gst_rtp_h265_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
{
GstRtpH265Depay *rtph265depay;
GstBuffer *outbuf = NULL;
gboolean is_discont;
guint8 nal_unit_type;
rtph265depay = GST_RTP_H265_DEPAY (depayload);
if (!rtph265depay->merge)
if (!rtph265depay->merge) {
rtph265depay->waiting_for_keyframe = FALSE;
rtph265depay->requesting_keyframe = FALSE;
}
/* flush remaining data on discont */
if (GST_BUFFER_IS_DISCONT (rtp->buffer)) {
is_discont = GST_BUFFER_IS_DISCONT (rtp->buffer);
if (is_discont) {
gst_adapter_clear (rtph265depay->adapter);
rtph265depay->wait_start = TRUE;
rtph265depay->current_fu_type = 0;
rtph265depay->last_fu_seqnum = 0;
if (rtph265depay->merge && rtph265depay->wait_for_keyframe) {
if (rtph265depay->merge)
rtph265depay->waiting_for_keyframe = TRUE;
}
}
{
gint payload_len;
@ -1589,6 +1627,16 @@ gst_rtp_h265_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
/* and assemble in the adapter */
gst_adapter_push (rtph265depay->adapter, outbuf);
} else {
/* If packet is discont and is not the first one of a FU, then we
* can tell it is not part of a keyframe, so request a new one */
if (is_discont && rtph265depay->request_keyframe) {
GST_INFO_OBJECT (depayload,
"discont FU received without start bit. Requesting keyframe.");
gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
gst_video_event_new_upstream_force_key_unit
(GST_CLOCK_TIME_NONE, TRUE, 0));
}
if (rtph265depay->current_fu_type == 0) {
/* previous FU packet missing start bit? */
GST_WARNING_OBJECT (rtph265depay, "missing FU start bit on an "

View File

@ -90,6 +90,7 @@ struct _GstRtpH265Depay
gboolean wait_for_keyframe;
gboolean request_keyframe;
gboolean waiting_for_keyframe;
gboolean requesting_keyframe;
};
struct _GstRtpH265DepayClass