gstreamer/subprojects/gst-plugins-bad/ext/lcevcencoder/gstlcevcencoder.c

867 lines
25 KiB
C
Raw Normal View History

/* GStreamer
* Copyright (C) <2024> V-Nova International Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#include <gst/codecparsers/gstlcevcmeta.h>
#include <lcevc_eil.h>
#include "gstlcevcencoder.h"
#include "gstlcevcencoderutils.h"
GST_DEBUG_CATEGORY_STATIC (lcevcencoder_debug);
#define GST_CAT_DEFAULT (lcevcencoder_debug)
#define DEFAULT_MIN_BITRATE 0
#define DEFAULT_MAX_BITRATE 2048000
#define DEFAULT_BITRATE 0
#define DEFAULT_SEI_LCEVC TRUE
#define DEFAULT_MIN_GOP_LENGTH -2
#define DEFAULT_GOP_LENGTH -2
#define DEFAULT_DEBUG FALSE
/* The max number of frames the encoder can receive without encoding a frame */
#define MAX_DELAYED_FRAMES 65
struct GstEILContext_
{
grefcount ref;
EILContext context;
};
typedef struct GstEILContext_ GstEILContext;
static void
log_callback (void *userdata, int32_t level, const char *msg)
{
GstLcevcEncoder *eil = GST_LCEVC_ENCODER (userdata);
gchar *new_msg = NULL;
if (strlen (msg) <= 1)
return;
/* Remove \n from msg */
new_msg = g_strdup (msg);
new_msg[strlen (msg) - 1] = '\0';
switch (level) {
case EIL_LL_Error:
GST_ERROR_OBJECT (eil, "EIL: %s", new_msg);
break;
case EIL_LL_Warning:
GST_WARNING_OBJECT (eil, "EIL: %s", new_msg);
break;
case EIL_LL_Info:
GST_INFO_OBJECT (eil, "EIL: %s", new_msg);
break;
case EIL_LL_Debug:
GST_DEBUG_OBJECT (eil, "EIL: %s", new_msg);
break;
case EIL_LL_Verbose:
GST_TRACE_OBJECT (eil, "EIL: %s", new_msg);
break;
default:
break;
}
g_clear_pointer (&new_msg, g_free);
}
static GstEILContext *
gst_eil_context_new (GstLcevcEncoder * eil, const gchar * plugin_name,
gboolean debug)
{
GstEILContext *ctx = g_new0 (GstEILContext, 1);
EILOpenSettings settings;
EILReturnCode rc;
g_return_val_if_fail (plugin_name, NULL);
/* Initialize settings to default values */
rc = EIL_OpenSettingsDefault (&settings);
if (rc != EIL_RC_Success)
return NULL;
/* Set settings */
settings.base_encoder = plugin_name;
if (debug) {
settings.log_callback = log_callback;
settings.log_userdata = eil;
}
/* Open EIL context */
if ((rc = EIL_Open (&settings, &ctx->context)) != EIL_RC_Success)
return NULL;
/* Init refcount */
g_ref_count_init (&ctx->ref);
return ctx;
}
static void
gst_eil_context_unref (GstEILContext * ctx)
{
if (g_ref_count_dec (&ctx->ref)) {
EIL_Close (ctx->context);
g_free (ctx);
}
}
static GstEILContext *
gst_eil_context_ref (GstEILContext * ctx)
{
g_ref_count_inc (&ctx->ref);
return ctx;
}
typedef struct
{
GstEILContext *ctx;
EILOutput *output;
} OutputData;
static OutputData *
output_data_new (GstEILContext * ctx, EILOutput * output)
{
OutputData *data = g_new0 (OutputData, 1);
data->ctx = gst_eil_context_ref (ctx);
data->output = output;
return data;
}
static void
output_data_free (gpointer p)
{
OutputData *data = p;
EIL_ReleaseOutput (data->ctx->context, data->output);
g_clear_pointer (&data->ctx, gst_eil_context_unref);
g_free (data);
}
enum
{
PROP_0,
PROP_PLUGIN_NAME,
PROP_PLUGIN_PROPS,
PROP_BITRATE,
PROP_SEI_LCEVC,
PROP_GOP_LENGTH,
PROP_DEBUG,
};
typedef struct
{
/* Props */
gchar *plugin_name;
gchar *plugin_props;
guint bitrate;
gboolean sei_lcevc;
gint gop_length;
gboolean debug;
/* EIL */
GstEILContext *ctx;
GHashTable *plugin_props_spec;
/* Input info */
GstVideoInfo in_info;
EILColourFormat in_format;
EILFrameType in_frame_type;
GstClockTime out_ts_offset;
} GstLcevcEncoderPrivate;
#define gst_lcevc_encoder_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstLcevcEncoder, gst_lcevc_encoder,
GST_TYPE_VIDEO_ENCODER,
G_ADD_PRIVATE (GstLcevcEncoder);
GST_DEBUG_CATEGORY_INIT (lcevcencoder_debug, "lcevcencoder", 0,
"lcevcencoder"));
static GstStaticPadTemplate gst_lcevc_encoder_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
(GST_LCEVC_ENCODER_UTILS_SUPPORTED_FORMATS))
);
static void
gst_lcevc_encoder_finalize (GObject * obj)
{
GstLcevcEncoder *eil = GST_LCEVC_ENCODER (obj);
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
g_clear_pointer (&priv->plugin_name, g_free);
G_OBJECT_CLASS (parent_class)->finalize (obj);
}
static void
gst_lcevc_encoder_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstLcevcEncoder *self = GST_LCEVC_ENCODER (object);
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (self);
switch (prop_id) {
case PROP_PLUGIN_NAME:
g_clear_pointer (&priv->plugin_name, g_free);
priv->plugin_name = g_value_dup_string (value);
break;
case PROP_PLUGIN_PROPS:
g_clear_pointer (&priv->plugin_props, g_free);
priv->plugin_props = g_value_dup_string (value);
break;
case PROP_BITRATE:
priv->bitrate = g_value_get_uint (value);
break;
case PROP_SEI_LCEVC:
priv->sei_lcevc = g_value_get_boolean (value);
break;
case PROP_GOP_LENGTH:
priv->gop_length = g_value_get_int (value);
break;
case PROP_DEBUG:
priv->debug = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_lcevc_encoder_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstLcevcEncoder *self = GST_LCEVC_ENCODER (object);
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (self);
switch (prop_id) {
case PROP_PLUGIN_NAME:
g_value_set_string (value, priv->plugin_name);
break;
case PROP_PLUGIN_PROPS:
g_value_set_string (value, priv->plugin_props);
break;
case PROP_BITRATE:
g_value_set_uint (value, priv->bitrate);
break;
case PROP_SEI_LCEVC:
g_value_set_boolean (value, priv->sei_lcevc);
break;
case PROP_GOP_LENGTH:
g_value_set_int (value, priv->gop_length);
break;
case PROP_DEBUG:
g_value_set_boolean (value, priv->debug);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GHashTable *
get_plugin_props_spec (GstEILContext * ctx, const gchar * plugin_name)
{
EILReturnCode rc;
GHashTable *res;
EILPropertyGroups groups;
rc = EIL_QueryPropertyGroups (ctx->context, &groups);
if (rc != EIL_RC_Success)
return NULL;
res = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
for (guint32 i = 0; i < groups.group_count; i++) {
EILPropertyGroup *group = &groups.group[i];
for (guint32 j = 0; j < group->property_count; j++) {
EILProperty *property = &group->properties[j];
g_hash_table_insert (res, g_strdup (property->name),
GINT_TO_POINTER (property->type));
}
}
return res;
}
static gboolean
open_eil_context (GstLcevcEncoder * eil)
{
GstLcevcEncoderClass *klass = GST_LCEVC_ENCODER_GET_CLASS (eil);
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
const gchar *plugin_name;
g_return_val_if_fail (!priv->ctx, FALSE);
/* Get the plugin name */
if (priv->plugin_name)
plugin_name = priv->plugin_name;
else if (klass->get_eil_plugin_name)
plugin_name = klass->get_eil_plugin_name (eil);
else
return FALSE;
/* Create the EIL context */
priv->ctx = gst_eil_context_new (eil, plugin_name, priv->debug);
if (!priv->ctx)
return FALSE;
/* Get the plugin properties spec */
priv->plugin_props_spec = get_plugin_props_spec (priv->ctx, plugin_name);
if (!priv->plugin_props_spec) {
g_clear_pointer (&priv->ctx, gst_eil_context_unref);
return FALSE;
}
return TRUE;
}
static void
close_eil_context (GstLcevcEncoder * eil)
{
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
/* Flush */
EIL_Encode (priv->ctx->context, NULL);
/* Clear properties spec */
g_clear_pointer (&priv->plugin_props_spec, g_hash_table_unref);
/* Clear context */
g_clear_pointer (&priv->ctx, gst_eil_context_unref);
}
static gboolean
gst_lcevc_encoder_start (GstVideoEncoder * encoder)
{
GstLcevcEncoder *eil = GST_LCEVC_ENCODER (encoder);
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
/* Open EIL context */
if (!open_eil_context (eil)) {
GST_ELEMENT_ERROR (encoder, LIBRARY, INIT, (NULL),
("Couldn't initialize EIL context"));
return FALSE;
}
/* Reset out TS offset */
priv->out_ts_offset = 0;
return TRUE;
}
static gboolean
gst_lcevc_encoder_stop (GstVideoEncoder * encoder)
{
GstLcevcEncoder *eil = GST_LCEVC_ENCODER (encoder);
/* Close EIL context */
close_eil_context (eil);
return TRUE;
}
static gboolean
try_parse_number (const char *value, double *parsed)
{
char *endptr;
/* Skip leading spaces */
while (g_ascii_isspace (*value))
value++;
/* Parse number */
*parsed = g_strtod (value, &endptr);
/* Allow trailing spaces */
while (g_ascii_isspace (*endptr))
value++;
/* Ceck no extra characters after number and spaces */
if (*endptr != '\0')
return FALSE;
return TRUE;
}
static GString *
build_json_props (GstLcevcEncoder * eil)
{
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
gchar *key_value;
GString *res = g_string_new ("{");
/* I/O props */
if (priv->sei_lcevc) {
/* separate_output=false */
g_string_append_printf (res, "\"%s\"", "separate_output");
g_string_append (res, ":");
g_string_append (res, "false");
} else {
/* separate_output=true */
g_string_append_printf (res, "\"%s\"", "separate_output");
g_string_append (res, ": ");
g_string_append (res, "true");
g_string_append (res, ", ");
/* output_format=raw */
g_string_append_printf (res, "\"%s\"", "output_format");
g_string_append (res, ": ");
g_string_append_printf (res, "\"%s\"", "raw");
}
if (!priv->plugin_props)
goto done;
/* Plugin props */
key_value = strtok (priv->plugin_props, ";");
while (key_value != NULL) {
const gchar *val_str = strchr (key_value, '=');
if (val_str) {
gsize key_size = val_str - key_value;
if (key_size > 0) {
gchar *key = g_strndup (key_value, key_size);
gpointer p = g_hash_table_lookup (priv->plugin_props_spec, key);
/* Add key */
g_string_append (res, ", ");
g_string_append_printf (res, "\"%s\"", key);
g_string_append (res, ": ");
/* Convert value to type defined by spec and add it, otherwise add the
* value as it is */
if (p) {
EILPropertyType spec = GPOINTER_TO_INT (p);
switch (spec) {
case EIL_PT_Int8:
case EIL_PT_Int16:
case EIL_PT_Int32:
case EIL_PT_Int64:{
gint64 val = g_ascii_strtoll (val_str + 1, NULL, 10);
g_string_append_printf (res, "%ld", val);
break;
}
case EIL_PT_Uint8:
case EIL_PT_Uint16:
case EIL_PT_Uint32:
case EIL_PT_Uint64:{
guint64 val = g_ascii_strtoull (val_str + 1, NULL, 10);
g_string_append_printf (res, "%lu", val);
break;
}
case EIL_PT_Float:
case EIL_PT_Double:{
double val = g_ascii_strtod (val_str + 1, NULL);
g_string_append_printf (res, "%f", val);
break;
}
case EIL_PT_Boolean:{
if (g_str_equal (val_str + 1, "TRUE") ||
g_str_equal (val_str + 1, "True") ||
g_str_equal (val_str + 1, "true") ||
g_str_equal (val_str + 1, "1"))
g_string_append (res, "true");
else
g_string_append (res, "false");
break;
}
case EIL_PT_String:
default:
g_string_append_printf (res, "\"%s\"", val_str + 1);
break;
}
} else {
double val;
if (try_parse_number (val_str + 1, &val)) {
if (val == ceil (val))
g_string_append_printf (res, "%d", (gint) val);
else
g_string_append_printf (res, "%f", val);
} else {
g_string_append_printf (res, "\"%s\"", val_str + 1);
}
}
g_free (key);
} else {
GST_WARNING_OBJECT (eil, "Key value pair %s does not have key",
key_value);
goto error;
}
} else {
GST_WARNING_OBJECT (eil, "Key value pair %s does not have '=' char",
key_value);
goto error;
}
key_value = strtok (NULL, ";");
}
done:
res = g_string_append (res, "}");
return res;
error:
g_string_free (res, TRUE);
return NULL;
}
static void
on_encoded_output (void *data, EILOutput * output)
{
GstLcevcEncoder *eil = GST_LCEVC_ENCODER (data);
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
GstVideoCodecFrame *frame;
GstClockTime pts;
if (!output) {
GST_INFO_OBJECT (eil, "All EIL Pictures processed");
return;
}
frame = output->user_data;
pts = frame->input_buffer->pts;
GST_INFO_OBJECT (eil, "Received output frame %ld with lcevc size %d", pts,
output->lcevc_length);
/* The EIL DTS can be negative, we need to do the conversion so it can be
* stored in a GstClockTime (guint64). The EIL PTS can never be negative
* because it is set using the input buffer PTS, which is a GstClockTime. */
if (output->dts < 0 && priv->out_ts_offset == 0) {
priv->out_ts_offset = -1 * output->dts;
GST_INFO_OBJECT (eil, "Output DTS offset set to %ld", priv->out_ts_offset);
}
/* Created output buffer with output data */
frame->output_buffer = gst_buffer_new_wrapped_full (0,
(gpointer) output->data, output->data_length, 0, output->data_length,
output_data_new (priv->ctx, output), output_data_free);
frame->pts = priv->out_ts_offset + output->pts;
frame->dts = priv->out_ts_offset + output->dts;
/* Add LCEVC metadata to output buffer if present */
if (output->lcevc_length > 0) {
GstBuffer *lcevc_data = gst_buffer_new_memdup ((gpointer) output->lcevc,
output->lcevc_length);
gst_buffer_add_lcevc_meta (frame->output_buffer, lcevc_data);
gst_buffer_unref (lcevc_data);
}
/* Set Delta unit flag if this is not a key frame */
if (!output->keyframe)
GST_BUFFER_FLAG_SET (frame->output_buffer, GST_BUFFER_FLAG_DELTA_UNIT);
else
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (eil), frame);
}
static void
gst_lcevc_encoder_set_latency (GstLcevcEncoder * eil, GstVideoInfo * info)
{
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
gint delayed_frames;
GstClockTime latency;
/* The GOP affects the number of delayed frames */
if (priv->gop_length == -2 || priv->gop_length == -1)
delayed_frames = MAX_DELAYED_FRAMES;
else
delayed_frames = MIN (5 + priv->gop_length, MAX_DELAYED_FRAMES);
latency =
gst_util_uint64_scale_ceil (GST_SECOND * GST_VIDEO_INFO_FPS_D (info),
delayed_frames, GST_VIDEO_INFO_FPS_N (info));
gst_video_encoder_set_latency (GST_VIDEO_ENCODER (eil), latency, latency);
GST_INFO_OBJECT (eil, "Updated latency to %" GST_TIME_FORMAT " (%d frames)",
GST_TIME_ARGS (latency), delayed_frames);
}
static gboolean
gst_lcevc_encoder_set_format (GstVideoEncoder * encoder,
GstVideoCodecState * state)
{
GstLcevcEncoder *eil = GST_LCEVC_ENCODER (encoder);
GstLcevcEncoderClass *klass = GST_LCEVC_ENCODER_GET_CLASS (eil);
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
EILInitSettings settings;
GString *properties_json;
GstVideoInterlaceMode interlace_mode;
EILReturnCode rc;
GstCaps *outcaps;
GstVideoCodecState *s;
gint width = GST_VIDEO_INFO_WIDTH (&state->info);
gint height = GST_VIDEO_INFO_HEIGHT (&state->info);
/* Set input info, format and frame type */
priv->in_info = state->info;
priv->in_format =
gst_lcevc_encoder_utils_get_color_format (GST_VIDEO_INFO_FORMAT
(&state->info));
interlace_mode = GST_VIDEO_INFO_INTERLACE_MODE (&state->info);
switch (interlace_mode) {
case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
priv->in_frame_type = EIL_FrameType_Progressive;
break;
case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
priv->in_frame_type = EIL_FrameType_Interlaced;
break;
case GST_VIDEO_INTERLACE_MODE_FIELDS:
priv->in_frame_type = EIL_FrameType_Field;
break;
default:
GST_ELEMENT_ERROR (eil, STREAM, FORMAT, (NULL),
("Interlace mode %s not supported",
gst_video_interlace_mode_to_string (interlace_mode)));
return FALSE;
}
/* Init EIL Settings to default values */
rc = EIL_InitSettingsDefault (&settings);
if (rc != EIL_RC_Success) {
GST_ELEMENT_ERROR (eil, LIBRARY, INIT, (NULL),
("Unabled to initialize EIL Settings"));
return FALSE;
}
/* Set basic EIL settings */
settings.color_format = priv->in_format;
settings.memory_type = EIL_MT_Host;
settings.width = width;
settings.height = height;
settings.fps_num = GST_VIDEO_INFO_FPS_N (&state->info);
settings.fps_denom = GST_VIDEO_INFO_FPS_D (&state->info);
settings.bitrate = priv->bitrate;
settings.gop_length = priv->gop_length;
settings.external_input = 1;
/* Set properties JSON EIL setting */
properties_json = build_json_props (eil);
if (!properties_json) {
GST_ELEMENT_ERROR (eil, RESOURCE, SETTINGS, (NULL),
("Could not parse plugin properties to JSON"));
return FALSE;
}
settings.properties_json = properties_json->str;
GST_INFO_OBJECT (eil, "Properties JSON: %s", properties_json->str);
/* Initialize EIL */
rc = EIL_Initialise (priv->ctx->context, &settings);
g_string_free (properties_json, TRUE);
if (rc != EIL_RC_Success)
return FALSE;
/* Get output caps */
g_assert (klass->get_output_caps);
outcaps = klass->get_output_caps (eil);
if (!outcaps) {
GST_ELEMENT_ERROR (eil, RESOURCE, NOT_FOUND, (NULL),
("Could not get output caps"));
return FALSE;
}
/* Set width, height and pixel aspect ration.
* The values from settings are updated to base width and height after
* initialization.
*/
if (width != settings.width || height != settings.height) {
GST_VIDEO_INFO_WIDTH (&state->info) = settings.width;
GST_VIDEO_INFO_HEIGHT (&state->info) = settings.height;
/* If changed, the new width and height values are always half of what they
* used to be, so update the pixel aspect ratio accordingly */
GST_VIDEO_INFO_PAR_N (&state->info) = width > settings.width ? 2 : 1;
GST_VIDEO_INFO_PAR_D (&state->info) = height > settings.height ? 2 : 1;
GST_INFO_OBJECT (eil,
"Base resolution changed: w=%d h=%d -> w=%d h=%d (par_n=%d, par_d=%d)",
width, height, settings.width, settings.height,
GST_VIDEO_INFO_PAR_N (&state->info),
GST_VIDEO_INFO_PAR_D (&state->info));
}
/* Set output state */
s = gst_video_encoder_set_output_state (encoder, outcaps, state);
if (!s) {
GST_ELEMENT_ERROR (eil, STREAM, FORMAT, (NULL),
("Could not set output state"));
return FALSE;
}
/* Set output callback */
EIL_SetOnEncodedCallback (priv->ctx->context, eil, on_encoded_output);
/* Update latency */
gst_lcevc_encoder_set_latency (eil, &s->info);
gst_video_codec_state_unref (s);
return TRUE;
}
static gboolean
gst_lcevc_encoder_sink_event (GstVideoEncoder * encoder, GstEvent * event)
{
GstLcevcEncoder *eil = GST_LCEVC_ENCODER (encoder);
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
/* Flush on EOS */
GST_INFO_OBJECT (eil, "EOS received, flushing encoder");
EIL_Encode (priv->ctx->context, NULL);
break;
default:
break;
}
return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_event (encoder, event);
}
static GstFlowReturn
gst_lcevc_encoder_handle_frame (GstVideoEncoder * encoder,
GstVideoCodecFrame * frame)
{
GstLcevcEncoder *eil = GST_LCEVC_ENCODER (encoder);
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
GstClockTime pts = frame->input_buffer->pts;
GstVideoFrame video_frame = { 0, };
EILPicture picture;
/* Map the input buffer */
if (!gst_video_frame_map (&video_frame, &priv->in_info, frame->input_buffer,
GST_MAP_READ)) {
GST_ELEMENT_ERROR (eil, STREAM, ENCODE, (NULL),
("Could not map input buffer %ld", pts));
goto error;
}
/* Initialize EIL picture */
if (EIL_InitPictureDefault (&picture) != EIL_RC_Success) {
GST_ELEMENT_ERROR (eil, STREAM, ENCODE, (NULL),
("Could not initialize EIL picture %ld", pts));
goto error;
}
/* Set frame values on EIL picture */
if (!gst_lcevc_encoder_utils_init_eil_picture (priv->in_frame_type,
&video_frame, pts, &picture)) {
GST_ELEMENT_ERROR (eil, STREAM, ENCODE, (NULL),
("Could not set frame values on EIL picture %ld", pts));
goto error;
}
/* Set input frame as user data. This will be set in the encoded output as
* user data, which will help us getting the associated frame */
picture.user_data = frame;
/* Encode frame */
if (EIL_Encode (priv->ctx->context, &picture) != EIL_RC_Success) {
GST_ELEMENT_ERROR (eil, STREAM, ENCODE, (NULL),
("Could not encode input frame %ld", pts));
goto error;
}
GST_INFO_OBJECT (eil, "Sent input frame %ld", pts);
gst_video_frame_unmap (&video_frame);
return GST_FLOW_OK;
error:
gst_video_frame_unmap (&video_frame);
return GST_FLOW_ERROR;
}
static void
gst_lcevc_encoder_class_init (GstLcevcEncoderClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstVideoEncoderClass *video_encoder_class = GST_VIDEO_ENCODER_CLASS (klass);
gst_element_class_add_static_pad_template (element_class,
&gst_lcevc_encoder_sink_template);
gst_type_mark_as_plugin_api (GST_TYPE_LCEVC_ENCODER, 0);
gobject_class->finalize = gst_lcevc_encoder_finalize;
gobject_class->set_property = gst_lcevc_encoder_set_property;
gobject_class->get_property = gst_lcevc_encoder_get_property;
video_encoder_class->start = gst_lcevc_encoder_start;
video_encoder_class->stop = gst_lcevc_encoder_stop;
video_encoder_class->set_format = gst_lcevc_encoder_set_format;
video_encoder_class->sink_event = gst_lcevc_encoder_sink_event;
video_encoder_class->handle_frame = gst_lcevc_encoder_handle_frame;
g_object_class_install_property (gobject_class, PROP_PLUGIN_NAME,
g_param_spec_string ("plugin-name", "Plugin Name",
"The name of the EIL plugin to use (NULL = auto)",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PLUGIN_PROPS,
g_param_spec_string ("plugin-props", "Plugin Props",
"A semi-colon list of key value pair properties for the EIL plugin",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_BITRATE,
g_param_spec_uint ("bitrate", "Bitrate",
"Bitrate in kbit/sec (0 = auto)",
DEFAULT_MIN_BITRATE, DEFAULT_MAX_BITRATE, DEFAULT_BITRATE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_SEI_LCEVC,
g_param_spec_boolean ("sei-lcevc", "SEI LCEVC",
"Whether to have LCEVC data as SEI (in the video stream) or not (attached to buffers as GstMeta)",
DEFAULT_SEI_LCEVC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_GOP_LENGTH,
g_param_spec_int ("gop-length", "GOP Length",
"The group of pictures length (-2 = auto, -1 = infinite, 0 = intra-only)",
DEFAULT_MIN_GOP_LENGTH, INT_MAX, DEFAULT_GOP_LENGTH,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_DEBUG,
g_param_spec_boolean ("debug", "Debug",
"Whether to show EIL SDK logs or not",
DEFAULT_DEBUG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
gst_lcevc_encoder_init (GstLcevcEncoder * eil)
{
GstLcevcEncoderPrivate *priv = gst_lcevc_encoder_get_instance_private (eil);
/* Props */
priv->plugin_name = NULL;
priv->plugin_props = NULL;
priv->bitrate = DEFAULT_BITRATE;
priv->sei_lcevc = DEFAULT_SEI_LCEVC;
priv->gop_length = DEFAULT_GOP_LENGTH;
}