2013-07-29 05:44:48 +00:00
|
|
|
/*
|
|
|
|
* gstvaapiencode.c - VA-API video encoder
|
|
|
|
*
|
2014-01-22 17:54:14 +00:00
|
|
|
* Copyright (C) 2013-2014 Intel Corporation
|
2014-01-22 17:49:20 +00:00
|
|
|
* Author: Wind Yuan <feng.yuan@intel.com>
|
|
|
|
* Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
|
2013-07-29 05:44:48 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2015-04-03 14:08:30 +00:00
|
|
|
#include "gstcompat.h"
|
2013-11-28 12:26:40 +00:00
|
|
|
#include <gst/vaapi/gstvaapivalue.h>
|
2013-07-29 05:44:48 +00:00
|
|
|
#include <gst/vaapi/gstvaapidisplay.h>
|
2019-08-16 17:35:58 +00:00
|
|
|
#include <gst/vaapi/gstvaapiprofilecaps.h>
|
2013-07-29 05:44:48 +00:00
|
|
|
#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);
|
2017-03-20 15:45:01 +00:00
|
|
|
#ifndef GST_DISABLE_GST_DEBUG
|
2013-07-29 05:44:48 +00:00
|
|
|
#define GST_CAT_DEFAULT gst_vaapiencode_debug
|
2017-03-20 15:45:01 +00:00
|
|
|
#else
|
|
|
|
#define GST_CAT_DEFAULT NULL
|
|
|
|
#endif
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2014-01-08 17:56:23 +00:00
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstVaapiEncode,
|
2013-07-29 05:44:48 +00:00
|
|
|
gst_vaapiencode, GST_TYPE_VIDEO_ENCODER,
|
2014-01-03 15:57:25 +00:00
|
|
|
GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2016-04-21 10:57:30 +00:00
|
|
|
GST_VAAPI_PLUGIN_BASE_DEFINE_SET_CONTEXT (gst_vaapiencode_parent_class);
|
|
|
|
|
2013-11-28 12:26:40 +00:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
2014-01-08 17:36:46 +00:00
|
|
|
|
|
|
|
PROP_BASE,
|
2013-11-28 12:26:40 +00:00
|
|
|
};
|
|
|
|
|
2013-11-27 15:27:31 +00:00
|
|
|
static inline gboolean
|
|
|
|
ensure_display (GstVaapiEncode * encode)
|
|
|
|
{
|
2013-12-13 10:52:47 +00:00
|
|
|
return gst_vaapi_plugin_base_ensure_display (GST_VAAPI_PLUGIN_BASE (encode));
|
2013-11-27 15:27:31 +00:00
|
|
|
}
|
|
|
|
|
2015-11-05 11:39:55 +00:00
|
|
|
static gboolean
|
|
|
|
gst_vaapiencode_sink_query (GstVideoEncoder * encoder, GstQuery * query)
|
|
|
|
{
|
|
|
|
gboolean ret = TRUE;
|
2017-01-27 11:10:54 +00:00
|
|
|
GstElement *const element = GST_ELEMENT (encoder);
|
2015-11-05 11:39:55 +00:00
|
|
|
|
|
|
|
switch (GST_QUERY_TYPE (query)) {
|
|
|
|
case GST_QUERY_CONTEXT:
|
2017-01-27 11:10:54 +00:00
|
|
|
ret = gst_vaapi_handle_context_query (element, query);
|
2015-11-05 11:39:55 +00:00
|
|
|
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;
|
2017-01-27 11:10:54 +00:00
|
|
|
GstElement *const element = GST_ELEMENT (encoder);
|
2015-11-05 11:39:55 +00:00
|
|
|
|
|
|
|
switch (GST_QUERY_TYPE (query)) {
|
|
|
|
case GST_QUERY_CONTEXT:
|
2017-01-27 11:10:54 +00:00
|
|
|
ret = gst_vaapi_handle_context_query (element, query);
|
2015-11-05 11:39:55 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = GST_VIDEO_ENCODER_CLASS (gst_vaapiencode_parent_class)->src_query
|
|
|
|
(encoder, query);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2014-01-08 17:36:46 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
2019-08-20 14:16:35 +00:00
|
|
|
guint id;
|
2014-01-08 17:36:46 +00:00
|
|
|
GParamSpec *pspec;
|
|
|
|
GValue value;
|
|
|
|
} PropValue;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-08-20 14:16:35 +00:00
|
|
|
static PropValue *
|
|
|
|
prop_value_new_entry (guint id, GParamSpec * pspec, const GValue * value)
|
|
|
|
{
|
|
|
|
PropValue *prop_value;
|
|
|
|
|
|
|
|
if (!pspec)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
prop_value = g_slice_new0 (PropValue);
|
|
|
|
if (!prop_value)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
prop_value->id = id;
|
|
|
|
prop_value->pspec = g_param_spec_ref (pspec);
|
|
|
|
g_value_init (&prop_value->value, pspec->value_type);
|
|
|
|
|
|
|
|
g_assert (g_value_type_compatible (pspec->value_type, G_VALUE_TYPE (value)));
|
|
|
|
g_value_copy (value, &prop_value->value);
|
|
|
|
|
|
|
|
return prop_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline PropValue *
|
|
|
|
prop_value_lookup_entry (GPtrArray * prop_values, guint prop_id)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
PropValue *prop_value;
|
|
|
|
|
|
|
|
if (prop_values == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < prop_values->len; i++) {
|
|
|
|
prop_value = g_ptr_array_index (prop_values, i);
|
|
|
|
if (prop_value->id == prop_id)
|
|
|
|
return prop_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
static GstFlowReturn
|
2014-01-09 09:09:38 +00:00
|
|
|
gst_vaapiencode_default_alloc_buffer (GstVaapiEncode * encode,
|
2013-07-29 05:44:48 +00:00
|
|
|
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);
|
2013-11-26 16:26:44 +00:00
|
|
|
if (buf_size <= 0)
|
|
|
|
goto error_invalid_buffer;
|
|
|
|
|
2013-11-27 15:27:31 +00:00
|
|
|
buf =
|
|
|
|
gst_video_encoder_allocate_output_buffer (GST_VIDEO_ENCODER_CAST (encode),
|
|
|
|
buf_size);
|
2013-11-26 16:26:44 +00:00
|
|
|
if (!buf)
|
|
|
|
goto error_create_buffer;
|
2013-12-03 15:11:46 +00:00
|
|
|
if (!gst_vaapi_coded_buffer_copy_into (buf, coded_buf))
|
2013-11-26 16:26:44 +00:00
|
|
|
goto error_copy_buffer;
|
|
|
|
|
|
|
|
*outbuf_ptr = buf;
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
|
|
|
|
/* ERRORS */
|
|
|
|
error_invalid_buffer:
|
|
|
|
{
|
2014-01-02 10:17:28 +00:00
|
|
|
GST_ERROR ("invalid GstVaapiCodedBuffer size (%d bytes)", buf_size);
|
2013-07-29 05:44:48 +00:00
|
|
|
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
|
|
|
|
}
|
2013-11-26 16:26:44 +00:00
|
|
|
error_create_buffer:
|
|
|
|
{
|
|
|
|
GST_ERROR ("failed to create output buffer of size %d", buf_size);
|
2013-07-29 05:44:48 +00:00
|
|
|
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
|
|
|
|
}
|
2013-11-26 16:26:44 +00:00
|
|
|
error_copy_buffer:
|
|
|
|
{
|
|
|
|
GST_ERROR ("failed to copy GstVaapiCodedBuffer data");
|
|
|
|
gst_buffer_unref (buf);
|
2013-07-29 05:44:48 +00:00
|
|
|
return GST_VAAPI_ENCODE_FLOW_MEM_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-09 17:10:35 +00:00
|
|
|
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);
|
|
|
|
|
2014-10-29 13:46:12 +00:00
|
|
|
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;
|
|
|
|
}
|
2014-01-09 17:10:35 +00:00
|
|
|
|
|
|
|
if (!gst_video_encoder_negotiate (venc))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
encode->input_state_changed = FALSE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
static GstFlowReturn
|
2013-11-28 14:14:43 +00:00
|
|
|
gst_vaapiencode_push_frame (GstVaapiEncode * encode, gint64 timeout)
|
2013-07-29 05:44:48 +00:00
|
|
|
{
|
|
|
|
GstVideoEncoder *const venc = GST_VIDEO_ENCODER_CAST (encode);
|
2013-11-28 12:57:54 +00:00
|
|
|
GstVaapiEncodeClass *const klass = GST_VAAPIENCODE_GET_CLASS (encode);
|
2013-12-04 16:55:18 +00:00
|
|
|
GstVideoCodecFrame *out_frame;
|
2013-12-03 15:11:46 +00:00
|
|
|
GstVaapiCodedBufferProxy *codedbuf_proxy = NULL;
|
2013-11-28 12:57:54 +00:00
|
|
|
GstVaapiEncoderStatus status;
|
|
|
|
GstBuffer *out_buffer;
|
2013-07-29 05:44:48 +00:00
|
|
|
GstFlowReturn ret;
|
|
|
|
|
2013-12-04 16:55:18 +00:00
|
|
|
status = gst_vaapi_encoder_get_buffer_with_timeout (encode->encoder,
|
|
|
|
&codedbuf_proxy, timeout);
|
2013-12-03 16:04:43 +00:00
|
|
|
if (status == GST_VAAPI_ENCODER_STATUS_NO_BUFFER)
|
2013-07-29 05:44:48 +00:00
|
|
|
return GST_VAAPI_ENCODE_FLOW_TIMEOUT;
|
2013-11-28 14:14:43 +00:00
|
|
|
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
|
|
|
|
goto error_get_buffer;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2013-12-04 16:55:18 +00:00
|
|
|
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);
|
2013-07-29 05:44:48 +00:00
|
|
|
gst_video_codec_frame_set_user_data (out_frame, NULL, NULL);
|
|
|
|
|
2014-01-09 17:10:35 +00:00
|
|
|
/* Update output state */
|
|
|
|
GST_VIDEO_ENCODER_STREAM_LOCK (encode);
|
|
|
|
if (!ensure_output_state (encode))
|
|
|
|
goto error_output_state;
|
|
|
|
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
|
|
|
|
|
2013-11-28 14:14:43 +00:00
|
|
|
/* Allocate and copy buffer into system memory */
|
|
|
|
out_buffer = NULL;
|
2014-01-09 09:09:38 +00:00
|
|
|
ret = klass->alloc_buffer (encode,
|
2013-12-03 15:11:46 +00:00
|
|
|
GST_VAAPI_CODED_BUFFER_PROXY_BUFFER (codedbuf_proxy), &out_buffer);
|
2017-08-10 01:32:13 +00:00
|
|
|
|
2013-12-03 15:11:46 +00:00
|
|
|
gst_vaapi_coded_buffer_proxy_replace (&codedbuf_proxy, NULL);
|
2013-07-29 05:44:48 +00:00
|
|
|
if (ret != GST_FLOW_OK)
|
2013-11-28 14:14:43 +00:00
|
|
|
goto error_allocate_buffer;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2013-11-28 14:14:43 +00:00
|
|
|
gst_buffer_replace (&out_frame->output_buffer, out_buffer);
|
|
|
|
gst_buffer_unref (out_buffer);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2016-07-05 16:23:22 +00:00
|
|
|
GST_TRACE_OBJECT (encode, "output:%" GST_TIME_FORMAT ", size:%zu",
|
2013-11-28 12:57:54 +00:00
|
|
|
GST_TIME_ARGS (out_frame->pts), gst_buffer_get_size (out_buffer));
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2013-11-28 14:14:43 +00:00
|
|
|
return gst_video_encoder_finish_frame (venc, out_frame);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2013-11-28 14:14:43 +00:00
|
|
|
/* ERRORS */
|
|
|
|
error_get_buffer:
|
|
|
|
{
|
|
|
|
GST_ERROR ("failed to get encoded buffer (status %d)", status);
|
2013-12-03 15:11:46 +00:00
|
|
|
if (codedbuf_proxy)
|
|
|
|
gst_vaapi_coded_buffer_proxy_unref (codedbuf_proxy);
|
2013-11-28 14:14:43 +00:00
|
|
|
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);
|
2013-12-04 16:55:18 +00:00
|
|
|
gst_video_codec_frame_unref (out_frame);
|
2013-11-28 14:14:43 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2014-01-09 17:10:35 +00:00
|
|
|
error_output_state:
|
2013-11-28 14:14:43 +00:00
|
|
|
{
|
2014-01-14 15:33:04 +00:00
|
|
|
GST_ERROR ("failed to negotiate output state (status %d)", status);
|
2013-11-28 14:14:43 +00:00
|
|
|
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
|
2013-07-29 05:44:48 +00:00
|
|
|
gst_video_codec_frame_unref (out_frame);
|
2014-01-09 17:10:35 +00:00
|
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
2013-11-28 14:14:43 +00:00
|
|
|
}
|
2013-07-29 05:44:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2016-07-06 02:17:23 +00:00
|
|
|
GST_LOG_OBJECT (encode, "pausing task, reason %s", gst_flow_get_name (ret));
|
2014-01-09 17:20:24 +00:00
|
|
|
gst_pad_pause_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode));
|
2013-07-29 05:44:48 +00:00
|
|
|
}
|
|
|
|
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
static GArray *
|
|
|
|
get_profiles (GstVaapiEncode * encode)
|
2019-08-16 17:28:27 +00:00
|
|
|
{
|
|
|
|
GstVaapiEncodeClass *klass = GST_VAAPIENCODE_GET_CLASS (encode);
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
GArray *profiles = NULL;
|
2019-08-16 17:28:27 +00:00
|
|
|
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
if (klass->get_allowed_profiles) {
|
2019-08-16 17:28:27 +00:00
|
|
|
GstCaps *allowed =
|
|
|
|
gst_pad_get_allowed_caps (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode));
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
GST_LOG_OBJECT (encode,
|
|
|
|
"Get allowed sink caps from downstream %" GST_PTR_FORMAT, allowed);
|
|
|
|
if (allowed && !gst_caps_is_empty (allowed) && !gst_caps_is_any (allowed))
|
|
|
|
profiles = klass->get_allowed_profiles (encode, allowed);
|
2019-08-16 17:28:27 +00:00
|
|
|
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
if (allowed)
|
2019-08-16 17:28:27 +00:00
|
|
|
gst_caps_unref (allowed);
|
|
|
|
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
if (profiles)
|
|
|
|
return profiles;
|
2019-08-16 17:28:27 +00:00
|
|
|
}
|
|
|
|
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
profiles = gst_vaapi_encoder_get_available_profiles (encode->encoder);
|
|
|
|
return profiles;
|
2019-08-16 17:28:27 +00:00
|
|
|
}
|
|
|
|
|
2016-12-01 09:57:10 +00:00
|
|
|
static gboolean
|
|
|
|
ensure_allowed_sinkpad_caps (GstVaapiEncode * encode)
|
|
|
|
{
|
plugin: encode: Refine encode's sink caps.
The old manner to get the encode's sink caps is not correct.
Such as 264 encode, it gets:
video/x-raw(memory:VASurface),
format=(string){ ENCODED, NV12, I420, YV12, YUY2, UYVY, Y210,
P010_10LE, AYUV, Y410, Y444 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ];
video/x-raw(memory:DMABuf), format=(string){ I420, YV12, RGBA },
width=(int)[ 32, 4096 ], height=(int)[ 32, 4096 ],
framerate=(fraction)[ 0/1, 2147483647/1 ];
video/x-raw, format=(string){ NV12 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
where the formats for memory:VASurface and memory:DMABuf are superfluous.
All the "I420, YV12, YUY2, UYVY, Y210, RGBA" can not be really used as
input format for encoder.
We should get:
video/x-raw, format=(string){ NV12 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ];
video/x-raw(memory:VASurface), format=(string){ NV12 },
width=(int)[ 32, 4096 ], height=(int)[ 32, 4096 ],
framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw(memory:DMABuf),
format=(string){ NV12 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
as the correct result.
2019-12-05 16:21:12 +00:00
|
|
|
GstCaps *out_caps = NULL;
|
|
|
|
GstCaps *raw_caps = NULL;
|
|
|
|
GstCaps *va_caps, *dma_caps;
|
2016-12-01 09:57:10 +00:00
|
|
|
GArray *formats = NULL;
|
|
|
|
gboolean ret = FALSE;
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
GArray *profiles = NULL;
|
2019-08-16 17:35:58 +00:00
|
|
|
guint i, size;
|
|
|
|
GstStructure *structure;
|
2019-12-30 06:09:17 +00:00
|
|
|
gint min_width, min_height, max_width, max_height;
|
2020-01-15 22:04:08 +00:00
|
|
|
guint mem_types;
|
2016-12-01 09:57:10 +00:00
|
|
|
|
|
|
|
if (encode->allowed_sinkpad_caps)
|
|
|
|
return TRUE;
|
|
|
|
if (!encode->encoder)
|
|
|
|
return TRUE;
|
|
|
|
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
/* First, get all possible profiles. */
|
|
|
|
profiles = get_profiles (encode);
|
|
|
|
if (profiles == NULL)
|
|
|
|
goto failed_get_profiles;
|
2019-08-16 17:28:27 +00:00
|
|
|
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
/* Then get all supported formats, all these formats should be recognized
|
plugin: encode: Refine encode's sink caps.
The old manner to get the encode's sink caps is not correct.
Such as 264 encode, it gets:
video/x-raw(memory:VASurface),
format=(string){ ENCODED, NV12, I420, YV12, YUY2, UYVY, Y210,
P010_10LE, AYUV, Y410, Y444 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ];
video/x-raw(memory:DMABuf), format=(string){ I420, YV12, RGBA },
width=(int)[ 32, 4096 ], height=(int)[ 32, 4096 ],
framerate=(fraction)[ 0/1, 2147483647/1 ];
video/x-raw, format=(string){ NV12 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
where the formats for memory:VASurface and memory:DMABuf are superfluous.
All the "I420, YV12, YUY2, UYVY, Y210, RGBA" can not be really used as
input format for encoder.
We should get:
video/x-raw, format=(string){ NV12 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ];
video/x-raw(memory:VASurface), format=(string){ NV12 },
width=(int)[ 32, 4096 ], height=(int)[ 32, 4096 ],
framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw(memory:DMABuf),
format=(string){ NV12 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
as the correct result.
2019-12-05 16:21:12 +00:00
|
|
|
in video-format map. */
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
formats = gst_vaapi_encoder_get_surface_attributes (encode->encoder, profiles,
|
2020-01-15 22:04:08 +00:00
|
|
|
&min_width, &min_height, &max_width, &max_height, &mem_types);
|
2016-12-01 09:57:10 +00:00
|
|
|
if (!formats)
|
2019-12-30 06:09:17 +00:00
|
|
|
goto failed_get_attributes;
|
2016-12-01 09:57:10 +00:00
|
|
|
|
|
|
|
raw_caps = gst_vaapi_video_format_new_template_caps_from_list (formats);
|
|
|
|
if (!raw_caps)
|
|
|
|
goto failed_create_raw_caps;
|
|
|
|
|
2019-12-30 06:09:17 +00:00
|
|
|
/* Set the width/height info to caps */
|
|
|
|
size = gst_caps_get_size (raw_caps);
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
structure = gst_caps_get_structure (raw_caps, i);
|
|
|
|
if (!structure)
|
|
|
|
continue;
|
|
|
|
gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, min_width,
|
|
|
|
max_width, "height", GST_TYPE_INT_RANGE, min_height, max_height, NULL);
|
|
|
|
}
|
|
|
|
|
2020-01-15 22:04:08 +00:00
|
|
|
out_caps = gst_caps_copy (raw_caps);
|
|
|
|
|
plugin: encode: Refine encode's sink caps.
The old manner to get the encode's sink caps is not correct.
Such as 264 encode, it gets:
video/x-raw(memory:VASurface),
format=(string){ ENCODED, NV12, I420, YV12, YUY2, UYVY, Y210,
P010_10LE, AYUV, Y410, Y444 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ];
video/x-raw(memory:DMABuf), format=(string){ I420, YV12, RGBA },
width=(int)[ 32, 4096 ], height=(int)[ 32, 4096 ],
framerate=(fraction)[ 0/1, 2147483647/1 ];
video/x-raw, format=(string){ NV12 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
where the formats for memory:VASurface and memory:DMABuf are superfluous.
All the "I420, YV12, YUY2, UYVY, Y210, RGBA" can not be really used as
input format for encoder.
We should get:
video/x-raw, format=(string){ NV12 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ];
video/x-raw(memory:VASurface), format=(string){ NV12 },
width=(int)[ 32, 4096 ], height=(int)[ 32, 4096 ],
framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw(memory:DMABuf),
format=(string){ NV12 }, width=(int)[ 32, 4096 ],
height=(int)[ 32, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
as the correct result.
2019-12-05 16:21:12 +00:00
|
|
|
va_caps = gst_caps_copy (raw_caps);
|
|
|
|
gst_caps_set_features_simple (va_caps,
|
|
|
|
gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE));
|
|
|
|
gst_caps_append (out_caps, va_caps);
|
2020-01-15 22:04:08 +00:00
|
|
|
|
|
|
|
if (gst_vaapi_mem_type_supports (mem_types,
|
|
|
|
GST_VAAPI_BUFFER_MEMORY_TYPE_DMA_BUF)) {
|
|
|
|
dma_caps = gst_caps_copy (raw_caps);
|
|
|
|
gst_caps_set_features_simple (dma_caps,
|
|
|
|
gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_DMABUF));
|
|
|
|
gst_caps_append (out_caps, dma_caps);
|
|
|
|
}
|
2019-08-16 17:35:58 +00:00
|
|
|
|
2016-12-01 09:57:10 +00:00
|
|
|
gst_caps_replace (&encode->allowed_sinkpad_caps, out_caps);
|
2017-03-31 09:21:21 +00:00
|
|
|
GST_INFO_OBJECT (encode, "Allowed sink caps %" GST_PTR_FORMAT,
|
|
|
|
encode->allowed_sinkpad_caps);
|
|
|
|
|
2016-12-01 09:57:10 +00:00
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
bail:
|
2019-12-22 06:35:18 +00:00
|
|
|
if (!encode->allowed_sinkpad_caps)
|
|
|
|
encode->allowed_sinkpad_caps = gst_caps_new_empty ();
|
|
|
|
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
if (profiles)
|
|
|
|
g_array_unref (profiles);
|
2016-12-01 09:57:10 +00:00
|
|
|
if (out_caps)
|
|
|
|
gst_caps_unref (out_caps);
|
|
|
|
if (raw_caps)
|
|
|
|
gst_caps_unref (raw_caps);
|
|
|
|
if (formats)
|
|
|
|
g_array_unref (formats);
|
|
|
|
return ret;
|
|
|
|
|
2019-12-30 06:09:17 +00:00
|
|
|
failed_get_attributes:
|
2016-12-01 09:57:10 +00:00
|
|
|
{
|
2019-12-30 06:09:17 +00:00
|
|
|
GST_WARNING_OBJECT (encode, "failed to get surface attributes");
|
2016-12-01 09:57:10 +00:00
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
failed_create_raw_caps:
|
|
|
|
{
|
|
|
|
GST_WARNING_OBJECT (encode, "failed to create raw sink caps");
|
|
|
|
goto bail;
|
|
|
|
}
|
plugin: encode: List all possible profiles to detect input formats.
The current get_profile just return one possible profile for the encode,
which is not enough. For example, if we want to support HEVC 4:4:4
profile, the input of encode should be VYUA rather than NV12 in HEVC
main profile. So the command line:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! fakesink
can not work because vaapih265enc just report NV12 in sink caps, we need
to specify the profile obviously like:
gst-launch-1.0 videotestsrc num-buffers=200 ! capsfilter \
caps=video/x-raw,format=VUYA,width=800,height=600 ! vaapih265enc \
tune=low-power init-qp=30 ! capsfilter caps=video/x-h265, \
profile=main-444 ! fakesink
The encode should have the ability to choose the profile based on input
format automatically. If the input video format is VUYA, the main-444
profile should be auto choosed.
We modify to let get_allowed_profiles of each encode sub class to return
an array of all supported profiles based on downstream's allowed caps, or
return NULL if no valid profiles specified by downstream.
If no allowed profiles found, all profiles which belong to the current
encoder's codec will be the candidates.
The function gst_vaapi_encoder_get_surface_attributes collects the surface's
attributes for that profile list we just get.
So for this case, both NV12 and VUYA should be returned.
TODO: some codec like VP9, need to implement the get_profile() function.
2019-12-23 06:29:08 +00:00
|
|
|
failed_get_profiles:
|
|
|
|
{
|
|
|
|
GST_WARNING_OBJECT (encode, "failed to get supported profiles");
|
|
|
|
goto bail;
|
|
|
|
}
|
2016-12-01 09:57:10 +00:00
|
|
|
}
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
static GstCaps *
|
2016-03-08 09:47:56 +00:00
|
|
|
gst_vaapiencode_get_caps (GstVideoEncoder * venc, GstCaps * filter)
|
2013-07-29 05:44:48 +00:00
|
|
|
{
|
2016-12-01 09:57:10 +00:00
|
|
|
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
|
2016-03-08 09:47:56 +00:00
|
|
|
GstCaps *result;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2016-12-01 09:57:10 +00:00
|
|
|
ensure_allowed_sinkpad_caps (encode);
|
|
|
|
result = gst_video_encoder_proxy_getcaps (venc, encode->allowed_sinkpad_caps,
|
|
|
|
filter);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2017-03-31 09:21:21 +00:00
|
|
|
GST_DEBUG_OBJECT (venc, "Negotiated sink caps %" GST_PTR_FORMAT, result);
|
2016-03-08 09:47:56 +00:00
|
|
|
return result;
|
2013-11-26 14:12:59 +00:00
|
|
|
}
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
static gboolean
|
|
|
|
gst_vaapiencode_destroy (GstVaapiEncode * encode)
|
|
|
|
{
|
2014-01-09 17:10:35 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-12-01 09:57:10 +00:00
|
|
|
|
|
|
|
gst_caps_replace (&encode->allowed_sinkpad_caps, NULL);
|
2013-07-29 05:44:48 +00:00
|
|
|
gst_vaapi_encoder_replace (&encode->encoder, NULL);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2016-07-06 02:17:23 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
static gboolean
|
|
|
|
ensure_encoder (GstVaapiEncode * encode)
|
|
|
|
{
|
|
|
|
GstVaapiEncodeClass *klass = GST_VAAPIENCODE_GET_CLASS (encode);
|
2014-01-08 17:36:46 +00:00
|
|
|
guint i;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2014-01-09 09:09:38 +00:00
|
|
|
g_return_val_if_fail (klass->alloc_encoder, FALSE);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2016-07-06 02:17:23 +00:00
|
|
|
if (encode->encoder)
|
|
|
|
return FALSE;
|
|
|
|
|
2014-01-09 09:09:38 +00:00
|
|
|
encode->encoder = klass->alloc_encoder (encode,
|
2013-12-13 09:24:26 +00:00
|
|
|
GST_VAAPI_PLUGIN_BASE_DISPLAY (encode));
|
2013-11-26 15:34:14 +00:00
|
|
|
if (!encode->encoder)
|
|
|
|
return FALSE;
|
2014-01-06 14:10:36 +00:00
|
|
|
|
2019-08-20 15:56:33 +00:00
|
|
|
if (encode->prop_values && encode->prop_values->len) {
|
|
|
|
for (i = 0; i < encode->prop_values->len; i++) {
|
|
|
|
PropValue *const prop_value = g_ptr_array_index (encode->prop_values, i);
|
|
|
|
g_object_set_property ((GObject *) encode->encoder,
|
|
|
|
g_param_spec_get_name (prop_value->pspec), &prop_value->value);
|
2014-01-08 17:36:46 +00:00
|
|
|
}
|
2019-08-20 15:56:33 +00:00
|
|
|
/* clear alll the cache */
|
|
|
|
g_ptr_array_unref (encode->prop_values);
|
|
|
|
encode->prop_values = NULL;
|
2014-01-08 17:36:46 +00:00
|
|
|
}
|
2019-08-20 15:56:33 +00:00
|
|
|
|
2013-11-26 15:34:14 +00:00
|
|
|
return TRUE;
|
2013-07-29 05:44:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_vaapiencode_open (GstVideoEncoder * venc)
|
|
|
|
{
|
2013-11-26 15:34:14 +00:00
|
|
|
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
|
2013-12-13 09:24:26 +00:00
|
|
|
GstVaapiDisplay *const old_display = GST_VAAPI_PLUGIN_BASE_DISPLAY (encode);
|
2013-07-29 05:44:48 +00:00
|
|
|
gboolean success;
|
|
|
|
|
2013-12-13 09:24:26 +00:00
|
|
|
if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (encode)))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
GST_VAAPI_PLUGIN_BASE_DISPLAY (encode) = NULL;
|
2013-07-29 05:44:48 +00:00
|
|
|
success = ensure_display (encode);
|
|
|
|
if (old_display)
|
2018-06-13 15:54:23 +00:00
|
|
|
gst_object_unref (old_display);
|
2013-07-29 05:44:48 +00:00
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2016-11-29 05:59:02 +00:00
|
|
|
static gboolean
|
|
|
|
gst_vaapiencode_start (GstVideoEncoder * venc)
|
|
|
|
{
|
|
|
|
return ensure_encoder (GST_VAAPIENCODE_CAST (venc));
|
|
|
|
}
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
static gboolean
|
2016-11-29 06:14:32 +00:00
|
|
|
gst_vaapiencode_stop (GstVideoEncoder * venc)
|
2013-07-29 05:44:48 +00:00
|
|
|
{
|
2016-11-29 06:14:32 +00:00
|
|
|
return gst_vaapiencode_destroy (GST_VAAPIENCODE_CAST (venc));
|
|
|
|
}
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2016-11-29 06:14:32 +00:00
|
|
|
static gboolean
|
|
|
|
gst_vaapiencode_close (GstVideoEncoder * venc)
|
|
|
|
{
|
|
|
|
gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (venc));
|
2013-12-13 09:24:26 +00:00
|
|
|
return TRUE;
|
2013-07-29 05:44:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2014-01-09 17:10:35 +00:00
|
|
|
set_codec_state (GstVaapiEncode * encode, GstVideoCodecState * state)
|
2013-07-29 05:44:48 +00:00
|
|
|
{
|
2014-01-10 09:54:22 +00:00
|
|
|
GstVaapiEncodeClass *const klass = GST_VAAPIENCODE_GET_CLASS (encode);
|
|
|
|
GstVaapiEncoderStatus status;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
|
|
|
g_return_val_if_fail (encode->encoder, FALSE);
|
|
|
|
|
2014-01-10 09:54:22 +00:00
|
|
|
/* Initialize codec specific parameters */
|
|
|
|
if (klass->set_config && !klass->set_config (encode))
|
2013-07-29 05:44:48 +00:00
|
|
|
return FALSE;
|
|
|
|
|
2014-01-10 09:54:22 +00:00
|
|
|
status = gst_vaapi_encoder_set_codec_state (encode->encoder, state);
|
|
|
|
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
|
|
|
|
return FALSE;
|
2013-07-29 05:44:48 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2017-09-25 15:04:12 +00:00
|
|
|
static gboolean
|
|
|
|
gst_vaapiencode_drain (GstVaapiEncode * encode)
|
|
|
|
{
|
|
|
|
GstVaapiEncoderStatus status;
|
|
|
|
|
|
|
|
if (!encode->encoder)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
status = gst_vaapi_encoder_flush (encode->encoder);
|
|
|
|
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
|
|
|
|
return FALSE;
|
|
|
|
gst_vaapiencode_purge (encode);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
static gboolean
|
|
|
|
gst_vaapiencode_set_format (GstVideoEncoder * venc, GstVideoCodecState * state)
|
|
|
|
{
|
2013-11-26 15:34:14 +00:00
|
|
|
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
|
|
|
g_return_val_if_fail (state->caps != NULL, FALSE);
|
|
|
|
|
2014-01-09 17:10:35 +00:00
|
|
|
if (!set_codec_state (encode, state))
|
2013-07-29 05:44:48 +00:00
|
|
|
return FALSE;
|
2014-01-09 17:10:35 +00:00
|
|
|
|
2013-12-17 17:52:23 +00:00
|
|
|
if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (encode),
|
2014-01-09 17:10:35 +00:00
|
|
|
state->caps, NULL))
|
2013-12-17 17:52:23 +00:00
|
|
|
return FALSE;
|
|
|
|
|
2017-09-25 15:04:12 +00:00
|
|
|
if (!gst_vaapiencode_drain (encode))
|
|
|
|
return FALSE;
|
|
|
|
|
2014-01-09 17:10:35 +00:00
|
|
|
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;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2017-02-16 17:37:59 +00:00
|
|
|
/* 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;
|
2013-07-29 05:44:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static GstFlowReturn
|
|
|
|
gst_vaapiencode_handle_frame (GstVideoEncoder * venc,
|
|
|
|
GstVideoCodecFrame * frame)
|
|
|
|
{
|
2013-11-26 15:34:14 +00:00
|
|
|
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
|
2018-11-12 12:39:51 +00:00
|
|
|
GstPad *const srcpad = GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode);
|
2013-11-28 14:56:53 +00:00
|
|
|
GstVaapiEncoderStatus status;
|
|
|
|
GstVaapiVideoMeta *meta;
|
|
|
|
GstVaapiSurfaceProxy *proxy;
|
|
|
|
GstFlowReturn ret;
|
2013-11-26 14:31:03 +00:00
|
|
|
GstBuffer *buf;
|
2018-11-12 12:39:51 +00:00
|
|
|
GstTaskState task_state;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2018-11-12 12:39:51 +00:00
|
|
|
task_state = gst_pad_get_task_state (srcpad);
|
|
|
|
if (task_state == GST_TASK_STOPPED || task_state == GST_TASK_PAUSED)
|
|
|
|
if (!gst_pad_start_task (srcpad,
|
|
|
|
(GstTaskFunction) gst_vaapiencode_buffer_loop, encode, NULL))
|
|
|
|
goto error_task_failed;
|
|
|
|
|
2013-11-28 14:56:53 +00:00
|
|
|
buf = NULL;
|
2013-12-17 17:52:23 +00:00
|
|
|
ret = gst_vaapi_plugin_base_get_input_buffer (GST_VAAPI_PLUGIN_BASE (encode),
|
|
|
|
frame->input_buffer, &buf);
|
2013-11-26 14:31:03 +00:00
|
|
|
if (ret != GST_FLOW_OK)
|
2013-11-28 14:56:53 +00:00
|
|
|
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;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2013-11-28 14:56:53 +00:00
|
|
|
proxy = gst_vaapi_video_meta_get_surface_proxy (meta);
|
|
|
|
if (!proxy)
|
|
|
|
goto error_buffer_no_surface_proxy;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2013-11-28 14:56:53 +00:00
|
|
|
gst_video_codec_frame_set_user_data (frame,
|
|
|
|
gst_vaapi_surface_proxy_ref (proxy),
|
2014-01-03 15:57:25 +00:00
|
|
|
(GDestroyNotify) gst_vaapi_surface_proxy_unref);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
|
|
|
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
|
2013-11-28 12:57:54 +00:00
|
|
|
status = gst_vaapi_encoder_put_frame (encode->encoder, frame);
|
2013-07-29 05:44:48 +00:00
|
|
|
GST_VIDEO_ENCODER_STREAM_LOCK (encode);
|
2013-11-28 14:56:53 +00:00
|
|
|
if (status < GST_VAAPI_ENCODER_STATUS_SUCCESS)
|
|
|
|
goto error_encode_frame;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
|
|
|
gst_video_codec_frame_unref (frame);
|
2013-11-28 14:56:53 +00:00
|
|
|
return GST_FLOW_OK;
|
|
|
|
|
|
|
|
/* ERRORS */
|
2018-11-12 12:39:51 +00:00
|
|
|
error_task_failed:
|
|
|
|
{
|
|
|
|
GST_ELEMENT_ERROR (venc, RESOURCE, FAILED,
|
|
|
|
("Failed to start encoding thread."), (NULL));
|
|
|
|
gst_video_codec_frame_unref (frame);
|
|
|
|
return GST_FLOW_ERROR;
|
|
|
|
}
|
2013-11-28 14:56:53 +00:00
|
|
|
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;
|
|
|
|
}
|
2013-07-29 05:44:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static GstFlowReturn
|
|
|
|
gst_vaapiencode_finish (GstVideoEncoder * venc)
|
|
|
|
{
|
2013-11-26 15:34:14 +00:00
|
|
|
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc);
|
2013-07-29 05:44:48 +00:00
|
|
|
GstVaapiEncoderStatus status;
|
|
|
|
GstFlowReturn ret = GST_FLOW_OK;
|
|
|
|
|
2014-01-13 16:18:42 +00:00
|
|
|
/* 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;
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
status = gst_vaapi_encoder_flush (encode->encoder);
|
|
|
|
|
|
|
|
GST_VIDEO_ENCODER_STREAM_UNLOCK (encode);
|
2014-01-09 17:20:24 +00:00
|
|
|
gst_pad_stop_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode));
|
2013-07-29 05:44:48 +00:00
|
|
|
GST_VIDEO_ENCODER_STREAM_LOCK (encode);
|
|
|
|
|
|
|
|
while (status == GST_VAAPI_ENCODER_STATUS_SUCCESS && ret == GST_FLOW_OK)
|
|
|
|
ret = gst_vaapiencode_push_frame (encode, 0);
|
|
|
|
|
2013-11-26 16:11:22 +00:00
|
|
|
if (ret == GST_VAAPI_ENCODE_FLOW_TIMEOUT)
|
|
|
|
ret = GST_FLOW_OK;
|
2013-07-29 05:44:48 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-12-17 09:23:42 +00:00
|
|
|
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));
|
2019-05-28 09:09:36 +00:00
|
|
|
|
|
|
|
if (!gst_vaapiencode_drain (encode))
|
|
|
|
goto drain_error;
|
2013-12-17 09:23:42 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2015-06-30 06:44:18 +00:00
|
|
|
return
|
|
|
|
GST_ELEMENT_CLASS (gst_vaapiencode_parent_class)->change_state (element,
|
|
|
|
transition);
|
2019-05-28 09:09:36 +00:00
|
|
|
|
|
|
|
drain_error:
|
|
|
|
{
|
|
|
|
GST_ERROR ("failed to drain pending encoded frames");
|
|
|
|
return GST_STATE_CHANGE_FAILURE;
|
|
|
|
}
|
2013-12-17 09:23:42 +00:00
|
|
|
}
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
static gboolean
|
|
|
|
gst_vaapiencode_propose_allocation (GstVideoEncoder * venc, GstQuery * query)
|
|
|
|
{
|
2013-12-17 17:52:23 +00:00
|
|
|
GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (venc);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2013-12-17 17:52:23 +00:00
|
|
|
if (!gst_vaapi_plugin_base_propose_allocation (plugin, query))
|
2013-07-29 05:44:48 +00:00
|
|
|
return FALSE;
|
2013-12-17 17:52:23 +00:00
|
|
|
return TRUE;
|
2013-07-29 05:44:48 +00:00
|
|
|
}
|
|
|
|
|
2016-07-06 02:17:23 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
if (!encode->encoder)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
GST_LOG_OBJECT (encode, "flushing");
|
|
|
|
|
2017-09-25 15:04:12 +00:00
|
|
|
if (!gst_vaapiencode_drain (encode))
|
2016-07-06 02:17:23 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
static void
|
|
|
|
gst_vaapiencode_finalize (GObject * object)
|
|
|
|
{
|
2013-11-26 15:34:14 +00:00
|
|
|
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (object);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
|
|
|
gst_vaapiencode_destroy (encode);
|
|
|
|
|
2014-01-08 17:36:46 +00:00
|
|
|
if (encode->prop_values) {
|
|
|
|
g_ptr_array_unref (encode->prop_values);
|
|
|
|
encode->prop_values = NULL;
|
|
|
|
}
|
|
|
|
|
2013-12-13 09:24:26 +00:00
|
|
|
gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (object));
|
2013-07-29 05:44:48 +00:00
|
|
|
G_OBJECT_CLASS (gst_vaapiencode_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_vaapiencode_init (GstVaapiEncode * encode)
|
|
|
|
{
|
2014-01-09 17:20:24 +00:00
|
|
|
GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (encode);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2014-01-09 17:20:24 +00:00
|
|
|
gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (encode), GST_CAT_DEFAULT);
|
2019-10-29 22:13:44 +00:00
|
|
|
gst_pad_use_fixed_caps (GST_VAAPI_PLUGIN_BASE_SRC_PAD (plugin));
|
2013-07-29 05:44:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_vaapiencode_class_init (GstVaapiEncodeClass * klass)
|
|
|
|
{
|
|
|
|
GObjectClass *const object_class = G_OBJECT_CLASS (klass);
|
2013-12-17 09:23:42 +00:00
|
|
|
GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
|
2013-07-29 05:44:48 +00:00
|
|
|
GstVideoEncoderClass *const venc_class = GST_VIDEO_ENCODER_CLASS (klass);
|
|
|
|
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_vaapiencode_debug,
|
|
|
|
GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
|
|
|
|
|
2013-12-13 09:24:26 +00:00
|
|
|
gst_vaapi_plugin_base_class_init (GST_VAAPI_PLUGIN_BASE_CLASS (klass));
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
object_class->finalize = gst_vaapiencode_finalize;
|
|
|
|
|
2016-04-21 10:57:30 +00:00
|
|
|
element_class->set_context = gst_vaapi_base_set_context;
|
2013-12-17 09:23:42 +00:00
|
|
|
element_class->change_state =
|
|
|
|
GST_DEBUG_FUNCPTR (gst_vaapiencode_change_state);
|
|
|
|
|
2013-07-29 05:44:48 +00:00
|
|
|
venc_class->open = GST_DEBUG_FUNCPTR (gst_vaapiencode_open);
|
|
|
|
venc_class->close = GST_DEBUG_FUNCPTR (gst_vaapiencode_close);
|
2016-11-29 05:59:02 +00:00
|
|
|
venc_class->start = GST_DEBUG_FUNCPTR (gst_vaapiencode_start);
|
2016-11-29 06:14:32 +00:00
|
|
|
venc_class->stop = GST_DEBUG_FUNCPTR (gst_vaapiencode_stop);
|
2013-07-29 05:44:48 +00:00
|
|
|
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);
|
2013-11-26 14:12:59 +00:00
|
|
|
venc_class->getcaps = GST_DEBUG_FUNCPTR (gst_vaapiencode_get_caps);
|
2013-07-29 05:44:48 +00:00
|
|
|
venc_class->propose_allocation =
|
|
|
|
GST_DEBUG_FUNCPTR (gst_vaapiencode_propose_allocation);
|
2016-07-06 02:17:23 +00:00
|
|
|
venc_class->flush = GST_DEBUG_FUNCPTR (gst_vaapiencode_flush);
|
|
|
|
venc_class->sink_event = GST_DEBUG_FUNCPTR (gst_vaapiencode_sink_event);
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2014-01-09 09:09:38 +00:00
|
|
|
klass->alloc_buffer = gst_vaapiencode_default_alloc_buffer;
|
2013-07-29 05:44:48 +00:00
|
|
|
|
2015-11-05 11:39:55 +00:00
|
|
|
venc_class->src_query = GST_DEBUG_FUNCPTR (gst_vaapiencode_src_query);
|
|
|
|
venc_class->sink_query = GST_DEBUG_FUNCPTR (gst_vaapiencode_sink_query);
|
2014-01-08 17:36:46 +00:00
|
|
|
}
|
2013-11-28 12:26:40 +00:00
|
|
|
|
2019-08-20 14:16:35 +00:00
|
|
|
/* Only used by the drived class */
|
|
|
|
void
|
|
|
|
gst_vaapiencode_set_property_subclass (GObject * object,
|
|
|
|
guint prop_id, const GValue * value, GParamSpec * pspec)
|
|
|
|
{
|
|
|
|
GstVaapiEncodeClass *const encode_class = GST_VAAPIENCODE_GET_CLASS (object);
|
|
|
|
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (object);
|
|
|
|
PropValue *prop_value;
|
|
|
|
|
|
|
|
if (prop_id <= PROP_BASE || prop_id >= encode_class->prop_num) {
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* direct forward the property to encoder */
|
|
|
|
if (encode->encoder) {
|
|
|
|
g_object_set_property ((GObject *) encode->encoder,
|
|
|
|
g_param_spec_get_name (pspec), value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (encode->prop_values) {
|
|
|
|
/* Delete the same prop already in cache */
|
|
|
|
prop_value = prop_value_lookup_entry (encode->prop_values, prop_id);
|
|
|
|
if (prop_value)
|
|
|
|
g_ptr_array_remove (encode->prop_values, prop_value);
|
|
|
|
} else {
|
|
|
|
encode->prop_values =
|
|
|
|
g_ptr_array_new_with_free_func ((GDestroyNotify) prop_value_free);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The encoder is delay created, we need to cache the property setting */
|
|
|
|
prop_value = prop_value_new_entry (prop_id, pspec, value);
|
|
|
|
g_ptr_array_add (encode->prop_values, prop_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only used by the drived class */
|
|
|
|
void
|
|
|
|
gst_vaapiencode_get_property_subclass (GObject * object, guint prop_id,
|
|
|
|
GValue * value, GParamSpec * pspec)
|
|
|
|
{
|
|
|
|
GstVaapiEncodeClass *const encode_class = GST_VAAPIENCODE_GET_CLASS (object);
|
|
|
|
GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (object);
|
|
|
|
PropValue *prop_value = NULL;
|
|
|
|
|
|
|
|
if (prop_id <= PROP_BASE || prop_id >= encode_class->prop_num) {
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* direct forward the property to encoder */
|
|
|
|
if (encode->encoder) {
|
|
|
|
g_object_get_property ((GObject *) encode->encoder,
|
|
|
|
g_param_spec_get_name (pspec), value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (encode->prop_values)
|
|
|
|
prop_value = prop_value_lookup_entry (encode->prop_values, prop_id);
|
|
|
|
|
|
|
|
if (prop_value) {
|
|
|
|
/* In the cache */
|
|
|
|
g_value_copy (&prop_value->value, value);
|
|
|
|
} else {
|
|
|
|
/* set the default value */
|
|
|
|
g_param_value_set_default (pspec, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called by drived class to install all properties. The encode base class
|
|
|
|
does not have any property, all the properties of the according encoderXXX
|
|
|
|
class are installed to encodeXXX class. */
|
|
|
|
gboolean
|
|
|
|
gst_vaapiencode_class_install_properties (GstVaapiEncodeClass * klass,
|
|
|
|
GObjectClass * encoder_class)
|
|
|
|
{
|
|
|
|
GObjectClass *const object_class = G_OBJECT_CLASS (klass);
|
|
|
|
guint i, n_props, installed;
|
|
|
|
GParamSpec **specs = NULL;
|
|
|
|
GParamSpec *pspec;
|
|
|
|
GParamSpec *new_spec;
|
|
|
|
GParamFlags flags;
|
|
|
|
|
|
|
|
if (encoder_class)
|
|
|
|
specs = g_object_class_list_properties (encoder_class, &n_props);
|
|
|
|
if (!specs)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
installed = 0;
|
|
|
|
for (i = 0; i < n_props; i++) {
|
|
|
|
pspec = specs[i];
|
|
|
|
|
|
|
|
/* Encoder do not want to expose */
|
2019-09-11 12:32:22 +00:00
|
|
|
if (!(pspec->flags & GST_VAAPI_PARAM_ENCODER_EXPOSURE))
|
2019-08-20 14:16:35 +00:00
|
|
|
continue;
|
|
|
|
/* Can only set on encoder init time */
|
|
|
|
if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* filter out the G_PARAM_CONSTRUCT, the encoder created later, no need
|
|
|
|
to set the init value in encode.
|
2019-09-11 12:32:22 +00:00
|
|
|
Also no GST_VAAPI_PARAM_ENCODER_EXPOSURE */
|
|
|
|
flags = pspec->flags & (~(GST_VAAPI_PARAM_ENCODER_EXPOSURE |
|
2019-08-20 14:16:35 +00:00
|
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
|
|
|
|
if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_UINT) {
|
|
|
|
GParamSpecUInt *pspecuint = G_PARAM_SPEC_UINT (pspec);
|
|
|
|
new_spec = g_param_spec_uint (g_param_spec_get_name (pspec),
|
|
|
|
g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
|
|
|
|
pspecuint->minimum, pspecuint->maximum,
|
|
|
|
pspecuint->default_value, flags);
|
|
|
|
} else if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_INT) {
|
|
|
|
GParamSpecInt *pspecint = G_PARAM_SPEC_INT (pspec);
|
|
|
|
new_spec = g_param_spec_int (g_param_spec_get_name (pspec),
|
|
|
|
g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
|
|
|
|
pspecint->minimum, pspecint->maximum, pspecint->default_value, flags);
|
|
|
|
} else if (G_IS_PARAM_SPEC_ENUM (pspec)) {
|
|
|
|
GParamSpecEnum *pspecenum = G_PARAM_SPEC_ENUM (pspec);
|
|
|
|
new_spec = g_param_spec_enum (g_param_spec_get_name (pspec),
|
|
|
|
g_param_spec_get_nick (pspec),
|
|
|
|
g_param_spec_get_blurb (pspec),
|
|
|
|
pspec->value_type, pspecenum->default_value, flags);
|
|
|
|
} else if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) {
|
|
|
|
GParamSpecBoolean *pspecbool = G_PARAM_SPEC_BOOLEAN (pspec);
|
|
|
|
new_spec = g_param_spec_boolean (g_param_spec_get_name (pspec),
|
|
|
|
g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
|
|
|
|
pspecbool->default_value, flags);
|
|
|
|
} else if (G_IS_PARAM_SPEC_FLAGS (pspec)) {
|
|
|
|
GParamSpecFlags *pspecflags = G_PARAM_SPEC_FLAGS (pspec);
|
|
|
|
new_spec = g_param_spec_flags (g_param_spec_get_name (pspec),
|
|
|
|
g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
|
|
|
|
pspec->value_type, pspecflags->default_value, flags);
|
|
|
|
} else if (GST_IS_PARAM_SPEC_ARRAY_LIST (pspec)) {
|
|
|
|
GstParamSpecArray *pspecarray = GST_PARAM_SPEC_ARRAY_LIST (pspec);
|
|
|
|
new_spec = gst_param_spec_array (g_param_spec_get_name (pspec),
|
|
|
|
g_param_spec_get_nick (pspec), g_param_spec_get_blurb (pspec),
|
|
|
|
pspecarray->element_spec, flags);
|
|
|
|
} else {
|
|
|
|
GST_WARNING ("encoder's %s property has a unimplemented"
|
|
|
|
" type to expose to encode, the encode may lose the %s property",
|
|
|
|
g_param_spec_get_name (pspec), g_param_spec_get_name (pspec));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_class_install_property (object_class, PROP_BASE + 1 + installed,
|
|
|
|
new_spec);
|
|
|
|
installed++;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (specs);
|
|
|
|
klass->prop_num = PROP_BASE + 1 + installed;
|
|
|
|
return TRUE;
|
|
|
|
}
|