diff --git a/ext/x264/gstx264enc.c b/ext/x264/gstx264enc.c index ea3cf0bf9c..4865bcd84e 100644 --- a/ext/x264/gstx264enc.c +++ b/ext/x264/gstx264enc.c @@ -97,6 +97,7 @@ #include "gstx264enc.h" #include +#include #if X264_BUILD >= 71 #define X264_DELAYED_FRAMES_API @@ -127,6 +128,10 @@ #define X264_PRESETS #endif +#if X264_BUILD >= 95 +#define FORCE_INTRA_API +#endif + #include #include @@ -936,6 +941,8 @@ gst_x264_enc_init (GstX264Enc * encoder, GstX264EncClass * klass) encoder->x264param.p_log_private = encoder; encoder->x264param.i_log_level = X264_LOG_DEBUG; + gst_segment_init (&encoder->segment, GST_FORMAT_TIME); + encoder->force_key_unit_event = NULL; gst_x264_enc_reset (encoder); } @@ -946,12 +953,11 @@ gst_x264_enc_reset (GstX264Enc * encoder) encoder->width = 0; encoder->height = 0; encoder->current_byte_stream = GST_X264_ENC_STREAM_FORMAT_FROM_PROPERTY; + gst_segment_init (&encoder->segment, GST_FORMAT_UNDEFINED); GST_OBJECT_LOCK (encoder); - encoder->i_type = X264_TYPE_AUTO; - if (encoder->forcekeyunit_event) - gst_event_unref (encoder->forcekeyunit_event); - encoder->forcekeyunit_event = NULL; + gst_event_replace (&encoder->force_key_unit_event, NULL); + encoder->pending_key_unit_ts = GST_CLOCK_TIME_NONE; GST_OBJECT_UNLOCK (encoder); } @@ -1743,25 +1749,31 @@ gst_x264_enc_src_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CUSTOM_UPSTREAM:{ - const GstStructure *s; - s = gst_event_get_structure (event); - if (gst_structure_has_name (s, "GstForceKeyUnit")) { - /* Set I frame request */ - GST_OBJECT_LOCK (encoder); - encoder->i_type = X264_TYPE_I; - encoder->forcekeyunit_event = gst_event_copy (event); - GST_EVENT_TYPE (encoder->forcekeyunit_event) = - GST_EVENT_CUSTOM_DOWNSTREAM; - GST_OBJECT_UNLOCK (encoder); - forward = FALSE; - gst_event_unref (event); - } + guint count; + gboolean all_headers; + + if (!gst_video_event_is_force_key_unit (event)) + goto out; + + GST_OBJECT_LOCK (encoder); + gst_video_event_parse_upstream_force_key_unit (event, + &encoder->pending_key_unit_ts, &all_headers, &count); + GST_INFO_OBJECT (encoder, "received upstream force-key-unit event, " + "seqnum %d running_time %" GST_TIME_FORMAT " all_headers %d count %d", + gst_event_get_seqnum (event), + GST_TIME_ARGS (encoder->pending_key_unit_ts), all_headers, count); + + gst_event_replace (&encoder->force_key_unit_event, event); + gst_event_unref (event); + GST_OBJECT_UNLOCK (encoder); + forward = FALSE; break; } default: break; } +out: if (forward) ret = gst_pad_push_event (encoder->sinkpad, event); @@ -1778,6 +1790,22 @@ gst_x264_enc_sink_event (GstPad * pad, GstEvent * event) encoder = GST_X264_ENC (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + gdouble rate, applied_rate; + GstFormat format; + gint64 start, stop, position; + + gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, + &format, &start, &stop, &position); + gst_segment_set_newsegment (&encoder->segment, update, rate, format, + start, stop, position); + break; + } + case GST_EVENT_FLUSH_STOP: + gst_segment_init (&encoder->segment, GST_FORMAT_UNDEFINED); + break; case GST_EVENT_EOS: gst_x264_enc_flush_frames (encoder, TRUE); break; @@ -1799,13 +1827,24 @@ gst_x264_enc_sink_event (GstPad * pad, GstEvent * event) * buffers in encoder are considered (in the) past */ } case GST_EVENT_CUSTOM_DOWNSTREAM:{ - const GstStructure *s; - s = gst_event_get_structure (event); - if (gst_structure_has_name (s, "GstForceKeyUnit")) { - GST_OBJECT_LOCK (encoder); - encoder->i_type = X264_TYPE_I; - GST_OBJECT_UNLOCK (encoder); - } + guint count; + gboolean all_headers; + + if (!gst_video_event_is_force_key_unit (event)) + break; + + GST_OBJECT_LOCK (encoder); + + gst_video_event_parse_downstream_force_key_unit (event, NULL, NULL, + &encoder->pending_key_unit_ts, &all_headers, &count); + GST_INFO_OBJECT (encoder, "received downstream force-key-unit event, " + "seqnum %d running_time %" GST_TIME_FORMAT " all_headers %d count %d", + gst_event_get_seqnum (event), + GST_TIME_ARGS (encoder->pending_key_unit_ts), all_headers, count); + + gst_event_replace (&encoder->force_key_unit_event, event); + gst_event_unref (event); + GST_OBJECT_UNLOCK (encoder); break; } default: @@ -1849,13 +1888,7 @@ gst_x264_enc_chain (GstPad * pad, GstBuffer * buf) pic_in.img.i_stride[i] = encoder->stride[i]; } - GST_OBJECT_LOCK (encoder); - pic_in.i_type = encoder->i_type; - - /* Reset encoder forced picture type */ - encoder->i_type = X264_TYPE_AUTO; - GST_OBJECT_UNLOCK (encoder); - + pic_in.i_type = X264_TYPE_AUTO; pic_in.i_pts = GST_BUFFER_TIMESTAMP (buf); ret = gst_x264_enc_encode_frame (encoder, &pic_in, &i_nal, TRUE); @@ -1881,6 +1914,45 @@ wrong_buffer_size: } } +static GstEvent * +check_pending_key_unit_event (GstEvent * pending_event, GstSegment * segment, + GstClockTime timestamp, GstClockTime pending_key_unit_ts) +{ + GstClockTime running_time, stream_time; + gboolean all_headers; + guint count; + GstEvent *event = NULL; + + g_return_val_if_fail (pending_event != NULL, NULL); + g_return_val_if_fail (segment != NULL, NULL); + + if (pending_event == NULL || timestamp == GST_CLOCK_TIME_NONE) + goto out; + + running_time = gst_segment_to_running_time (segment, + GST_FORMAT_TIME, timestamp); + + GST_INFO ("now %" GST_TIME_FORMAT " wanted %" GST_TIME_FORMAT, + GST_TIME_ARGS (running_time), GST_TIME_ARGS (pending_key_unit_ts)); + + if (running_time < pending_key_unit_ts) + goto out; + + stream_time = gst_segment_to_stream_time (segment, + GST_FORMAT_TIME, timestamp); + + gst_video_event_parse_upstream_force_key_unit (pending_event, + NULL, &all_headers, &count); + + event = + gst_video_event_new_downstream_force_key_unit (timestamp, stream_time, + running_time, all_headers, count); + gst_event_set_seqnum (event, gst_event_get_seqnum (pending_event)); + +out: + return event; +} + static GstFlowReturn gst_x264_enc_encode_frame (GstX264Enc * encoder, x264_picture_t * pic_in, int *i_nal, gboolean send) @@ -1897,7 +1969,7 @@ gst_x264_enc_encode_frame (GstX264Enc * encoder, x264_picture_t * pic_in, GstFlowReturn ret; GstClockTime duration; guint8 *data; - GstEvent *forcekeyunit_event = NULL; + GstEvent *event = NULL; if (G_UNLIKELY (encoder->x264enc == NULL)) return GST_FLOW_NOT_NEGOTIATED; @@ -1908,6 +1980,24 @@ gst_x264_enc_encode_frame (GstX264Enc * encoder, x264_picture_t * pic_in, if (x264_encoder_reconfig (encoder->x264enc, &encoder->x264param) < 0) GST_WARNING_OBJECT (encoder, "Could not reconfigure"); } + + if (encoder->pending_key_unit_ts != GST_CLOCK_TIME_NONE && pic_in != NULL) { + event = check_pending_key_unit_event (encoder->force_key_unit_event, + &encoder->segment, pic_in->i_pts, encoder->pending_key_unit_ts); + if (event) { + encoder->pending_key_unit_ts = GST_CLOCK_TIME_NONE; + gst_event_replace (&encoder->force_key_unit_event, NULL); + +#ifdef FORCE_INTRA_API + if (encoder->intra_refresh) + x264_encoder_intra_refresh (encoder->x264enc); + else + pic_in->i_type = X264_TYPE_IDR; +#else + pic_in->i_type = X264_TYPE_IDR; +#endif + } + } GST_OBJECT_UNLOCK (encoder); encoder_return = x264_encoder_encode (encoder->x264enc, @@ -1916,11 +2006,13 @@ gst_x264_enc_encode_frame (GstX264Enc * encoder, x264_picture_t * pic_in, if (encoder_return < 0) { GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode x264 frame failed."), ("x264_encoder_encode return code=%d", encoder_return)); - return GST_FLOW_ERROR; + ret = GST_FLOW_ERROR; + goto out; } if (!*i_nal) { - return GST_FLOW_OK; + ret = GST_FLOW_OK; + goto out; } #ifndef X264_ENC_NALS i_size = 0; @@ -1957,16 +2049,19 @@ gst_x264_enc_encode_frame (GstX264Enc * encoder, x264_picture_t * pic_in, } else { GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, (NULL), ("Timestamp queue empty.")); - return GST_FLOW_ERROR; + ret = GST_FLOW_ERROR; + goto out; } - if (!send) - return GST_FLOW_OK; + if (!send) { + ret = GST_FLOW_OK; + goto out; + } ret = gst_pad_alloc_buffer (encoder->srcpad, GST_BUFFER_OFFSET_NONE, i_size, GST_PAD_CAPS (encoder->srcpad), &out_buf); if (ret != GST_FLOW_OK) - return ret; + goto out; memcpy (GST_BUFFER_DATA (out_buf), data, i_size); GST_BUFFER_SIZE (out_buf) = i_size; @@ -1989,17 +2084,16 @@ gst_x264_enc_encode_frame (GstX264Enc * encoder, x264_picture_t * pic_in, GST_BUFFER_FLAG_SET (out_buf, GST_BUFFER_FLAG_DELTA_UNIT); } - GST_OBJECT_LOCK (encoder); - forcekeyunit_event = encoder->forcekeyunit_event; - encoder->forcekeyunit_event = NULL; - GST_OBJECT_UNLOCK (encoder); - if (forcekeyunit_event) { - gst_structure_set (forcekeyunit_event->structure, - "timestamp", G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (out_buf), NULL); - gst_pad_push_event (encoder->srcpad, forcekeyunit_event); - } + if (event) + gst_pad_push_event (encoder->srcpad, gst_event_ref (event)); - return gst_pad_push (encoder->srcpad, out_buf); + ret = gst_pad_push (encoder->srcpad, out_buf); + +out: + if (event) + gst_event_unref (event); + + return ret; } static void diff --git a/ext/x264/gstx264enc.h b/ext/x264/gstx264enc.h index a2f1145b97..4686465f49 100644 --- a/ext/x264/gstx264enc.h +++ b/ext/x264/gstx264enc.h @@ -49,6 +49,7 @@ struct _GstX264Enc /*< private >*/ GstPad *sinkpad; GstPad *srcpad; + GstSegment segment; x264_t *x264enc; x264_param_t x264param; @@ -111,9 +112,6 @@ struct _GstX264Enc guint8 *buffer; gulong buffer_size; - gint i_type; - GstEvent *forcekeyunit_event; - /* configuration changed while playing */ gboolean reconfig; @@ -121,6 +119,9 @@ struct _GstX264Enc const gchar *peer_profile; gboolean peer_intra_profile; const x264_level_t *peer_level; + + GstClockTime pending_key_unit_ts; + GstEvent *force_key_unit_event; }; struct _GstX264EncClass