From 449ac54348e8816a927b56a285e3b277272226ac Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Thu, 9 Jan 2014 18:10:35 +0100 Subject: [PATCH] vaapiencode: fix negotiation process of output caps. The specified caps in gst_video_encoder_set_output_state() function arguments should not contain any resolution, pixel-aspect-ratio, framerate, codec-data et al. Those rather should be set through the returned GstVideoCodecState. This means that output caps creation could be delayed until before gst_video_encoder_finish_frame() is called. This greatly simplifies the GstVideoEncoder::set_format() callback by the way. --- gst/vaapi/gstvaapiencode.c | 160 +++++++++++++++++---------------------- gst/vaapi/gstvaapiencode.h | 6 +- gst/vaapi/gstvaapiencode_h264.c | 50 ++++++++++-- gst/vaapi/gstvaapiencode_h264.h | 2 + gst/vaapi/gstvaapiencode_mpeg2.c | 19 ++++- 5 files changed, 135 insertions(+), 102 deletions(-) diff --git a/gst/vaapi/gstvaapiencode.c b/gst/vaapi/gstvaapiencode.c index f5b0606..fdb18ce 100644 --- a/gst/vaapi/gstvaapiencode.c +++ b/gst/vaapi/gstvaapiencode.c @@ -36,7 +36,6 @@ #define GST_VAAPI_ENCODE_FLOW_TIMEOUT GST_FLOW_CUSTOM_SUCCESS #define GST_VAAPI_ENCODE_FLOW_MEM_ERROR GST_FLOW_CUSTOM_ERROR #define GST_VAAPI_ENCODE_FLOW_CONVERT_ERROR GST_FLOW_CUSTOM_ERROR_1 -#define GST_VAAPI_ENCODE_FLOW_CODEC_DATA_ERROR GST_FLOW_CUSTOM_ERROR_2 GST_DEBUG_CATEGORY_STATIC (gst_vaapiencode_debug); #define GST_CAT_DEFAULT gst_vaapiencode_debug @@ -220,6 +219,40 @@ error_copy_buffer: } } +static gboolean +ensure_output_state (GstVaapiEncode * encode) +{ + GstVideoEncoder *const venc = GST_VIDEO_ENCODER_CAST (encode); + GstVaapiEncodeClass *const klass = GST_VAAPIENCODE_GET_CLASS (encode); + GstVaapiEncoderStatus status; + GstCaps *out_caps; + + if (!encode->input_state_changed) + return TRUE; + + out_caps = klass->get_caps (encode); + if (!out_caps) + return FALSE; + + if (encode->output_state) + gst_video_codec_state_unref (encode->output_state); + encode->output_state = gst_video_encoder_set_output_state (venc, out_caps, + encode->input_state); + + status = gst_vaapi_encoder_get_codec_data (encode->encoder, + &encode->output_state->codec_data); + if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS) + return FALSE; + +#if GST_CHECK_VERSION(1,0,0) + if (!gst_video_encoder_negotiate (venc)) + return FALSE; +#endif + + encode->input_state_changed = FALSE; + return TRUE; +} + static GstFlowReturn gst_vaapiencode_push_frame (GstVaapiEncode * encode, gint64 timeout) { @@ -244,6 +277,12 @@ gst_vaapiencode_push_frame (GstVaapiEncode * encode, gint64 timeout) gst_video_codec_frame_ref (out_frame); gst_video_codec_frame_set_user_data (out_frame, NULL, NULL); + /* Update output state */ + GST_VIDEO_ENCODER_STREAM_LOCK (encode); + if (!ensure_output_state (encode)) + goto error_output_state; + GST_VIDEO_ENCODER_STREAM_UNLOCK (encode); + /* Allocate and copy buffer into system memory */ out_buffer = NULL; ret = klass->alloc_buffer (encode, @@ -255,35 +294,6 @@ gst_vaapiencode_push_frame (GstVaapiEncode * encode, gint64 timeout) gst_buffer_replace (&out_frame->output_buffer, out_buffer); gst_buffer_unref (out_buffer); - /* Check output caps */ - GST_VIDEO_ENCODER_STREAM_LOCK (encode); - if (!encode->out_caps_done) { - GstVideoCodecState *old_state, *new_state; - GstBuffer *codec_data; - - status = gst_vaapi_encoder_get_codec_data (encode->encoder, &codec_data); - if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS) - goto error_codec_data; - - if (codec_data) { - encode->srcpad_caps = gst_caps_make_writable (encode->srcpad_caps); - gst_caps_set_simple (encode->srcpad_caps, - "codec_data", GST_TYPE_BUFFER, codec_data, NULL); - gst_buffer_unref (codec_data); - old_state = - gst_video_encoder_get_output_state (GST_VIDEO_ENCODER_CAST (encode)); - new_state = - gst_video_encoder_set_output_state (GST_VIDEO_ENCODER_CAST (encode), - gst_caps_ref (encode->srcpad_caps), old_state); - gst_video_codec_state_unref (old_state); - gst_video_codec_state_unref (new_state); - GST_DEBUG ("updated srcpad caps to: %" GST_PTR_FORMAT, - encode->srcpad_caps); - } - encode->out_caps_done = TRUE; - } - GST_VIDEO_ENCODER_STREAM_UNLOCK (encode); - GST_DEBUG ("output:%" GST_TIME_FORMAT ", size:%zu", GST_TIME_ARGS (out_frame->pts), gst_buffer_get_size (out_buffer)); @@ -305,12 +315,12 @@ error_allocate_buffer: gst_video_codec_frame_unref (out_frame); return ret; } -error_codec_data: +error_output_state: { - GST_ERROR ("failed to construct codec-data (status %d)", status); + GST_ERROR ("failed to negotiate output state", status); GST_VIDEO_ENCODER_STREAM_UNLOCK (encode); gst_video_codec_frame_unref (out_frame); - return GST_VAAPI_ENCODE_FLOW_CODEC_DATA_ERROR; + return GST_FLOW_NOT_NEGOTIATED; } } @@ -374,6 +384,15 @@ gst_vaapiencode_get_caps (GstVideoEncoder * venc, GstCaps * filter) static gboolean gst_vaapiencode_destroy (GstVaapiEncode * encode) { + if (encode->input_state) { + gst_video_codec_state_unref (encode->input_state); + encode->input_state = NULL; + } + + if (encode->output_state) { + gst_video_codec_state_unref (encode->output_state); + encode->output_state = NULL; + } gst_vaapi_encoder_replace (&encode->encoder, NULL); gst_caps_replace (&encode->sinkpad_caps, NULL); gst_caps_replace (&encode->srcpad_caps, NULL); @@ -446,19 +465,12 @@ gst_vaapiencode_update_sink_caps (GstVaapiEncode * encode, } static gboolean -gst_vaapiencode_update_src_caps (GstVaapiEncode * encode, - GstVideoCodecState * in_state) +set_codec_state (GstVaapiEncode * encode, GstVideoCodecState * state) { - GstVideoCodecState *out_state; - GstStructure *structure; - GstCaps *outcaps, *allowed_caps, *template_caps, *intersect; - GstVaapiEncoderStatus status; - GstBuffer *codec_data = NULL; + GstCaps *out_caps, *allowed_caps, *template_caps, *intersect; g_return_val_if_fail (encode->encoder, FALSE); - encode->out_caps_done = FALSE; - /* get peer caps for stream-format avc/bytestream, codec_data */ template_caps = gst_pad_get_pad_template_caps (encode->srcpad); allowed_caps = gst_pad_get_allowed_caps (encode->srcpad); @@ -467,38 +479,19 @@ gst_vaapiencode_update_src_caps (GstVaapiEncode * encode, gst_caps_unref (allowed_caps); /* codec data was not set */ - outcaps = gst_vaapi_encoder_set_format (encode->encoder, in_state, intersect); + out_caps = gst_vaapi_encoder_set_format (encode->encoder, state, intersect); gst_caps_unref (intersect); - g_return_val_if_fail (outcaps, FALSE); + g_return_val_if_fail (out_caps, FALSE); - if (!gst_caps_is_fixed (outcaps)) { + if (!gst_caps_is_fixed (out_caps)) { GST_ERROR ("encoder output caps was not fixed"); - gst_caps_unref (outcaps); + gst_caps_unref (out_caps); return FALSE; } - structure = gst_caps_get_structure (outcaps, 0); - if (!gst_structure_has_field (structure, "codec_data")) { - status = gst_vaapi_encoder_get_codec_data (encode->encoder, &codec_data); - if (status == GST_VAAPI_ENCODER_STATUS_SUCCESS) { - if (codec_data) { - outcaps = gst_caps_make_writable (outcaps); - gst_caps_set_simple (outcaps, - "codec_data", GST_TYPE_BUFFER, codec_data, NULL); - gst_buffer_replace (&codec_data, NULL); - } - encode->out_caps_done = TRUE; - } - } else - encode->out_caps_done = TRUE; - - out_state = - gst_video_encoder_set_output_state (GST_VIDEO_ENCODER_CAST (encode), - outcaps, in_state); - gst_caps_replace (&encode->srcpad_caps, out_state->caps); - gst_video_codec_state_unref (out_state); - - GST_DEBUG ("set srcpad caps to: %" GST_PTR_FORMAT, encode->srcpad_caps); + GST_DEBUG ("set srcpad caps to: %" GST_PTR_FORMAT, out_caps); + gst_caps_replace (&encode->srcpad_caps, out_caps); + gst_caps_unref (out_caps); return TRUE; } @@ -511,39 +504,25 @@ gst_vaapiencode_set_format (GstVideoEncoder * venc, GstVideoCodecState * state) if (!ensure_encoder (encode)) return FALSE; - if (!gst_vaapiencode_update_sink_caps (encode, state)) + if (!set_codec_state (encode, state)) return FALSE; - if (!gst_vaapiencode_update_src_caps (encode, state)) + + if (!gst_vaapiencode_update_sink_caps (encode, state)) return FALSE; if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (encode), - encode->sinkpad_caps, encode->srcpad_caps)) + state->caps, NULL)) return FALSE; -#if GST_CHECK_VERSION(1,0,0) - if (encode->out_caps_done && !gst_video_encoder_negotiate (venc)) { - GST_ERROR ("failed to negotiate with caps %" GST_PTR_FORMAT, - encode->srcpad_caps); - return FALSE; - } -#endif + if (encode->input_state) + gst_video_codec_state_unref (encode->input_state); + encode->input_state = gst_video_codec_state_ref (state); + encode->input_state_changed = TRUE; return gst_pad_start_task (encode->srcpad, (GstTaskFunction) gst_vaapiencode_buffer_loop, encode, NULL); } -static gboolean -gst_vaapiencode_reset (GstVideoEncoder * venc, gboolean hard) -{ - GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc); - - GST_DEBUG ("vaapiencode starting reset"); - - /* FIXME: compare sink_caps with encoder */ - encode->out_caps_done = FALSE; - return TRUE; -} - static GstFlowReturn gst_vaapiencode_handle_frame (GstVideoEncoder * venc, GstVideoCodecFrame * frame) @@ -700,7 +679,6 @@ gst_vaapiencode_class_init (GstVaapiEncodeClass * klass) venc_class->open = GST_DEBUG_FUNCPTR (gst_vaapiencode_open); venc_class->close = GST_DEBUG_FUNCPTR (gst_vaapiencode_close); venc_class->set_format = GST_DEBUG_FUNCPTR (gst_vaapiencode_set_format); - venc_class->reset = GST_DEBUG_FUNCPTR (gst_vaapiencode_reset); venc_class->handle_frame = GST_DEBUG_FUNCPTR (gst_vaapiencode_handle_frame); venc_class->finish = GST_DEBUG_FUNCPTR (gst_vaapiencode_finish); venc_class->getcaps = GST_DEBUG_FUNCPTR (gst_vaapiencode_get_caps); diff --git a/gst/vaapi/gstvaapiencode.h b/gst/vaapi/gstvaapiencode.h index 91712af..7ceffa3 100644 --- a/gst/vaapi/gstvaapiencode.h +++ b/gst/vaapi/gstvaapiencode.h @@ -59,9 +59,10 @@ struct _GstVaapiEncode GstPadQueryFunction srcpad_query; GstVaapiEncoder *encoder; + GstVideoCodecState *input_state; + gboolean input_state_changed; + GstVideoCodecState *output_state; GPtrArray *prop_values; - - guint32 out_caps_done:1; }; struct _GstVaapiEncodeClass @@ -75,6 +76,7 @@ struct _GstVaapiEncodeClass gboolean (*set_property) (GstVaapiEncode * encode, guint prop_id, const GValue * value); + GstCaps * (*get_caps) (GstVaapiEncode * encode); GstVaapiEncoder * (*alloc_encoder) (GstVaapiEncode * encode, GstVaapiDisplay * display); GstFlowReturn (*alloc_buffer) (GstVaapiEncode * encode, diff --git a/gst/vaapi/gstvaapiencode_h264.c b/gst/vaapi/gstvaapiencode_h264.c index 0d222b5..f64db7f 100644 --- a/gst/vaapi/gstvaapiencode_h264.c +++ b/gst/vaapi/gstvaapiencode_h264.c @@ -34,7 +34,9 @@ GST_DEBUG_CATEGORY_STATIC (gst_vaapi_h264_encode_debug); #define GST_CAT_DEFAULT gst_vaapi_h264_encode_debug -#define GST_CAPS_CODEC(CODEC) CODEC "; " +#define GST_CODEC_CAPS \ + "video/x-h264, " \ + "alignment = (string) au" /* *INDENT-OFF* */ static const char gst_vaapiencode_h264_sink_caps_str[] = @@ -57,7 +59,7 @@ static const char gst_vaapiencode_h264_sink_caps_str[] = /* *INDENT-OFF* */ static const char gst_vaapiencode_h264_src_caps_str[] = - GST_CAPS_CODEC ("video/x-h264"); + GST_CODEC_CAPS; /* *INDENT-ON* */ /* *INDENT-OFF* */ @@ -121,6 +123,39 @@ gst_vaapiencode_h264_get_property (GObject * object, } } +static GstCaps * +gst_vaapiencode_h264_get_caps (GstVaapiEncode * base_encode) +{ + GstVaapiEncodeH264 *const encode = GST_VAAPIENCODE_H264_CAST (base_encode); + GstCaps *caps, *allowed_caps; + + caps = gst_caps_from_string (GST_CODEC_CAPS); + + /* Check whether "stream-format" is avcC mode */ + allowed_caps = gst_pad_get_allowed_caps (base_encode->srcpad); + if (allowed_caps) { + const char *stream_format = NULL; + GstStructure *structure; + guint i, num_structures; + + num_structures = gst_caps_get_size (allowed_caps); + for (i = 0; !stream_format && i < num_structures; i++) { + structure = gst_caps_get_structure (allowed_caps, i); + if (!gst_structure_has_field_typed (structure, "stream-format", + G_TYPE_STRING)) + continue; + stream_format = gst_structure_get_string (structure, "stream-format"); + } + encode->is_avc = stream_format && strcmp (stream_format, "avc") == 0; + gst_caps_unref (allowed_caps); + } + gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING, + encode->is_avc ? "avc" : "byte-stream", NULL); + + /* XXX: update profile and level information */ + return caps; +} + static GstVaapiEncoder * gst_vaapiencode_h264_alloc_encoder (GstVaapiEncode * base, GstVaapiDisplay * display) @@ -221,21 +256,23 @@ error: } static GstFlowReturn -gst_vaapiencode_h264_alloc_buffer (GstVaapiEncode * encode, +gst_vaapiencode_h264_alloc_buffer (GstVaapiEncode * base_encode, GstVaapiCodedBuffer * coded_buf, GstBuffer ** out_buffer_ptr) { - GstVaapiEncoderH264 *const encoder = (GstVaapiEncoderH264 *) encode->encoder; + GstVaapiEncodeH264 *const encode = GST_VAAPIENCODE_H264_CAST (base_encode); + GstVaapiEncoderH264 *const encoder = (GstVaapiEncoderH264 *) + base_encode->encoder; GstFlowReturn ret; g_return_val_if_fail (encoder != NULL, GST_FLOW_ERROR); ret = GST_VAAPIENCODE_CLASS (gst_vaapiencode_h264_parent_class)->alloc_buffer - (encode, coded_buf, out_buffer_ptr); + (base_encode, coded_buf, out_buffer_ptr); if (ret != GST_FLOW_OK) return ret; - if (!gst_vaapi_encoder_h264_is_avc (encoder)) + if (!encode->is_avc) return GST_FLOW_OK; /* Convert to avcC format */ @@ -267,6 +304,7 @@ gst_vaapiencode_h264_class_init (GstVaapiEncodeH264Class * klass) object_class->get_property = gst_vaapiencode_h264_get_property; encode_class->get_properties = gst_vaapi_encoder_h264_get_default_properties; + encode_class->get_caps = gst_vaapiencode_h264_get_caps; encode_class->alloc_encoder = gst_vaapiencode_h264_alloc_encoder; encode_class->alloc_buffer = gst_vaapiencode_h264_alloc_buffer; diff --git a/gst/vaapi/gstvaapiencode_h264.h b/gst/vaapi/gstvaapiencode_h264.h index bf440bf..d30c065 100644 --- a/gst/vaapi/gstvaapiencode_h264.h +++ b/gst/vaapi/gstvaapiencode_h264.h @@ -52,6 +52,8 @@ struct _GstVaapiEncodeH264 { /*< private >*/ GstVaapiEncode parent_instance; + + guint is_avc:1; /* [FALSE]=byte-stream (default); [TRUE]=avcC */ }; struct _GstVaapiEncodeH264Class diff --git a/gst/vaapi/gstvaapiencode_mpeg2.c b/gst/vaapi/gstvaapiencode_mpeg2.c index 7579b2a..b84821c 100644 --- a/gst/vaapi/gstvaapiencode_mpeg2.c +++ b/gst/vaapi/gstvaapiencode_mpeg2.c @@ -34,7 +34,9 @@ GST_DEBUG_CATEGORY_STATIC (gst_vaapi_mpeg2_encode_debug); #define GST_CAT_DEFAULT gst_vaapi_mpeg2_encode_debug -#define GST_CAPS_CODEC(CODEC) CODEC "; " +#define GST_CODEC_CAPS \ + "video/mpeg, mpegversion = (int) 2, " \ + "systemstream = (boolean) false" /* *INDENT-OFF* */ static const char gst_vaapiencode_mpeg2_sink_caps_str[] = @@ -57,8 +59,7 @@ static const char gst_vaapiencode_mpeg2_sink_caps_str[] = /* *INDENT-OFF* */ static const char gst_vaapiencode_mpeg2_src_caps_str[] = - GST_CAPS_CODEC ("video/mpeg, mpegversion = (int) 2, " - "systemstream = (boolean) false"); + GST_CODEC_CAPS; /* *INDENT-ON* */ /* *INDENT-OFF* */ @@ -123,6 +124,17 @@ gst_vaapiencode_mpeg2_get_property (GObject * object, } } +static GstCaps * +gst_vaapiencode_mpeg2_get_caps (GstVaapiEncode * base_encode) +{ + GstCaps *caps; + + caps = gst_caps_from_string (GST_CODEC_CAPS); + + /* XXX: update profile and level information */ + return caps; +} + static GstVaapiEncoder * gst_vaapiencode_mpeg2_alloc_encoder (GstVaapiEncode * base, GstVaapiDisplay * display) @@ -145,6 +157,7 @@ gst_vaapiencode_mpeg2_class_init (GstVaapiEncodeMpeg2Class * klass) object_class->get_property = gst_vaapiencode_mpeg2_get_property; encode_class->get_properties = gst_vaapi_encoder_mpeg2_get_default_properties; + encode_class->get_caps = gst_vaapiencode_mpeg2_get_caps; encode_class->alloc_encoder = gst_vaapiencode_mpeg2_alloc_encoder; gst_element_class_set_static_metadata (element_class, -- 2.7.4