mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-22 23:28:16 +00:00
x264enc: implement force-key-unit API
This commit is contained in:
parent
1651493ae0
commit
4322fd0752
2 changed files with 146 additions and 51 deletions
|
@ -97,6 +97,7 @@
|
|||
#include "gstx264enc.h"
|
||||
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#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 <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue