gstreamer/gst/vaapi/gstvaapiencode.c
Víctor Manuel Jáquez Leal 5ccadd6e9c vaapiencode: add get_profile() vmethod
This new virtual method, get_profile(), if implemented by specific
encoders, will return the VA profile potentially determined by the
source caps.

Also it is implemented by h264 and h265 encoders, which are the main
users of this vmethod.

https://bugzilla.gnome.org/show_bug.cgi?id=771291
2017-04-06 22:00:05 +02:00

900 lines
25 KiB
C

/*
* gstvaapiencode.c - VA-API video encoder
*
* Copyright (C) 2013-2014 Intel Corporation
* Author: Wind Yuan <feng.yuan@intel.com>
* Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include "gstcompat.h"
#include <gst/vaapi/gstvaapivalue.h>
#include <gst/vaapi/gstvaapidisplay.h>
#include "gstvaapiencode.h"
#include "gstvaapipluginutil.h"
#include "gstvaapivideometa.h"
#include "gstvaapivideomemory.h"
#include "gstvaapivideobufferpool.h"
#define GST_PLUGIN_NAME "vaapiencode"
#define GST_PLUGIN_DESC "A VA-API based video encoder"
#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
GST_DEBUG_CATEGORY_STATIC (gst_vaapiencode_debug);
#ifndef GST_DISABLE_GST_DEBUG
#define GST_CAT_DEFAULT gst_vaapiencode_debug
#else
#define GST_CAT_DEFAULT NULL
#endif
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstVaapiEncode,
gst_vaapiencode, GST_TYPE_VIDEO_ENCODER,
GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES);
GST_VAAPI_PLUGIN_BASE_DEFINE_SET_CONTEXT (gst_vaapiencode_parent_class);
enum
{
PROP_0,
PROP_BASE,
};
static inline gboolean
ensure_display (GstVaapiEncode * encode)
{
return gst_vaapi_plugin_base_ensure_display (GST_VAAPI_PLUGIN_BASE (encode));
}
static gboolean
gst_vaapiencode_sink_query (GstVideoEncoder * encoder, GstQuery * query)
{
gboolean ret = TRUE;
GstElement *const element = GST_ELEMENT (encoder);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONTEXT:
ret = gst_vaapi_handle_context_query (element, query);
break;
default:
ret = GST_VIDEO_ENCODER_CLASS (gst_vaapiencode_parent_class)->sink_query
(encoder, query);
break;
}
return ret;
}
static gboolean
gst_vaapiencode_src_query (GstVideoEncoder * encoder, GstQuery * query)
{
gboolean ret = TRUE;
GstElement *const element = GST_ELEMENT (encoder);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONTEXT:
ret = gst_vaapi_handle_context_query (element, query);
break;
default:
ret = GST_VIDEO_ENCODER_CLASS (gst_vaapiencode_parent_class)->src_query
(encoder, query);
break;
}
return ret;
}
typedef struct
{
GstVaapiEncoderProp id;
GParamSpec *pspec;
GValue value;
} PropValue;
static PropValue *
prop_value_new (GstVaapiEncoderPropInfo * prop)
{
static const GValue default_value = G_VALUE_INIT;
PropValue *prop_value;
if (!prop || !prop->pspec)
return NULL;
prop_value = g_slice_new (PropValue);
if (!prop_value)
return NULL;
prop_value->id = prop->prop;
prop_value->pspec = g_param_spec_ref (prop->pspec);
memcpy (&prop_value->value, &default_value, sizeof (prop_value->value));
g_value_init (&prop_value->value, prop->pspec->value_type);
g_param_value_set_default (prop->pspec, &prop_value->value);
return prop_value;
}
static void
prop_value_free (PropValue * prop_value)
{
if (!prop_value)
return;
if (G_VALUE_TYPE (&prop_value->value))
g_value_unset (&prop_value->value);
if (prop_value->pspec) {
g_param_spec_unref (prop_value->pspec);
prop_value->pspec = NULL;
}
g_slice_free (PropValue, prop_value);
}
static inline PropValue *
prop_value_lookup (GstVaapiEncode * encode, guint prop_id)
{
GPtrArray *const prop_values = encode->prop_values;
if (prop_values &&
(prop_id >= PROP_BASE && prop_id < PROP_BASE + prop_values->len))
return g_ptr_array_index (prop_values, prop_id - PROP_BASE);
return NULL;
}
static gboolean
gst_vaapiencode_default_get_property (GstVaapiEncode * encode, guint prop_id,
GValue * value)
{
PropValue *const prop_value = prop_value_lookup (encode, prop_id);
if (prop_value) {
g_value_copy (&prop_value->value, value);
return TRUE;
}
return FALSE;
}
static gboolean
gst_vaapiencode_default_set_property (GstVaapiEncode * encode, guint prop_id,
const GValue * value)
{
PropValue *const prop_value = prop_value_lookup (encode, prop_id);
if (prop_value) {
g_value_copy (value, &prop_value->value);
return TRUE;
}
return FALSE;
}
static GstFlowReturn
gst_vaapiencode_default_alloc_buffer (GstVaapiEncode * encode,
GstVaapiCodedBuffer * coded_buf, GstBuffer ** outbuf_ptr)
{
GstBuffer *buf;
gint32 buf_size;
g_return_val_if_fail (coded_buf != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (outbuf_ptr != NULL, GST_FLOW_ERROR);
buf_size = gst_vaapi_coded_buffer_get_size (coded_buf);
if (buf_size <= 0)
goto error_invalid_buffer;
buf =
gst_video_encoder_allocate_output_buffer (GST_VIDEO_ENCODER_CAST (encode),
buf_size);
if (!buf)
goto error_create_buffer;
if (!gst_vaapi_coded_buffer_copy_into (buf, coded_buf))
goto error_copy_buffer;
*outbuf_ptr = buf;
return GST_FLOW_OK;
/* ERRORS */
error_invalid_buffer:
{
GST_ERROR ("invalid GstVaapiCodedBuffer size (%d bytes)", buf_size);
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
}
error_create_buffer:
{
GST_ERROR ("failed to create output buffer of size %d", buf_size);
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
}
error_copy_buffer:
{
GST_ERROR ("failed to copy GstVaapiCodedBuffer data");
gst_buffer_unref (buf);
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
}
}
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);
if (encode->need_codec_data) {
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_video_encoder_negotiate (venc))
return FALSE;
encode->input_state_changed = FALSE;
return TRUE;
}
static GstFlowReturn
gst_vaapiencode_push_frame (GstVaapiEncode * encode, gint64 timeout)
{
GstVideoEncoder *const venc = GST_VIDEO_ENCODER_CAST (encode);
GstVaapiEncodeClass *const klass = GST_VAAPIENCODE_GET_CLASS (encode);
GstVideoCodecFrame *out_frame;
GstVaapiCodedBufferProxy *codedbuf_proxy = NULL;
GstVaapiEncoderStatus status;
GstBuffer *out_buffer;
GstFlowReturn ret;
status = gst_vaapi_encoder_get_buffer_with_timeout (encode->encoder,
&codedbuf_proxy, timeout);
if (status == GST_VAAPI_ENCODER_STATUS_NO_BUFFER)
return GST_VAAPI_ENCODE_FLOW_TIMEOUT;
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
goto error_get_buffer;
out_frame = gst_vaapi_coded_buffer_proxy_get_user_data (codedbuf_proxy);
if (!out_frame)
goto error_get_buffer;
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,
GST_VAAPI_CODED_BUFFER_PROXY_BUFFER (codedbuf_proxy), &out_buffer);
gst_vaapi_coded_buffer_proxy_replace (&codedbuf_proxy, NULL);
if (ret != GST_FLOW_OK)
goto error_allocate_buffer;
gst_buffer_replace (&out_frame->output_buffer, out_buffer);
gst_buffer_unref (out_buffer);
GST_TRACE_OBJECT (encode, "output:%" GST_TIME_FORMAT ", size:%zu",
GST_TIME_ARGS (out_frame->pts), gst_buffer_get_size (out_buffer));
return gst_video_encoder_finish_frame (venc, out_frame);
/* ERRORS */
error_get_buffer:
{
GST_ERROR ("failed to get encoded buffer (status %d)", status);
if (codedbuf_proxy)
gst_vaapi_coded_buffer_proxy_unref (codedbuf_proxy);
return GST_FLOW_ERROR;
}
error_allocate_buffer:
{
GST_ERROR ("failed to allocate encoded buffer in system memory");
if (out_buffer)
gst_buffer_unref (out_buffer);
gst_video_codec_frame_unref (out_frame);
return ret;
}
error_output_state:
{
GST_ERROR ("failed to negotiate output state (status %d)", status);
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
gst_video_codec_frame_unref (out_frame);
return GST_FLOW_NOT_NEGOTIATED;
}
}
static void
gst_vaapiencode_buffer_loop (GstVaapiEncode * encode)
{
GstFlowReturn ret;
const gint64 timeout = 50000; /* microseconds */
ret = gst_vaapiencode_push_frame (encode, timeout);
if (ret == GST_FLOW_OK || ret == GST_VAAPI_ENCODE_FLOW_TIMEOUT)
return;
GST_LOG_OBJECT (encode, "pausing task, reason %s", gst_flow_get_name (ret));
gst_pad_pause_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode));
}
static gboolean
ensure_allowed_sinkpad_caps (GstVaapiEncode * encode)
{
GstVaapiEncodeClass *klass = GST_VAAPIENCODE_GET_CLASS (encode);
GstCaps *out_caps, *raw_caps = NULL;
GArray *formats = NULL;
gboolean ret = FALSE;
GstVaapiProfile profile = GST_VAAPI_PROFILE_UNKNOWN;
if (encode->allowed_sinkpad_caps)
return TRUE;
if (!encode->encoder)
return TRUE;
out_caps = gst_caps_from_string (GST_VAAPI_MAKE_SURFACE_CAPS);
if (!out_caps)
goto failed_create_va_caps;
if (klass->get_profile) {
GstCaps *allowed =
gst_pad_get_allowed_caps (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode));
if (allowed) {
if (!gst_caps_is_empty (allowed) && !gst_caps_is_any (allowed))
profile = klass->get_profile (allowed);
gst_caps_unref (allowed);
}
}
formats = gst_vaapi_encoder_get_surface_formats (encode->encoder, profile);
if (!formats)
goto failed_get_formats;
raw_caps = gst_vaapi_video_format_new_template_caps_from_list (formats);
if (!raw_caps)
goto failed_create_raw_caps;
out_caps = gst_caps_make_writable (out_caps);
gst_caps_append (out_caps, gst_caps_copy (raw_caps));
gst_caps_replace (&encode->allowed_sinkpad_caps, out_caps);
GST_INFO_OBJECT (encode, "Allowed sink caps %" GST_PTR_FORMAT,
encode->allowed_sinkpad_caps);
ret = TRUE;
bail:
if (out_caps)
gst_caps_unref (out_caps);
if (raw_caps)
gst_caps_unref (raw_caps);
if (formats)
g_array_unref (formats);
return ret;
failed_create_va_caps:
{
GST_WARNING_OBJECT (encode, "failed to create VA/GL sink caps");
return FALSE;
}
failed_get_formats:
{
GST_WARNING_OBJECT (encode, "failed to get allowed surface formats");
goto bail;
}
failed_create_raw_caps:
{
GST_WARNING_OBJECT (encode, "failed to create raw sink caps");
goto bail;
}
}
static GstCaps *
gst_vaapiencode_get_caps (GstVideoEncoder * venc, GstCaps * filter)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
GstCaps *result;
ensure_allowed_sinkpad_caps (encode);
result = gst_video_encoder_proxy_getcaps (venc, encode->allowed_sinkpad_caps,
filter);
GST_DEBUG_OBJECT (venc, "Negotiated sink caps %" GST_PTR_FORMAT, result);
return result;
}
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_caps_replace (&encode->allowed_sinkpad_caps, NULL);
gst_vaapi_encoder_replace (&encode->encoder, NULL);
return TRUE;
}
static void
gst_vaapiencode_purge (GstVaapiEncode * encode)
{
GstVaapiCodedBufferProxy *codedbuf_proxy = NULL;
GstVaapiEncoderStatus status;
GstVideoCodecFrame *out_frame;
do {
status = gst_vaapi_encoder_get_buffer_with_timeout (encode->encoder,
&codedbuf_proxy, 0);
if (status == GST_VAAPI_ENCODER_STATUS_SUCCESS) {
out_frame = gst_vaapi_coded_buffer_proxy_get_user_data (codedbuf_proxy);
if (out_frame)
gst_video_codec_frame_set_user_data (out_frame, NULL, NULL);
gst_vaapi_coded_buffer_proxy_unref (codedbuf_proxy);
}
} while (status == GST_VAAPI_ENCODER_STATUS_SUCCESS);
}
static gboolean
ensure_encoder (GstVaapiEncode * encode)
{
GstVaapiEncodeClass *klass = GST_VAAPIENCODE_GET_CLASS (encode);
GstVaapiEncoderStatus status;
GPtrArray *const prop_values = encode->prop_values;
guint i;
g_return_val_if_fail (klass->alloc_encoder, FALSE);
if (encode->encoder)
return FALSE;
encode->encoder = klass->alloc_encoder (encode,
GST_VAAPI_PLUGIN_BASE_DISPLAY (encode));
if (!encode->encoder)
return FALSE;
if (prop_values) {
for (i = 0; i < prop_values->len; i++) {
PropValue *const prop_value = g_ptr_array_index (prop_values, i);
status = gst_vaapi_encoder_set_property (encode->encoder, prop_value->id,
&prop_value->value);
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
return FALSE;
}
}
return TRUE;
}
static gboolean
gst_vaapiencode_open (GstVideoEncoder * venc)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
GstVaapiDisplay *const old_display = GST_VAAPI_PLUGIN_BASE_DISPLAY (encode);
gboolean success;
if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (encode)))
return FALSE;
GST_VAAPI_PLUGIN_BASE_DISPLAY (encode) = NULL;
success = ensure_display (encode);
if (old_display)
gst_vaapi_display_unref (old_display);
return success;
}
static gboolean
gst_vaapiencode_start (GstVideoEncoder * venc)
{
return ensure_encoder (GST_VAAPIENCODE_CAST (venc));
}
static gboolean
gst_vaapiencode_stop (GstVideoEncoder * venc)
{
return gst_vaapiencode_destroy (GST_VAAPIENCODE_CAST (venc));
}
static gboolean
gst_vaapiencode_close (GstVideoEncoder * venc)
{
gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (venc));
return TRUE;
}
static gboolean
set_codec_state (GstVaapiEncode * encode, GstVideoCodecState * state)
{
GstVaapiEncodeClass *const klass = GST_VAAPIENCODE_GET_CLASS (encode);
GstVaapiEncoderStatus status;
g_return_val_if_fail (encode->encoder, FALSE);
/* Initialize codec specific parameters */
if (klass->set_config && !klass->set_config (encode))
return FALSE;
status = gst_vaapi_encoder_set_codec_state (encode->encoder, state);
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
return FALSE;
return TRUE;
}
static gboolean
gst_vaapiencode_set_format (GstVideoEncoder * venc, GstVideoCodecState * state)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
gboolean ret;
g_return_val_if_fail (state->caps != NULL, FALSE);
if (!set_codec_state (encode, state))
return FALSE;
if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (encode),
state->caps, NULL))
return FALSE;
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;
ret = gst_pad_start_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode),
(GstTaskFunction) gst_vaapiencode_buffer_loop, encode, NULL);
if (!ret)
return FALSE;
/* Store some tags */
{
GstTagList *tags = gst_tag_list_new_empty ();
const gchar *encoder, *codec;
guint bitrate = 0;
g_object_get (encode, "bitrate", &bitrate, NULL);
gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_NOMINAL_BITRATE,
bitrate, NULL);
if ((encoder =
gst_element_class_get_metadata (GST_ELEMENT_GET_CLASS (encode),
GST_ELEMENT_METADATA_LONGNAME)))
gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder,
NULL);
if ((codec =
gst_vaapi_codec_get_name (gst_vaapi_profile_get_codec
(gst_vaapi_profile_from_caps (state->caps)))))
gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CODEC, codec,
NULL);
gst_video_encoder_merge_tags (venc, tags, GST_TAG_MERGE_REPLACE);
gst_tag_list_unref (tags);
}
return TRUE;
}
static GstFlowReturn
gst_vaapiencode_handle_frame (GstVideoEncoder * venc,
GstVideoCodecFrame * frame)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
GstVaapiEncoderStatus status;
GstVaapiVideoMeta *meta;
GstVaapiSurfaceProxy *proxy;
GstFlowReturn ret;
GstBuffer *buf;
buf = NULL;
ret = gst_vaapi_plugin_base_get_input_buffer (GST_VAAPI_PLUGIN_BASE (encode),
frame->input_buffer, &buf);
if (ret != GST_FLOW_OK)
goto error_buffer_invalid;
gst_buffer_replace (&frame->input_buffer, buf);
gst_buffer_unref (buf);
meta = gst_buffer_get_vaapi_video_meta (buf);
if (!meta)
goto error_buffer_no_meta;
proxy = gst_vaapi_video_meta_get_surface_proxy (meta);
if (!proxy)
goto error_buffer_no_surface_proxy;
gst_video_codec_frame_set_user_data (frame,
gst_vaapi_surface_proxy_ref (proxy),
(GDestroyNotify) gst_vaapi_surface_proxy_unref);
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
status = gst_vaapi_encoder_put_frame (encode->encoder, frame);
GST_VIDEO_ENCODER_STREAM_LOCK (encode);
if (status < GST_VAAPI_ENCODER_STATUS_SUCCESS)
goto error_encode_frame;
gst_video_codec_frame_unref (frame);
return GST_FLOW_OK;
/* ERRORS */
error_buffer_invalid:
{
if (buf)
gst_buffer_unref (buf);
gst_video_codec_frame_unref (frame);
return ret;
}
error_buffer_no_meta:
{
GST_ERROR ("failed to get GstVaapiVideoMeta information");
gst_video_codec_frame_unref (frame);
return GST_FLOW_ERROR;
}
error_buffer_no_surface_proxy:
{
GST_ERROR ("failed to get VA surface proxy");
gst_video_codec_frame_unref (frame);
return GST_FLOW_ERROR;
}
error_encode_frame:
{
GST_ERROR ("failed to encode frame %d (status %d)",
frame->system_frame_number, status);
gst_video_codec_frame_unref (frame);
return GST_FLOW_ERROR;
}
}
static GstFlowReturn
gst_vaapiencode_finish (GstVideoEncoder * venc)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
GstVaapiEncoderStatus status;
GstFlowReturn ret = GST_FLOW_OK;
/* Don't try to destroy encoder if none was created in the first place.
Return "not-negotiated" error since this means we did not even reach
GstVideoEncoder::set_format() state, where the encoder could have
been created */
if (!encode->encoder)
return GST_FLOW_NOT_NEGOTIATED;
status = gst_vaapi_encoder_flush (encode->encoder);
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
gst_pad_stop_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode));
GST_VIDEO_ENCODER_STREAM_LOCK (encode);
while (status == GST_VAAPI_ENCODER_STATUS_SUCCESS && ret == GST_FLOW_OK)
ret = gst_vaapiencode_push_frame (encode, 0);
if (ret == GST_VAAPI_ENCODE_FLOW_TIMEOUT)
ret = GST_FLOW_OK;
return ret;
}
static GstStateChangeReturn
gst_vaapiencode_change_state (GstElement * element, GstStateChange transition)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (element);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_pad_stop_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode));
break;
default:
break;
}
return
GST_ELEMENT_CLASS (gst_vaapiencode_parent_class)->change_state (element,
transition);
}
static gboolean
gst_vaapiencode_propose_allocation (GstVideoEncoder * venc, GstQuery * query)
{
GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (venc);
if (!gst_vaapi_plugin_base_propose_allocation (plugin, query))
return FALSE;
return TRUE;
}
static gboolean
gst_vaapiencode_sink_event (GstVideoEncoder * venc, GstEvent * event)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
GstPad *const srcpad = GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode);
gboolean ret;
ret = GST_VIDEO_ENCODER_CLASS (gst_vaapiencode_parent_class)->sink_event
(venc, event);
if (!ret)
return FALSE;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_START:
gst_pad_pause_task (srcpad);
break;
case GST_EVENT_FLUSH_STOP:
ret = gst_pad_start_task (srcpad,
(GstTaskFunction) gst_vaapiencode_buffer_loop, encode, NULL);
break;
default:
break;
}
return ret;
}
static gboolean
gst_vaapiencode_flush (GstVideoEncoder * venc)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
GstVaapiEncoderStatus status;
if (!encode->encoder)
return FALSE;
GST_LOG_OBJECT (encode, "flushing");
status = gst_vaapi_encoder_flush (encode->encoder);
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
return FALSE;
gst_vaapiencode_purge (encode);
gst_vaapi_encoder_replace (&encode->encoder, NULL);
if (!ensure_encoder (encode))
return FALSE;
if (!set_codec_state (encode, encode->input_state))
return FALSE;
return TRUE;
}
static void
gst_vaapiencode_finalize (GObject * object)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (object);
gst_vaapiencode_destroy (encode);
if (encode->prop_values) {
g_ptr_array_unref (encode->prop_values);
encode->prop_values = NULL;
}
gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (object));
G_OBJECT_CLASS (gst_vaapiencode_parent_class)->finalize (object);
}
static void
gst_vaapiencode_init (GstVaapiEncode * encode)
{
GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (encode);
gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (encode), GST_CAT_DEFAULT);
gst_pad_use_fixed_caps (plugin->srcpad);
}
static void
gst_vaapiencode_class_init (GstVaapiEncodeClass * klass)
{
GObjectClass *const object_class = G_OBJECT_CLASS (klass);
GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
GstVideoEncoderClass *const venc_class = GST_VIDEO_ENCODER_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (gst_vaapiencode_debug,
GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
gst_vaapi_plugin_base_class_init (GST_VAAPI_PLUGIN_BASE_CLASS (klass));
object_class->finalize = gst_vaapiencode_finalize;
element_class->set_context = gst_vaapi_base_set_context;
element_class->change_state =
GST_DEBUG_FUNCPTR (gst_vaapiencode_change_state);
venc_class->open = GST_DEBUG_FUNCPTR (gst_vaapiencode_open);
venc_class->close = GST_DEBUG_FUNCPTR (gst_vaapiencode_close);
venc_class->start = GST_DEBUG_FUNCPTR (gst_vaapiencode_start);
venc_class->stop = GST_DEBUG_FUNCPTR (gst_vaapiencode_stop);
venc_class->set_format = GST_DEBUG_FUNCPTR (gst_vaapiencode_set_format);
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);
venc_class->propose_allocation =
GST_DEBUG_FUNCPTR (gst_vaapiencode_propose_allocation);
venc_class->flush = GST_DEBUG_FUNCPTR (gst_vaapiencode_flush);
venc_class->sink_event = GST_DEBUG_FUNCPTR (gst_vaapiencode_sink_event);
klass->get_property = gst_vaapiencode_default_get_property;
klass->set_property = gst_vaapiencode_default_set_property;
klass->alloc_buffer = gst_vaapiencode_default_alloc_buffer;
venc_class->src_query = GST_DEBUG_FUNCPTR (gst_vaapiencode_src_query);
venc_class->sink_query = GST_DEBUG_FUNCPTR (gst_vaapiencode_sink_query);
}
static inline GPtrArray *
get_properties (GstVaapiEncodeClass * klass)
{
return klass->get_properties ? klass->get_properties () : NULL;
}
gboolean
gst_vaapiencode_init_properties (GstVaapiEncode * encode)
{
GPtrArray *const props = get_properties (GST_VAAPIENCODE_GET_CLASS (encode));
guint i;
/* XXX: use base_init()/base_finalize() to avoid multiple initializations */
if (!props)
return FALSE;
encode->prop_values =
g_ptr_array_new_full (props->len, (GDestroyNotify) prop_value_free);
if (!encode->prop_values) {
g_ptr_array_unref (props);
return FALSE;
}
for (i = 0; i < props->len; i++) {
PropValue *const prop_value = prop_value_new ((GstVaapiEncoderPropInfo *)
g_ptr_array_index (props, i));
if (!prop_value)
return FALSE;
g_ptr_array_add (encode->prop_values, prop_value);
}
return TRUE;
}
gboolean
gst_vaapiencode_class_init_properties (GstVaapiEncodeClass * klass)
{
GObjectClass *const object_class = G_OBJECT_CLASS (klass);
GPtrArray *const props = get_properties (klass);
guint i;
if (!props)
return FALSE;
for (i = 0; i < props->len; i++) {
GstVaapiEncoderPropInfo *const prop = g_ptr_array_index (props, i);
g_object_class_install_property (object_class, PROP_BASE + i, prop->pspec);
}
g_ptr_array_unref (props);
return TRUE;
}