gstreamer/gst/vaapi/gstvaapiencode.c
Gwenole Beauchesne 84af151796 vaapiencode: fix support for raw YUV sink buffers.
Allow vaapiencode plug-in elements to encode from raw YUV buffers.
The most efficient way to do so is to let the vaapiencode elements
allocate a buffer pool, and subsequently buffers from it. This means
that upstream elements are expected to honour downstream pools.

If upstream elements insist on providing their own allocated buffers
to the vaapiencode elements, then it possibly would be more efficient
to insert a vaapipostproc element before the vaapiencode element.
This is because vaapipostproc currently has better support than other
elements for "foreign" raw YUV buffers.
2013-11-26 15:55:09 +01:00

821 lines
24 KiB
C

/*
* gstvaapiencode.c - VA-API video encoder
*
* Copyright (C) 2013 Intel Corporation
*
* 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 "gst/vaapi/sysdeps.h"
#include <gst/vaapi/gstvaapidisplay.h>
#include <gst/vaapi/gstvaapiencoder_priv.h>
#include <gst/vaapi/gstvaapiencoder_objects.h>
#include "gstvaapiencode.h"
#include "gstvaapivideocontext.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
#define GST_VAAPI_ENCODE_FLOW_CODEC_DATA_ERROR GST_FLOW_CUSTOM_ERROR_2
typedef struct _GstVaapiEncodeFrameUserData
{
GstVaapiEncObjUserDataHead head;
GstBuffer *vaapi_buf;
} GstVaapiEncodeFrameUserData;
GST_DEBUG_CATEGORY_STATIC (gst_vaapiencode_debug);
#define GST_CAT_DEFAULT gst_vaapiencode_debug
#define GstVideoContextClass GstVideoContextInterface
/* GstImplementsInterface interface */
#if !GST_CHECK_VERSION(1,0,0)
static gboolean
gst_vaapiencode_implements_interface_supported (GstImplementsInterface * iface,
GType type)
{
return (type == GST_TYPE_VIDEO_CONTEXT);
}
static void
gst_vaapiencode_implements_iface_init (GstImplementsInterfaceClass * iface)
{
iface->supported = gst_vaapiencode_implements_interface_supported;
}
#endif
/* context(display) interface */
#if GST_CHECK_VERSION(1,1,0)
static void
gst_vaapiencode_set_context (GstElement * element, GstContext * context)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (element);
GstVaapiDisplay *display = NULL;
if (gst_vaapi_video_context_get_display (context, &display)) {
GST_INFO_OBJECT (element, "set display %p", display);
gst_vaapi_display_replace (&encode->display, display);
}
}
#else
static void
gst_vaapiencode_set_video_context (GstVideoContext * context,
const gchar * type, const GValue * value)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (context);
gst_vaapi_set_display (type, value, &encode->display);
}
static void
gst_video_context_interface_init (GstVideoContextInterface * iface)
{
iface->set_context = gst_vaapiencode_set_video_context;
}
#endif
G_DEFINE_TYPE_WITH_CODE (GstVaapiEncode,
gst_vaapiencode, GST_TYPE_VIDEO_ENCODER,
#if !GST_CHECK_VERSION(1,0,0)
G_IMPLEMENT_INTERFACE (GST_TYPE_IMPLEMENTS_INTERFACE,
gst_vaapiencode_implements_iface_init);
#endif
#if !GST_CHECK_VERSION(1,1,0)
G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_CONTEXT,
gst_video_context_interface_init)
#endif
)
static gboolean
gst_vaapiencode_query (GstPad * pad, GstObject * parent,
GstQuery * query)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (parent);
gboolean success;
GST_DEBUG ("vaapiencode query %s", GST_QUERY_TYPE_NAME (query));
if (gst_vaapi_reply_to_query (query, encode->display))
success = TRUE;
else if (GST_PAD_IS_SINK (pad))
success = encode->sinkpad_query (pad, parent, query);
else
success = encode->srcpad_query (pad, parent, query);;
return success;
}
static GstFlowReturn
gst_vaapiencode_default_allocate_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);
if (!gst_vaapi_coded_buffer_map (coded_buf, NULL))
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
buf_size = gst_vaapi_coded_buffer_get_size (coded_buf);
if (buf_size <= 0) {
GST_ERROR ("get GstVaapiCodedBuf buffer size:%d", buf_size);
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
}
buf =
gst_video_encoder_allocate_output_buffer (GST_VIDEO_ENCODER_CAST (encode),
buf_size);
if (!buf) {
GST_ERROR ("failed to allocate output buffer of size %d", buf_size);
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
}
if (!gst_vaapi_coded_buffer_get_buffer (coded_buf, buf)) {
GST_ERROR ("failed to get encoded buffer");
gst_buffer_replace (&buf, NULL);
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
}
gst_vaapi_coded_buffer_unmap (coded_buf);
*outbuf_ptr = buf;
return GST_FLOW_OK;
}
static GstFlowReturn
gst_vaapiencode_push_frame (GstVaapiEncode * encode, gint64 ms_timeout)
{
GstVideoEncoder *const venc = GST_VIDEO_ENCODER_CAST (encode);
GstVaapiEncodeClass *klass = GST_VAAPIENCODE_GET_CLASS (encode);
GstVideoCodecFrame *out_frame = NULL;
GstVaapiCodedBufferProxy *coded_buf_proxy = NULL;
GstVaapiCodedBuffer *coded_buf;
GstVaapiEncoderStatus encode_status;
GstBuffer *output_buf;
GstFlowReturn ret;
g_return_val_if_fail (klass->allocate_buffer, GST_FLOW_ERROR);
encode_status = gst_vaapi_encoder_get_buffer (encode->encoder,
&out_frame, &coded_buf_proxy, ms_timeout);
if (encode_status == GST_VAAPI_ENCODER_STATUS_TIMEOUT)
return GST_VAAPI_ENCODE_FLOW_TIMEOUT;
if (encode_status != GST_VAAPI_ENCODER_STATUS_SUCCESS) {
GST_ERROR ("get encoded buffer failed, status:%d", encode_status);
ret = GST_FLOW_ERROR;
goto error;
}
g_assert (out_frame);
gst_video_codec_frame_set_user_data (out_frame, NULL, NULL);
coded_buf = coded_buf_proxy->buffer;
g_assert (coded_buf);
/* alloc buffer */
ret = klass->allocate_buffer (encode, coded_buf, &output_buf);
if (ret != GST_FLOW_OK)
goto error;
out_frame->output_buffer = output_buf;
gst_vaapi_coded_buffer_proxy_replace (&coded_buf_proxy, NULL);
/* check out_caps, need lock first */
GST_VIDEO_ENCODER_STREAM_LOCK (encode);
if (!encode->out_caps_done) {
GstVaapiEncoderStatus encoder_status;
GstVideoCodecState *old_state, *new_state;
GstBuffer *codec_data;
encoder_status =
gst_vaapi_encoder_get_codec_data (encode->encoder, &codec_data);
if (encoder_status != GST_VAAPI_ENCODER_STATUS_SUCCESS) {
ret = GST_VAAPI_ENCODE_FLOW_CODEC_DATA_ERROR;
goto error_unlock;
}
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_replace (&codec_data, NULL);
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);
if (!gst_video_encoder_negotiate (GST_VIDEO_ENCODER_CAST (encode))) {
GST_ERROR ("failed to negotiate with caps %" GST_PTR_FORMAT,
encode->srcpad_caps);
ret = GST_FLOW_NOT_NEGOTIATED;
goto error_unlock;
}
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:%d",
GST_TIME_ARGS (out_frame->pts), gst_buffer_get_size (output_buf));
ret = gst_video_encoder_finish_frame (venc, out_frame);
out_frame = NULL;
if (ret != GST_FLOW_OK)
goto error;
return GST_FLOW_OK;
error_unlock:
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
error:
gst_vaapi_coded_buffer_proxy_replace (&coded_buf_proxy, NULL);
if (out_frame)
gst_video_codec_frame_unref (out_frame);
return ret;
}
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_pad_pause_task (encode->srcpad);
}
static GstCaps *
gst_vaapiencode_get_caps_impl (GstVideoEncoder * venc)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (venc);
GstCaps *caps;
if (encode->sinkpad_caps)
caps = gst_caps_ref (encode->sinkpad_caps);
else
caps = gst_pad_get_pad_template_caps (encode->sinkpad);
return caps;
}
static GstCaps *
gst_vaapiencode_get_caps (GstVideoEncoder * venc, GstCaps * filter)
{
GstCaps *caps, *out_caps;
out_caps = gst_vaapiencode_get_caps_impl (venc);
if (out_caps && filter) {
caps = gst_caps_intersect_full (out_caps, filter, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (out_caps);
out_caps = caps;
}
return out_caps;
}
static gboolean
gst_vaapiencode_destroy (GstVaapiEncode * encode)
{
gst_vaapi_encoder_replace (&encode->encoder, NULL);
g_clear_object (&encode->video_buffer_pool);
if (encode->sinkpad_caps) {
gst_caps_unref (encode->sinkpad_caps);
encode->sinkpad_caps = NULL;
}
if (encode->srcpad_caps) {
gst_caps_unref (encode->srcpad_caps);
encode->srcpad_caps = NULL;
}
gst_vaapi_display_replace (&encode->display, NULL);
return TRUE;
}
static inline gboolean
ensure_display (GstVaapiEncode * encode)
{
return gst_vaapi_ensure_display (encode,
GST_VAAPI_DISPLAY_TYPE_ANY, &encode->display);
}
static gboolean
ensure_encoder (GstVaapiEncode * encode)
{
GstVaapiEncodeClass *klass = GST_VAAPIENCODE_GET_CLASS (encode);
g_return_val_if_fail (klass->create_encoder, FALSE);
if (!ensure_display (encode))
return FALSE;
encode->encoder = klass->create_encoder (encode, encode->display);
g_assert (encode->encoder);
return (encode->encoder ? TRUE : FALSE);
}
static gboolean
gst_vaapiencode_open (GstVideoEncoder * venc)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (venc);
GstVaapiDisplay *const old_display = encode->display;
gboolean success;
encode->display = NULL;
success = ensure_display (encode);
if (old_display)
gst_vaapi_display_unref (old_display);
GST_DEBUG ("ensure display %s, display:%p",
(success ? "okay" : "failed"), encode->display);
return success;
}
static gboolean
gst_vaapiencode_close (GstVideoEncoder * venc)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (venc);
GST_DEBUG ("vaapiencode starting close");
return gst_vaapiencode_destroy (encode);
}
static inline gboolean
gst_vaapiencode_update_sink_caps (GstVaapiEncode * encode,
GstVideoCodecState * state)
{
gst_caps_replace (&encode->sinkpad_caps, state->caps);
encode->sink_video_info = state->info;
return TRUE;
}
static gboolean
gst_vaapiencode_update_src_caps (GstVaapiEncode * encode,
GstVideoCodecState * in_state)
{
GstVideoCodecState *out_state;
GstStructure *structure;
GstCaps *outcaps, *allowed_caps, *template_caps, *intersect;
GstVaapiEncoderStatus encoder_status;
GstBuffer *codec_data = NULL;
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);
intersect = gst_caps_intersect (template_caps, allowed_caps);
gst_caps_unref (template_caps);
gst_caps_unref (allowed_caps);
/* codec data was not set */
outcaps = gst_vaapi_encoder_set_format (encode->encoder, in_state, intersect);
gst_caps_unref (intersect);
g_return_val_if_fail (outcaps, FALSE);
if (!gst_caps_is_fixed (outcaps)) {
GST_ERROR ("encoder output caps was not fixed");
gst_caps_unref (outcaps);
return FALSE;
}
structure = gst_caps_get_structure (outcaps, 0);
if (!gst_structure_has_field (structure, "codec_data")) {
encoder_status =
gst_vaapi_encoder_get_codec_data (encode->encoder, &codec_data);
if (encoder_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);
return TRUE;
}
static gboolean
gst_vaapiencode_ensure_video_buffer_pool (GstVaapiEncode * encode,
GstCaps * caps)
{
GstBufferPool *pool;
GstCaps *pool_caps;
GstStructure *config;
GstVideoInfo vi;
gboolean need_pool;
if (!ensure_display (encode))
return FALSE;
if (encode->video_buffer_pool) {
config = gst_buffer_pool_get_config (encode->video_buffer_pool);
gst_buffer_pool_config_get_params (config, &pool_caps, NULL, NULL, NULL);
need_pool = !gst_caps_is_equal (caps, pool_caps);
gst_structure_free (config);
if (!need_pool)
return TRUE;
g_clear_object (&encode->video_buffer_pool);
encode->video_buffer_size = 0;
}
pool = gst_vaapi_video_buffer_pool_new (encode->display);
if (!pool)
goto error_create_pool;
gst_video_info_init (&vi);
gst_video_info_from_caps (&vi, caps);
if (GST_VIDEO_INFO_FORMAT (&vi) == GST_VIDEO_FORMAT_ENCODED) {
GST_DEBUG ("assume video buffer pool format is NV12");
gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_NV12,
GST_VIDEO_INFO_WIDTH (&vi), GST_VIDEO_INFO_HEIGHT (&vi));
}
encode->video_buffer_size = vi.size;
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, encode->video_buffer_size,
0, 0);
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META);
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
if (!gst_buffer_pool_set_config (pool, config))
goto error_pool_config;
encode->video_buffer_pool = pool;
return TRUE;
/* ERRORS */
error_create_pool:
{
GST_ERROR ("failed to create buffer pool");
return FALSE;
}
error_pool_config:
{
GST_ERROR ("failed to reset buffer pool config");
gst_object_unref (pool);
return FALSE;
}
}
static gboolean
gst_vaapiencode_set_format (GstVideoEncoder * venc, GstVideoCodecState * state)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (venc);
g_return_val_if_fail (state->caps != NULL, FALSE);
if (!gst_vaapiencode_ensure_video_buffer_pool (encode, state->caps))
return FALSE;
if (!ensure_encoder (encode))
return FALSE;
if (!gst_vaapiencode_update_sink_caps (encode, state))
return FALSE;
if (!gst_vaapiencode_update_src_caps (encode, state))
return FALSE;
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;
}
return gst_pad_start_task (encode->srcpad,
(GstTaskFunction) gst_vaapiencode_buffer_loop, encode, NULL);
return TRUE;
}
static gboolean
gst_vaapiencode_reset (GstVideoEncoder * venc, gboolean hard)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (venc);
GST_DEBUG ("vaapiencode starting reset");
/* FIXME: compare sink_caps with encoder */
encode->is_running = FALSE;
encode->out_caps_done = FALSE;
return TRUE;
}
static GstFlowReturn
get_source_buffer (GstVaapiEncode * encode, GstBuffer * src_buffer,
GstBuffer ** out_buffer_ptr)
{
GstVaapiVideoMeta *meta;
GstBuffer *out_buffer;
GstVideoFrame src_frame, out_frame;
gboolean success;
*out_buffer_ptr = NULL;
meta = gst_buffer_get_vaapi_video_meta (src_buffer);
if (meta) {
*out_buffer_ptr = gst_buffer_ref (src_buffer);
return GST_FLOW_OK;
}
if (!GST_VIDEO_INFO_IS_YUV (&encode->sink_video_info))
goto error_invalid_buffer;
if (!encode->video_buffer_pool)
goto error_no_pool;
if (!gst_buffer_pool_set_active (encode->video_buffer_pool, TRUE))
goto error_activate_pool;
out_buffer = NULL;
success = gst_buffer_pool_acquire_buffer (encode->video_buffer_pool,
&out_buffer, NULL) == GST_FLOW_OK;
if (!success)
goto error_create_buffer;
if (!gst_video_frame_map (&src_frame, &encode->sink_video_info, src_buffer,
GST_MAP_READ))
goto error_map_src_buffer;
if (!gst_video_frame_map (&out_frame, &encode->sink_video_info, out_buffer,
GST_MAP_WRITE))
goto error_map_dst_buffer;
success = gst_video_frame_copy (&out_frame, &src_frame);
gst_video_frame_unmap (&out_frame);
gst_video_frame_unmap (&src_frame);
if (!success)
goto error_copy_buffer;
gst_buffer_copy_into (out_buffer, src_buffer, GST_BUFFER_COPY_TIMESTAMPS, 0,
-1);
*out_buffer_ptr = out_buffer;
return GST_FLOW_OK;
/* ERRORS */
error_invalid_buffer:
{
GST_ERROR ("unsupported video buffer");
return GST_FLOW_EOS;
}
error_no_pool:
{
GST_ERROR ("no buffer pool was negotiated");
return GST_FLOW_ERROR;
}
error_activate_pool:
{
GST_ERROR ("failed to activate buffer pool");
return GST_FLOW_ERROR;
}
error_create_buffer:
{
GST_WARNING ("failed to create buffer. Skipping this frame");
return GST_FLOW_OK;
}
error_map_dst_buffer:
{
gst_video_frame_unmap (&src_frame);
// fall-through
}
error_map_src_buffer:
{
GST_WARNING ("failed to map buffer. Skipping this frame");
gst_buffer_unref (out_buffer);
return GST_FLOW_OK;
}
error_copy_buffer:
{
GST_WARNING ("failed to upload buffer to VA surface. Skipping this frame");
gst_buffer_unref (out_buffer);
return GST_FLOW_OK;
}
}
static inline gpointer
_create_user_data (GstBuffer * buf)
{
GstVaapiVideoMeta *meta;
GstVaapiSurface *surface;
GstVaapiEncodeFrameUserData *user_data;
meta = gst_buffer_get_vaapi_video_meta (buf);
if (!meta) {
GST_DEBUG ("convert to vaapi buffer failed");
return NULL;
}
surface = gst_vaapi_video_meta_get_surface (meta);
if (!surface) {
GST_DEBUG ("vaapi_meta of codec frame doesn't have vaapisurfaceproxy");
return NULL;
}
user_data = g_slice_new0 (GstVaapiEncodeFrameUserData);
user_data->head.surface = surface;
user_data->vaapi_buf = gst_buffer_ref (buf);
return user_data;
}
static void
_destroy_user_data (gpointer data)
{
GstVaapiEncodeFrameUserData *user_data = (GstVaapiEncodeFrameUserData *) data;
g_assert (data);
if (!user_data)
return;
gst_buffer_replace (&user_data->vaapi_buf, NULL);
g_slice_free (GstVaapiEncodeFrameUserData, user_data);
}
static GstFlowReturn
gst_vaapiencode_handle_frame (GstVideoEncoder * venc,
GstVideoCodecFrame * frame)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (venc);
GstFlowReturn ret = GST_FLOW_OK;
GstVaapiEncoderStatus encoder_ret = GST_VAAPI_ENCODER_STATUS_SUCCESS;
GstBuffer *buf;
gpointer user_data;
g_assert (encode && encode->encoder);
g_assert (frame && frame->input_buffer);
ret = get_source_buffer (encode, frame->input_buffer, &buf);
if (ret != GST_FLOW_OK)
return ret;
user_data = _create_user_data (buf);
GST_VAAPI_ENCODER_CHECK_STATUS (user_data,
ret, "create frame user data failed");
gst_video_codec_frame_set_user_data (frame, user_data, _destroy_user_data);
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
/*encoding frames */
encoder_ret = gst_vaapi_encoder_put_frame (encode->encoder, frame);
GST_VIDEO_ENCODER_STREAM_LOCK (encode);
GST_VAAPI_ENCODER_CHECK_STATUS (GST_VAAPI_ENCODER_STATUS_SUCCESS <=
encoder_ret, GST_FLOW_ERROR, "gst_vaapiencoder_encode failed.");
end:
gst_video_codec_frame_unref (frame);
gst_buffer_replace (&buf, NULL);
return ret;
}
static GstFlowReturn
gst_vaapiencode_finish (GstVideoEncoder * venc)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (venc);
GstVaapiEncoderStatus status;
GstFlowReturn ret = GST_FLOW_OK;
GST_DEBUG ("vaapiencode starting finish");
status = gst_vaapi_encoder_flush (encode->encoder);
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
gst_pad_stop_task (encode->srcpad);
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 gboolean
gst_vaapiencode_propose_allocation (GstVideoEncoder * venc, GstQuery * query)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (venc);
GstCaps *caps = NULL;
gboolean need_pool;
gst_query_parse_allocation (query, &caps, &need_pool);
if (need_pool) {
if (!caps)
goto error_no_caps;
if (!gst_vaapiencode_ensure_video_buffer_pool (encode, caps))
return FALSE;
gst_query_add_allocation_pool (query, encode->video_buffer_pool,
encode->video_buffer_size, 0, 0);
}
gst_query_add_allocation_meta (query, GST_VAAPI_VIDEO_META_API_TYPE, NULL);
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
return TRUE;
/* ERRORS */
error_no_caps:
{
GST_ERROR ("no caps specified");
return FALSE;
}
}
static void
gst_vaapiencode_finalize (GObject * object)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE (object);
gst_vaapiencode_destroy (encode);
encode->sinkpad = NULL;
encode->srcpad = NULL;
G_OBJECT_CLASS (gst_vaapiencode_parent_class)->finalize (object);
}
static void
gst_vaapiencode_init (GstVaapiEncode * encode)
{
/* sink pad */
encode->sinkpad = GST_VIDEO_ENCODER_SINK_PAD (encode);
encode->sinkpad_query = GST_PAD_QUERYFUNC (encode->sinkpad);
gst_pad_set_query_function (encode->sinkpad, gst_vaapiencode_query);
gst_video_info_init (&encode->sink_video_info);
/* src pad */
encode->srcpad = GST_VIDEO_ENCODER_SRC_PAD (encode);
encode->srcpad_query = GST_PAD_QUERYFUNC (encode->srcpad);
gst_pad_set_query_function (encode->srcpad, gst_vaapiencode_query);
gst_pad_use_fixed_caps (encode->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);
object_class->finalize = gst_vaapiencode_finalize;
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);
venc_class->propose_allocation =
GST_DEBUG_FUNCPTR (gst_vaapiencode_propose_allocation);
klass->allocate_buffer = gst_vaapiencode_default_allocate_buffer;
#if GST_CHECK_VERSION(1,1,0)
element_class->set_context = GST_DEBUG_FUNCPTR (gst_vaapiencode_set_context);
#endif
/* Registering debug symbols for function pointers */
GST_DEBUG_REGISTER_FUNCPTR (gst_vaapiencode_query);
}