gstreamer/gst/vaapi/gstvaapiencode.c
Gwenole Beauchesne 30f382fcdf vaapiencode: optimize _handle_frame() to avoid extra allocation.
Optimize gst_vaapiencode_handle_frame() to avoid extra memory allocation,
and in particular the GstVaapiEncObjUserData object. i.e. directly use
the VA surface proxy from the source buffer. This also makes the user
data attached to the GstVideoCodecFrame more consistent between both
the decoder and encoder plug-in elements.
2013-11-28 17:32:18 +01:00

983 lines
28 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/gstvaapivalue.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"
#if GST_CHECK_VERSION(1,0,0)
#include "gstvaapivideomemory.h"
#include "gstvaapivideobufferpool.h"
#endif
#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
GST_DEBUG_CATEGORY_STATIC (gst_vaapiencode_debug);
#define GST_CAT_DEFAULT gst_vaapiencode_debug
/* 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
/* GstContext interface */
#if GST_CHECK_VERSION(1,1,0)
static void
gst_vaapiencode_set_context (GstElement * element, GstContext * context)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (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_CAST (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;
}
#define GstVideoContextClass GstVideoContextInterface
#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
)
enum
{
PROP_0,
PROP_RATE_CONTROL,
PROP_BITRATE,
};
static inline gboolean
ensure_display (GstVaapiEncode * encode)
{
return gst_vaapi_ensure_display (encode,
GST_VAAPI_DISPLAY_TYPE_ANY, &encode->display);
}
static gboolean
ensure_uploader (GstVaapiEncode * encode)
{
#if !GST_CHECK_VERSION(1,0,0)
if (!ensure_display (encode))
return FALSE;
if (!encode->uploader) {
encode->uploader = gst_vaapi_uploader_new (encode->display);
if (!encode->uploader)
return FALSE;
}
if (!gst_vaapi_uploader_ensure_display (encode->uploader, encode->display))
return FALSE;
#endif
return TRUE;
}
static gboolean
ensure_uploader_caps (GstVaapiEncode * encode)
{
#if !GST_CHECK_VERSION(1,0,0)
if (GST_VIDEO_INFO_IS_YUV (&encode->sink_video_info) &&
!gst_vaapi_uploader_ensure_caps (encode->uploader, encode->sinkpad_caps,
NULL))
return FALSE;
#endif
return TRUE;
}
static gboolean
gst_vaapiencode_query (GST_PAD_QUERY_FUNCTION_ARGS)
{
GstVaapiEncode *const encode =
GST_VAAPIENCODE_CAST (gst_pad_get_parent_element (pad));
gboolean success;
GST_INFO_OBJECT (encode, "query type %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 = GST_PAD_QUERY_FUNCTION_CALL (encode->sinkpad_query,
encode->sinkpad, parent, query);
else
success = GST_PAD_QUERY_FUNCTION_CALL (encode->srcpad_query,
encode->srcpad, parent, query);
gst_object_unref (encode);
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)
goto error_invalid_buffer;
#if GST_CHECK_VERSION(1,0,0)
buf =
gst_video_encoder_allocate_output_buffer (GST_VIDEO_ENCODER_CAST (encode),
buf_size);
#else
buf = gst_buffer_new_and_alloc (buf_size);
#endif
if (!buf)
goto error_create_buffer;
if (!gst_vaapi_coded_buffer_get_buffer (coded_buf, buf))
goto error_copy_buffer;
gst_vaapi_coded_buffer_unmap (coded_buf);
*outbuf_ptr = buf;
return GST_FLOW_OK;
/* ERRORS */
error_invalid_buffer:
{
GST_ERROR ("invalid GstVaapiCodedBuffer size (%d)", buf_size);
gst_vaapi_coded_buffer_unmap (coded_buf);
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
}
error_create_buffer:
{
GST_ERROR ("failed to create output buffer of size %d", buf_size);
gst_vaapi_coded_buffer_unmap (coded_buf);
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
}
error_copy_buffer:
{
GST_ERROR ("failed to copy GstVaapiCodedBuffer data");
gst_buffer_unref (buf);
gst_vaapi_coded_buffer_unmap (coded_buf);
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
}
}
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 = NULL;
GstVaapiCodedBufferProxy *coded_buf_proxy = NULL;
GstVaapiEncoderStatus status;
GstBuffer *out_buffer;
GstFlowReturn ret;
status = gst_vaapi_encoder_get_buffer (encode->encoder,
&out_frame, &coded_buf_proxy, timeout);
if (status == GST_VAAPI_ENCODER_STATUS_TIMEOUT)
return GST_VAAPI_ENCODE_FLOW_TIMEOUT;
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
goto error_get_buffer;
gst_video_codec_frame_set_user_data (out_frame, NULL, NULL);
/* Allocate and copy buffer into system memory */
out_buffer = NULL;
ret = klass->allocate_buffer (encode, coded_buf_proxy->buffer, &out_buffer);
gst_vaapi_coded_buffer_proxy_replace (&coded_buf_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);
/* 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:%d",
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 (out_frame)
gst_video_codec_frame_unref (out_frame);
if (coded_buf_proxy)
gst_vaapi_coded_buffer_proxy_unref (coded_buf_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);
return ret;
}
error_codec_data:
{
GST_ERROR ("failed to construct codec-data (status %d)", status);
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
gst_video_codec_frame_unref (out_frame);
return GST_VAAPI_ENCODE_FLOW_CODEC_DATA_ERROR;
}
}
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_CAST (venc);
GstCaps *caps;
if (encode->sinkpad_caps)
caps = gst_caps_ref (encode->sinkpad_caps);
else {
#if GST_CHECK_VERSION(1,0,0)
caps = gst_pad_get_pad_template_caps (encode->sinkpad);
#else
caps = gst_caps_from_string (GST_VAAPI_SURFACE_CAPS);
if (caps && ensure_uploader (encode)) {
GstCaps *const yuv_caps = gst_vaapi_uploader_get_caps (encode->uploader);
if (yuv_caps) {
caps = gst_caps_make_writable (caps);
gst_caps_append (caps, gst_caps_copy (yuv_caps));
}
}
#endif
}
return caps;
}
#if GST_CHECK_VERSION(1,0,0)
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;
}
#else
#define gst_vaapiencode_get_caps gst_vaapiencode_get_caps_impl
#endif
static gboolean
gst_vaapiencode_destroy (GstVaapiEncode * encode)
{
gst_vaapi_encoder_replace (&encode->encoder, NULL);
#if GST_CHECK_VERSION(1,0,0)
g_clear_object (&encode->video_buffer_pool);
#endif
g_clear_object (&encode->uploader);
gst_caps_replace (&encode->sinkpad_caps, NULL);
gst_caps_replace (&encode->srcpad_caps, NULL);
gst_vaapi_display_replace (&encode->display, NULL);
return TRUE;
}
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;
if (!ensure_uploader (encode))
return FALSE;
if (!ensure_uploader_caps (encode))
return FALSE;
encode->encoder = klass->create_encoder (encode, encode->display);
if (!encode->encoder)
return FALSE;
return TRUE;
}
static gboolean
gst_vaapiencode_open (GstVideoEncoder * venc)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (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);
return success;
}
static gboolean
gst_vaapiencode_close (GstVideoEncoder * venc)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
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;
#if !GST_CHECK_VERSION(1,0,0)
if (GST_VIDEO_INFO_IS_YUV (&encode->sink_video_info)) {
/* Ensure the uploader is set up for upstream allocated buffers */
GstVaapiUploader *const uploader = encode->uploader;
if (!gst_vaapi_uploader_ensure_display (uploader, encode->display))
return FALSE;
if (!gst_vaapi_uploader_ensure_caps (uploader, state->caps, NULL))
return FALSE;
}
#endif
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 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")) {
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);
return TRUE;
}
static gboolean
gst_vaapiencode_ensure_video_buffer_pool (GstVaapiEncode * encode,
GstCaps * caps)
{
#if GST_CHECK_VERSION(1,0,0)
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;
}
#else
return TRUE;
#endif
}
static gboolean
gst_vaapiencode_set_format (GstVideoEncoder * venc, GstVideoCodecState * state)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (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 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
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
get_source_buffer (GstVaapiEncode * encode, GstBuffer * src_buffer,
GstBuffer ** out_buffer_ptr)
{
GstVaapiVideoMeta *meta;
GstBuffer *out_buffer;
#if GST_CHECK_VERSION(1,0,0)
GstVideoFrame src_frame, out_frame;
gboolean success;
#endif
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_CHECK_VERSION(1,0,0)
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_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;
}
#else
out_buffer = gst_vaapi_uploader_get_buffer (encode->uploader);
if (!out_buffer)
goto error_create_buffer;
if (!gst_vaapi_uploader_process (encode->uploader, src_buffer, out_buffer))
goto error_copy_buffer;
gst_buffer_copy_metadata (out_buffer, src_buffer,
GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS);
*out_buffer_ptr = out_buffer;
return GST_FLOW_OK;
#endif
/* ERRORS */
error_create_buffer:
{
GST_WARNING ("failed to create buffer. Skipping this frame");
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 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 = get_source_buffer (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;
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;
}
#if GST_CHECK_VERSION(1,0,0)
static gboolean
gst_vaapiencode_propose_allocation (GstVideoEncoder * venc, GstQuery * query)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (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;
}
}
#endif
static inline gboolean
check_ratecontrol (GstVaapiEncode * encode, GstVaapiRateControl rate_control)
{
GstVaapiEncodeClass *const klass = GST_VAAPIENCODE_GET_CLASS (encode);
return !klass->check_ratecontrol || klass->check_ratecontrol (encode,
rate_control);
}
static void
gst_vaapiencode_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (object);
switch (prop_id) {
case PROP_RATE_CONTROL:
{
GstVaapiRateControl rate_control = g_value_get_enum (value);
if (check_ratecontrol (encode, rate_control)) {
encode->rate_control = rate_control;
} else {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
break;
}
case PROP_BITRATE:
encode->bitrate = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_vaapiencode_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (object);
switch (prop_id) {
case PROP_RATE_CONTROL:
g_value_set_enum (value, encode->rate_control);
break;
case PROP_BITRATE:
g_value_set_uint (value, encode->bitrate);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_vaapiencode_finalize (GObject * object)
{
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (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);
#if GST_CHECK_VERSION(1,1,0)
GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
#endif
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;
object_class->set_property = gst_vaapiencode_set_property;
object_class->get_property = gst_vaapiencode_get_property;
#if GST_CHECK_VERSION(1,1,0)
element_class->set_context = GST_DEBUG_FUNCPTR (gst_vaapiencode_set_context);
#endif
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);
#if GST_CHECK_VERSION(1,0,0)
venc_class->propose_allocation =
GST_DEBUG_FUNCPTR (gst_vaapiencode_propose_allocation);
#endif
klass->allocate_buffer = gst_vaapiencode_default_allocate_buffer;
/* Registering debug symbols for function pointers */
GST_DEBUG_REGISTER_FUNCPTR (gst_vaapiencode_query);
g_object_class_install_property (object_class,
PROP_RATE_CONTROL,
g_param_spec_enum ("rate-control",
"Rate Control",
"Rate control mode",
GST_VAAPI_TYPE_RATE_CONTROL,
GST_VAAPI_RATECONTROL_NONE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
PROP_BITRATE,
g_param_spec_uint ("bitrate",
"Bitrate (kbps)",
"The desired bitrate expressed in kbps (0: auto-calculate)",
0, 100 * 1024, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}