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.
This commit is contained in:
Gwenole Beauchesne 2014-01-09 18:10:35 +01:00
parent 37fa6a8a3d
commit 449ac54348
5 changed files with 135 additions and 102 deletions

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -52,6 +52,8 @@ struct _GstVaapiEncodeH264
{
/*< private >*/
GstVaapiEncode parent_instance;
guint is_avc:1; /* [FALSE]=byte-stream (default); [TRUE]=avcC */
};
struct _GstVaapiEncodeH264Class

View file

@ -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,