Merge branch 'h264-enc-st-dev' into 'main'

Draft: v4l2codecs: Add VP8 and H264 encoding support

See merge request gstreamer/gstreamer!5676
This commit is contained in:
Nicolas Dufresne 2024-05-03 20:25:00 +00:00
commit 9f8169527e
26 changed files with 5457 additions and 18 deletions

View file

@ -0,0 +1,394 @@
/* GStreamer
* Copyright (C) 2023 Michael Grzeschik <m.grzeschik@pengutronix.de>
* Copyright (C) 2023 Denis Shimizu <denis.shimizu@collabora.com>
*
* 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 "gsth264encoder.h"
#include "gstratecontroller.h"
#include <gst/codecparsers/gsth264bitwriter.h>
#include <gst/video/gstvideometa.h>
GST_DEBUG_CATEGORY (gst_h264_encoder_debug);
#define GST_CAT_DEFAULT gst_h264_encoder_debug
#define H264_MAX_QP 51
#define H264_MIN_QP 0
#define DEFAULT_KEYFRAME_INTERVAL 30
#define DEFAULT_MAX_QP 51
#define DEFAULT_MIN_QP 10
#define DEFAULT_QP_STEP 4
#define DEFAULT_QUANTIZER 18
#define DEFAULT_BITRATE G_MAXUINT
enum
{
PROP_0,
PROP_KEYFRAME_INTERVAL,
PROP_MAX_QP,
PROP_MIN_QP,
PROP_QP_STEP,
PROP_QUANTIZER,
PROP_BITRATE,
PROP_CABAC,
PROP_CABAC_INIT_IDC,
PROP_RATE_CONTROL,
};
struct _GstH264EncoderPrivate
{
guint32 last_keyframe;
GstRateController *rate_controller;
/* properties */
gint keyframe_interval;
gboolean cabac;
guint cabac_init_idc;
};
#define parent_class gst_h264_encoder_parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstH264Encoder, gst_h264_encoder,
GST_TYPE_VIDEO_ENCODER,
G_ADD_PRIVATE (GstH264Encoder);
GST_DEBUG_CATEGORY_INIT (gst_h264_encoder_debug, "h264encoder", 0,
"H264 Video Encoder"));
static void
gst_h264_encoder_init (GstH264Encoder * self)
{
self->priv = gst_h264_encoder_get_instance_private (self);
self->priv->rate_controller = gst_rc_new ();
}
static void
gst_h264_encoder_finalize (GObject * object)
{
GstH264Encoder *self = GST_H264_ENCODER (object);
gst_object_unref (self->priv->rate_controller);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
gst_h264_encoder_start (GstVideoEncoder * encoder)
{
return TRUE;
}
static gboolean
gst_h264_encoder_stop (GstVideoEncoder * encoder)
{
return TRUE;
}
static gboolean
gst_h264_encoder_set_format (GstVideoEncoder * encoder,
GstVideoCodecState * state)
{
GstH264Encoder *self = GST_H264_ENCODER (encoder);
GstH264EncoderPrivate *priv = self->priv;
gst_rc_set_format (priv->rate_controller, &state->info);
return TRUE;
}
static GstFlowReturn
gst_h264_encoder_set_frame_type (GstH264Encoder * self,
GstH264Frame * h264_frame)
{
GstH264EncoderPrivate *priv = self->priv;
GstVideoCodecFrame *frame = h264_frame->frame;
if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
h264_frame->type = GstH264Keyframe;
return GST_FLOW_OK;
}
if ((frame->system_frame_number - priv->last_keyframe) >
priv->keyframe_interval || frame->system_frame_number == 0) {
/* Generate a keyframe */
GST_DEBUG_OBJECT (self, "Generate a keyframe");
h264_frame->type = GstH264Keyframe;
return GST_FLOW_OK;
}
/* Generate a interframe */
GST_DEBUG_OBJECT (self, "Generate a interframe");
h264_frame->type = GstH264Inter;
return GST_FLOW_OK;
}
static void
gst_h264_encoder_mark_frame (GstH264Encoder * self, GstH264Frame * h264_frame)
{
GstVideoCodecFrame *frame = h264_frame->frame;
GstH264EncoderPrivate *priv = self->priv;
GstRcFrameType rc_frame_type = GST_RC_INTER_FRAME;
switch (h264_frame->type) {
case GstH264Keyframe:
priv->last_keyframe = frame->system_frame_number;
rc_frame_type = GST_RC_KEY_FRAME;
break;
default:
break;
}
gst_rc_record (priv->rate_controller, rc_frame_type,
gst_buffer_get_size (frame->output_buffer), frame->duration);
}
static GstFlowReturn
gst_h264_encoder_handle_frame (GstVideoEncoder * encoder,
GstVideoCodecFrame * frame)
{
GstH264Encoder *self = GST_H264_ENCODER (encoder);
GstH264EncoderPrivate *priv = self->priv;
GstH264EncoderClass *klass = GST_H264_ENCODER_GET_CLASS (self);
GstFlowReturn ret = GST_FLOW_OK;
GstH264Frame *h264_frame = gst_h264_frame_new (frame);
ret = gst_h264_encoder_set_frame_type (self, h264_frame);
if (ret != GST_FLOW_OK)
return ret;
h264_frame->qp = gst_rc_get_qp (priv->rate_controller);
/* Send the frame to encode */
if (klass->encode_frame) {
ret = klass->encode_frame (self, h264_frame);
if (ret == GST_FLOW_OK)
gst_h264_encoder_mark_frame (self, h264_frame);
}
gst_h264_frame_unref (h264_frame);
return ret;
}
static void
gst_h264_encoder_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
GstH264Encoder *self = GST_H264_ENCODER (object);
GstH264EncoderPrivate *priv = self->priv;
GST_OBJECT_LOCK (self);
switch (property_id) {
case PROP_KEYFRAME_INTERVAL:
g_value_set_int (value, priv->keyframe_interval);
break;
case PROP_MAX_QP:
g_value_set_int (value, gst_rc_get_max_qp (priv->rate_controller));
break;
case PROP_MIN_QP:
g_value_set_int (value, gst_rc_get_min_qp (priv->rate_controller));
break;
case PROP_QP_STEP:
g_value_set_int (value, gst_rc_get_qp_step (priv->rate_controller));
break;
case PROP_QUANTIZER:
g_value_set_int (value, gst_rc_get_init_qp (priv->rate_controller));
break;
case PROP_BITRATE:
g_value_set_uint (value, gst_rc_get_bitrate (priv->rate_controller));
break;
case PROP_RATE_CONTROL:
g_value_set_enum (value, gst_rc_get_mode (priv->rate_controller));
break;
case PROP_CABAC:
g_value_set_boolean (value, priv->cabac);
break;
case PROP_CABAC_INIT_IDC:
g_value_set_uint (value, priv->cabac_init_idc);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
GST_OBJECT_UNLOCK (self);
}
static void
gst_h264_encoder_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
GstH264Encoder *self = GST_H264_ENCODER (object);
GstH264EncoderPrivate *priv = self->priv;
GST_OBJECT_LOCK (self);
switch (property_id) {
case PROP_KEYFRAME_INTERVAL:
priv->keyframe_interval = g_value_get_int (value);
break;
case PROP_MAX_QP:
gst_rc_set_max_qp (priv->rate_controller, g_value_get_int (value));
break;
case PROP_MIN_QP:
gst_rc_set_min_qp (priv->rate_controller, g_value_get_int (value));
break;
case PROP_QP_STEP:
gst_rc_set_qp_step (priv->rate_controller, g_value_get_int (value));
break;
case PROP_QUANTIZER:
gst_rc_set_init_qp (priv->rate_controller, g_value_get_int (value));
break;
case PROP_BITRATE:
gst_rc_set_bitrate (priv->rate_controller, g_value_get_uint (value));
break;
case PROP_RATE_CONTROL:
gst_rc_set_mode (priv->rate_controller, g_value_get_enum (value));
break;
case PROP_CABAC:
priv->cabac = g_value_get_boolean (value);
break;
case PROP_CABAC_INIT_IDC:
priv->cabac_init_idc = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
GST_OBJECT_UNLOCK (self);
}
static void
gst_h264_encoder_class_init (GstH264EncoderClass * klass)
{
GstVideoEncoderClass *encoder_class = GST_VIDEO_ENCODER_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = GST_DEBUG_FUNCPTR (gst_h264_encoder_finalize);
object_class->get_property = gst_h264_encoder_get_property;
object_class->set_property = gst_h264_encoder_set_property;
encoder_class->start = GST_DEBUG_FUNCPTR (gst_h264_encoder_start);
encoder_class->stop = GST_DEBUG_FUNCPTR (gst_h264_encoder_stop);
encoder_class->set_format = GST_DEBUG_FUNCPTR (gst_h264_encoder_set_format);
encoder_class->handle_frame =
GST_DEBUG_FUNCPTR (gst_h264_encoder_handle_frame);
/**
* GstH264Encoder:keyframe-interval:
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_KEYFRAME_INTERVAL,
g_param_spec_int ("keyframe-interval", "Keyframe Interval",
"Maximum distance in frames between IDR.",
0, G_MAXINT, DEFAULT_KEYFRAME_INTERVAL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
* GstH264Encoder:qp-max:
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_MAX_QP,
g_param_spec_int ("qp-max", "Max Quantizer Level",
"Set upper qp limit (lower number equates to higher quality but more bits)",
H264_MIN_QP, H264_MAX_QP, DEFAULT_MAX_QP,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
* GstH264Encoder:qp-min:
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_MIN_QP,
g_param_spec_int ("qp-min", "Min Quantizer Level",
"Set lower qp limit (lower number equates to higher quality but more bits)",
H264_MIN_QP, H264_MAX_QP, DEFAULT_MIN_QP,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
* GstH264Encoder:qp-step:
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_QP_STEP,
g_param_spec_int ("qp-step", "Max QP increase/decrease step",
"Set maximum value which qp value can be increase/decrease by the bitrate controller (Valid only with rate-control=cbr)",
H264_MIN_QP, H264_MAX_QP, DEFAULT_QP_STEP,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
* GstH264Encoder:quantizer:
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_QUANTIZER,
g_param_spec_int ("quantizer", "Quantizer Level",
"Set the qp value (lower number equates to higher quality but more bits, initial value for rate-control=cbr)",
H264_MIN_QP, H264_MAX_QP, DEFAULT_QUANTIZER,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
* GstH264Encoder:bitrate:
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_BITRATE,
g_param_spec_uint ("bitrate", "Targeted bitrate",
"Set the targeted bitrate (in bit/s)",
0, UINT_MAX, DEFAULT_BITRATE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
* GstH264Encoder:cabac:
* Note: Supported only on main profile
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_CABAC,
g_param_spec_boolean ("cabac", "CABAC",
"Enable Cabac", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstH264Encoder:cabac-init-idc:
* Note: Supported only on main profile
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_CABAC_INIT_IDC,
g_param_spec_uint ("cabac-init-idc", "PROP_CABAC_INIT_IDC",
"Set Cabac init idc value",
0, 2, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
* GstH264Encoder:rate-control:
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_RATE_CONTROL,
g_param_spec_enum ("rate-control", "Rate Control Mode",
"Select rate control mode", gst_rate_control_mode_get_type (),
GST_RC_CONSTANT_QP,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
}

View file

@ -0,0 +1,81 @@
/* GStreamer
* Copyright (C) 2023 Michael Grzeschik <m.grzeschik@pengutronix.de>
*
* 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.
*/
#ifndef __GST_H264_ENCODER_H__
#define __GST_H264_ENCODER_H__
#include <gst/codecs/codecs-prelude.h>
#include <gst/video/video.h>
#include <gst/video/gstvideoencoder.h>
#include "gsth264frame.h"
G_BEGIN_DECLS
#define GST_TYPE_H264_ENCODER (gst_h264_encoder_get_type())
#define GST_H264_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_H264_ENCODER,GstH264Encoder))
#define GST_H264_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_H264_ENCODER,GstH264EncoderClass))
#define GST_H264_ENCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_H264_ENCODER,GstH264EncoderClass))
#define GST_IS_H264_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_H264_ENCODER))
#define GST_IS_H264_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_H264_ENCODER))
#define GST_H264_ENCODER_CAST(obj) ((GstH264Encoder*)obj)
typedef struct _GstH264Encoder GstH264Encoder;
typedef struct _GstH264EncoderClass GstH264EncoderClass;
typedef struct _GstH264EncoderPrivate GstH264EncoderPrivate;
/**
* GstH264Encoder:
*
* The opaque #GstH264Encoder data structure.
*/
struct _GstH264Encoder
{
/*< private > */
GstVideoEncoder parent;
/*< private > */
GstH264EncoderPrivate *priv;
gpointer padding[GST_PADDING_LARGE];
};
/**
* GstH264EncoderClass:
*/
struct _GstH264EncoderClass
{
GstVideoEncoderClass parent_class;
/**
* GstH264EncoderClass::encode_frame:
* @encoder: a #GstH264Encoder
* @frame: a #GstH264Frame
*
* Provide the frame to be encoded with the encode parameters (to be defined)
*/
GstFlowReturn (*encode_frame) (GstH264Encoder * encoder,
GstH264Frame * frame);
/*< private > */
gpointer padding[GST_PADDING_LARGE];
};
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstH264Encoder, gst_object_unref)
GST_CODECS_API GType gst_h264_encoder_get_type (void);
G_END_DECLS
#endif /* __GST_H264_ENCODER_H__ */

View file

@ -0,0 +1,66 @@
/* GStreamer
* Copyright (C) 2023 Michael Grzeschik <m.grzeschik@pengutronix.de>
*
* 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 "gsth264frame.h"
GST_DEBUG_CATEGORY_EXTERN (gst_h264_encoder_debug);
#define GST_CAT_DEFAULT gst_h264_encoder_debug
GST_DEFINE_MINI_OBJECT_TYPE (GstH264Frame, gst_h264_frame);
static void
_gst_h264_frame_free (GstH264Frame * frame)
{
GST_TRACE ("Free frame %p", frame);
gst_video_codec_frame_unref (frame->frame);
g_free (frame);
}
/**
* gst_h264_frame_new:
*
* Create new #GstH264Frame
*
* Returns: a new #GstH264Frame
*/
GstH264Frame *
gst_h264_frame_new (GstVideoCodecFrame * f)
{
GstH264Frame *frame;
if (!f)
return NULL;
frame = g_new0 (GstH264Frame, 1);
gst_mini_object_init (GST_MINI_OBJECT_CAST (frame), 0,
GST_TYPE_H264_FRAME, NULL, NULL,
(GstMiniObjectFreeFunction) _gst_h264_frame_free);
frame->frame = gst_video_codec_frame_ref (f);
GST_TRACE ("New frame %p", frame);
return frame;
}

View file

@ -0,0 +1,70 @@
/* GStreamer
* Copyright (C) 2023 Michael Grzeschik <m.grzeschik@pengutronix.de>
*
* 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.
*/
#ifndef __GTS_H264_FRAME_H__
#define __GTS_H264_FRAME_H__
#include <gst/codecs/codecs-prelude.h>
#include <gst/video/video.h>
G_BEGIN_DECLS
#define GST_TYPE_H264_FRAME (gst_h264_frame_get_type())
#define GST_IS_H264_FRAME(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_H264_FRAME))
#define GST_H264_FRAME(obj) ((GstH264Frame *)obj)
#define GST_H264_FRAME_CAST(obj) (GST_H264_FRAME(obj))
typedef struct _GstH264Frame GstH264Frame;
enum
{
GstH264Keyframe,
GstH264Inter,
};
struct _GstH264Frame
{
GstMiniObject parent;
gint type;
gint qp;
GstVideoCodecFrame *frame;
};
GST_CODECS_API
GType gst_h264_frame_get_type (void);
GST_CODECS_API
GstH264Frame * gst_h264_frame_new (GstVideoCodecFrame *f);
static inline GstH264Frame *
gst_h264_frame_ref (GstH264Frame * frame)
{
return (GstH264Frame *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (frame));
}
static inline void
gst_h264_frame_unref (GstH264Frame * frame)
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (frame));
}
G_END_DECLS
#endif /* __GTS_H264_FRAME_H__ */

View file

@ -0,0 +1,241 @@
/* GStreamer
* Copyright (C) 2023 Denis Yuji Shimizu <denis.shimizu@collabora.com>
*
* 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.
*/
#include "gstratecontroller.h"
#include <math.h>
#define KP_CONST 0.0001
#define KI_CONST 0.0000000
#define KD_CONST 0.0000000
typedef struct
{
gdouble prev_error;
gdouble integral;
} PIDController;
struct _GstRateController
{
GstObject parent;
PIDController pid;
gdouble qp;
gsize total_macroblocks;
GstClockTime frame_duration;
/* configuration */
gint max_qp;
gint min_qp;
gint init_qp;
gint qp_step;
GstRateControlMode mode;
gint bitrate;
};
GType
gst_rate_control_mode_get_type (void)
{
const static GEnumValue rate_control_modes[] = {
{GST_RC_CONSTANT_QP, "cqp", "Constant QP"},
{GST_RC_CONSTANT_BITRATE, "cbr", "Constant Bitrate"},
{0, NULL, NULL},
};
static GType rc_mode_type = 0;
if (g_once_init_enter (&rc_mode_type)) {
GType _type = g_enum_register_static ("GstRateControlMode",
rate_control_modes);
g_once_init_leave (&rc_mode_type, _type);
}
return rc_mode_type;
}
G_DEFINE_TYPE (GstRateController, gst_rc, G_TYPE_OBJECT);
static void
gst_rc_init (GstRateController * self)
{
}
static void
gst_rc_class_init (GstRateControllerClass * klass)
{
}
GstRateController *
gst_rc_new (void)
{
return g_object_new (GST_RATE_CONTROLLER_TYPE, NULL);
}
static gdouble
pid_control (GstRateController * self, gdouble measured_value,
gdouble target_bitrate)
{
PIDController *pid = &self->pid;
gdouble error = target_bitrate - measured_value;
gdouble derivative = error - pid->prev_error;
pid->integral += error;
pid->prev_error = error;
/* Output is inverted, since QP and bitrate are inversely related (higher QP
* means lower bitrate) */
gdouble output =
0.0 - (KP_CONST * error +
KI_CONST * pid->integral + KD_CONST * derivative);
return output;
}
void
gst_rc_record (GstRateController * self, GstRcFrameType frame_type,
gsize coded_size, GstClockTime duration)
{
/* If there is no duration, use frame rate */
if (!GST_CLOCK_TIME_IS_VALID (duration) || duration == 0)
duration = self->frame_duration;
/* We simply ignore keyframe for now */
if (frame_type != GST_RC_KEY_FRAME) {
GstClockTime bps =
gst_util_uint64_scale (coded_size * 8, GST_SECOND, duration);
double target_bps_per_macroblock =
(gdouble) self->bitrate / self->total_macroblocks;
double bps_per_macroblock =
gst_guint64_to_gdouble (bps) / self->total_macroblocks;
gdouble pid_output =
pid_control (self, bps_per_macroblock, target_bps_per_macroblock);
gdouble qp_increase = CLAMP (pid_output,
0.0 - (gdouble) self->qp_step, (gdouble) self->qp_step);
self->qp += qp_increase;
}
}
gint
gst_rc_get_max_qp (GstRateController * self)
{
return self->max_qp;
}
gint
gst_rc_get_min_qp (GstRateController * self)
{
return self->min_qp;
}
gint
gst_rc_get_qp_step (GstRateController * self)
{
return self->qp_step;
}
gint
gst_rc_get_init_qp (GstRateController * self)
{
return self->init_qp;
}
GstRateControlMode
gst_rc_get_mode (GstRateController * self)
{
return self->mode;
}
gint
gst_rc_get_bitrate (GstRateController * self)
{
return self->bitrate;
}
void
gst_rc_set_format (GstRateController * self, const GstVideoInfo * vinfo)
{
gint width = GST_VIDEO_INFO_WIDTH (vinfo);
gint height = GST_VIDEO_INFO_HEIGHT (vinfo);
gint fps_n = GST_VIDEO_INFO_FPS_N (vinfo);
gint fps_d = GST_VIDEO_INFO_FPS_D (vinfo);
gint width_in_macroblocks = (width + 15) / 16;
gint height_in_macroblocks = (height + 15) / 16;
self->total_macroblocks = width_in_macroblocks * height_in_macroblocks;
self->pid.prev_error = 0;
self->pid.integral = 0;
self->qp = self->init_qp;
if (!fps_n || !fps_d) {
fps_n = 30;
fps_d = 1;
}
self->frame_duration = gst_util_uint64_scale (fps_d, GST_SECOND, fps_n);
}
void
gst_rc_set_max_qp (GstRateController * self, gint max_qp)
{
self->max_qp = max_qp;
}
void
gst_rc_set_min_qp (GstRateController * self, gint min_qp)
{
self->min_qp = min_qp;
}
void
gst_rc_set_qp_step (GstRateController * self, gint qp_step)
{
self->qp_step = qp_step;
}
void
gst_rc_set_init_qp (GstRateController * self, gint init_qp)
{
self->init_qp = self->qp = init_qp;
}
void
gst_rc_set_mode (GstRateController * self, GstRateControlMode mode)
{
self->mode = mode;
}
void
gst_rc_set_bitrate (GstRateController * self, gint bitrate)
{
self->bitrate = bitrate;
}
gint
gst_rc_get_qp (GstRateController * self)
{
switch (self->mode) {
case GST_RC_CONSTANT_BITRATE:
return CLAMP (round (self->qp), self->min_qp, self->max_qp);
case GST_RC_CONSTANT_QP:
return self->init_qp;
}
g_assert_not_reached ();
}

View file

@ -0,0 +1,70 @@
/* GStreamer
* Copyright (C) 2023 Michael Grzeschik <m.grzeschik@pengutronix.de>
*
* 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.
*/
#pragma once
#include <gst/codecs/codecs-prelude.h>
#include <gst/video/video.h>
G_BEGIN_DECLS
typedef enum
{
GST_RC_CONSTANT_QP,
GST_RC_CONSTANT_BITRATE,
} GstRateControlMode;
typedef enum
{
GST_RC_KEY_FRAME,
GST_RC_INTER_FRAME,
} GstRcFrameType;
GType gst_rate_control_mode_get_type (void);
#define GST_RATE_CONTROLLER_TYPE (gst_rc_get_type())
G_DECLARE_FINAL_TYPE(GstRateController, gst_rc,
GST, RC, GObject);
GstRateController* gst_rc_new (void);
void gst_rc_record (GstRateController * self,
GstRcFrameType frame_type,
gsize coded_size,
GstClockTime duration);
gint gst_rc_get_max_qp (GstRateController *self);
gint gst_rc_get_min_qp (GstRateController *self);
gint gst_rc_get_qp_step (GstRateController *self);
gint gst_rc_get_init_qp (GstRateController *self);
GstRateControlMode gst_rc_get_mode (GstRateController *self);
gint gst_rc_get_bitrate (GstRateController *self);
void gst_rc_set_format (GstRateController *self,
const GstVideoInfo * vinfo);
void gst_rc_set_max_qp (GstRateController *self, gint max_qp);
void gst_rc_set_min_qp (GstRateController *self, gint min_qp);
void gst_rc_set_qp_step (GstRateController *self, gint qp_step);
void gst_rc_set_init_qp (GstRateController *self, gint init_qp);
void gst_rc_set_mode (GstRateController *self, GstRateControlMode mode);
void gst_rc_set_bitrate (GstRateController *self, gint bitrate);
gint gst_rc_get_qp(GstRateController *self);
G_END_DECLS

View file

@ -0,0 +1,351 @@
/* GStreamer
* Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
*
* 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 "gstvp8encoder.h"
#include <gst/video/video.h>
#include <gst/video/gstvideometa.h>
#include <gst/base/base.h>
GST_DEBUG_CATEGORY (gst_vp8_encoder_debug);
#define GST_CAT_DEFAULT gst_vp8_encoder_debug
#define VP8ENC_DEFAULT_KEYFRAME_INTERVAL 30
#define VP8_MAX_QUALITY 63
#define VP8_MIN_QUALITY 0
#define VP8_DEFAULT_BITRATE 100000
enum
{
PROP_0,
PROP_KEYFRAME_INTERVAL,
PROP_MAX_QUALITY,
PROP_MIN_QUALITY,
PROP_BITRATE,
};
struct _GstVp8EncoderPrivate
{
gint keyframe_interval;
guint32 last_keyframe;
guint64 targeted_bitrate;
gint max_quality;
gint min_quality;
gint current_quality;
guint64 used_bytes;
guint64 nb_frames;
};
#define parent_class gst_vp8_encoder_parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstVp8Encoder, gst_vp8_encoder,
GST_TYPE_VIDEO_ENCODER,
G_ADD_PRIVATE (GstVp8Encoder);
GST_DEBUG_CATEGORY_INIT (gst_vp8_encoder_debug, "vp8encoder", 0,
"Vp8 Video Encoder"));
static void
gst_vp8_encoder_init (GstVp8Encoder * self)
{
self->priv = gst_vp8_encoder_get_instance_private (self);
}
static void
gst_vp8_encoder_finalize (GObject * object)
{
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
gst_vp8_encoder_start (GstVideoEncoder * encoder)
{
GstVp8Encoder *self = GST_VP8_ENCODER (encoder);
GstVp8EncoderPrivate *priv = self->priv;
priv->last_keyframe = 0;
priv->current_quality = priv->min_quality;
priv->used_bytes = 0;
priv->nb_frames = 0;
return TRUE;
}
static gboolean
gst_vp8_encoder_stop (GstVideoEncoder * encoder)
{
return TRUE;
}
static gboolean
gst_vp8_encoder_set_format (GstVideoEncoder * encoder,
GstVideoCodecState * state)
{
return TRUE;
}
static GstFlowReturn
gst_vp8_encoder_set_quality (GstVp8Encoder * self, GstVp8Frame * vp8_frame)
{
GstVp8EncoderPrivate *priv = self->priv;
GstVideoEncoder *encoder = GST_VIDEO_ENCODER (self);
GstVideoCodecState *output_state =
gst_video_encoder_get_output_state (encoder);
gint qp = priv->current_quality;
guint64 bitrate = 0;
guint fps_n = 30, fps_d = 1;
if (output_state == NULL)
return qp;
if (GST_VIDEO_INFO_FPS_N (&output_state->info) != 0) {
fps_n = GST_VIDEO_INFO_FPS_N (&output_state->info);
fps_d = GST_VIDEO_INFO_FPS_D (&output_state->info);
}
gst_video_codec_state_unref (output_state);
bitrate = (priv->used_bytes * 8 * fps_n) / (priv->nb_frames * fps_d);
if (bitrate > priv->targeted_bitrate) {
qp++;
}
if (bitrate < priv->targeted_bitrate) {
qp--;
}
if (qp > priv->max_quality)
qp = priv->max_quality;
if (qp < priv->min_quality)
qp = priv->min_quality;
vp8_frame->quality = qp;
return GST_FLOW_OK;
}
static GstFlowReturn
gst_vp8_encoder_set_frame_type (GstVp8Encoder * self, GstVp8Frame * vp8_frame)
{
GstVp8EncoderPrivate *priv = self->priv;
GstVideoCodecFrame *frame = vp8_frame->frame;
if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
vp8_frame->type = GstVp8Keyframe;
return GST_FLOW_OK;
}
if ((frame->system_frame_number - priv->last_keyframe) >
priv->keyframe_interval || frame->system_frame_number == 0) {
/* Generate a keyframe */
GST_DEBUG_OBJECT (self, "Generate a keyframe");
vp8_frame->type = GstVp8Keyframe;
return GST_FLOW_OK;
}
/* Generate a interframe */
GST_DEBUG_OBJECT (self, "Generate a interframe");
vp8_frame->type = GstVp8Inter;
return GST_FLOW_OK;
}
static void
gst_vp8_encoder_mark_frame (GstVp8Encoder * self, GstVp8Frame * vp8_frame)
{
GstVideoCodecFrame *frame = vp8_frame->frame;
GstVp8EncoderPrivate *priv = self->priv;
switch (vp8_frame->type) {
case GstVp8Keyframe:
priv->last_keyframe = frame->system_frame_number;
break;
}
priv->current_quality = vp8_frame->quality;
priv->used_bytes += gst_buffer_get_size (frame->output_buffer);
priv->nb_frames++;
}
static GstFlowReturn
gst_vp8_encoder_handle_frame (GstVideoEncoder * encoder,
GstVideoCodecFrame * frame)
{
GstVp8Encoder *self = GST_VP8_ENCODER (encoder);
GstVp8EncoderClass *klass = GST_VP8_ENCODER_GET_CLASS (self);
GstFlowReturn ret = GST_FLOW_OK;
GstVp8Frame *vp8_frame = gst_vp8_frame_new (frame);
ret = gst_vp8_encoder_set_frame_type (self, vp8_frame);
if (ret != GST_FLOW_OK)
return ret;
ret = gst_vp8_encoder_set_quality (self, vp8_frame);
if (ret != GST_FLOW_OK)
return ret;
/* TODO: add encoding parameters management here
* for now just send the frame to encode */
if (klass->encode_frame) {
ret = klass->encode_frame (self, vp8_frame);
if (ret == GST_FLOW_OK)
gst_vp8_encoder_mark_frame (self, vp8_frame);
}
gst_vp8_frame_unref (vp8_frame);
return ret;
}
static void
gst_vp8_encoder_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
GstVp8Encoder *self = GST_VP8_ENCODER (object);
GstVp8EncoderPrivate *priv = self->priv;
switch (property_id) {
case PROP_KEYFRAME_INTERVAL:
GST_OBJECT_LOCK (self);
g_value_set_int (value, priv->keyframe_interval);
GST_OBJECT_UNLOCK (self);
break;
case PROP_MAX_QUALITY:
GST_OBJECT_LOCK (self);
g_value_set_int (value, priv->max_quality);
GST_OBJECT_UNLOCK (self);
break;
case PROP_MIN_QUALITY:
GST_OBJECT_LOCK (self);
g_value_set_int (value, priv->min_quality);
GST_OBJECT_UNLOCK (self);
break;
case PROP_BITRATE:
GST_OBJECT_LOCK (self);
g_value_set_uint64 (value, priv->targeted_bitrate);
GST_OBJECT_UNLOCK (self);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gst_vp8_encoder_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
GstVp8Encoder *self = GST_VP8_ENCODER (object);
GstVp8EncoderPrivate *priv = self->priv;
switch (property_id) {
case PROP_KEYFRAME_INTERVAL:
GST_OBJECT_LOCK (self);
priv->keyframe_interval = g_value_get_int (value);
GST_OBJECT_UNLOCK (self);
break;
case PROP_MAX_QUALITY:
GST_OBJECT_LOCK (self);
priv->max_quality = g_value_get_int (value);
GST_OBJECT_UNLOCK (self);
break;
case PROP_MIN_QUALITY:
GST_OBJECT_LOCK (self);
priv->min_quality = g_value_get_int (value);
GST_OBJECT_UNLOCK (self);
break;
case PROP_BITRATE:
GST_OBJECT_LOCK (self);
priv->targeted_bitrate = g_value_get_uint64 (value);
GST_OBJECT_UNLOCK (self);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gst_vp8_encoder_class_init (GstVp8EncoderClass * klass)
{
GstVideoEncoderClass *encoder_class = GST_VIDEO_ENCODER_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = GST_DEBUG_FUNCPTR (gst_vp8_encoder_finalize);
object_class->get_property = gst_vp8_encoder_get_property;
object_class->set_property = gst_vp8_encoder_set_property;
encoder_class->start = GST_DEBUG_FUNCPTR (gst_vp8_encoder_start);
encoder_class->stop = GST_DEBUG_FUNCPTR (gst_vp8_encoder_stop);
encoder_class->set_format = GST_DEBUG_FUNCPTR (gst_vp8_encoder_set_format);
encoder_class->handle_frame =
GST_DEBUG_FUNCPTR (gst_vp8_encoder_handle_frame);
/**
* GstVp8Encoder:keyframe-interval:
*
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_KEYFRAME_INTERVAL,
g_param_spec_int ("keyframe-interval", "Keyframe Interval",
"Interval between keyframes",
0, G_MAXINT, VP8ENC_DEFAULT_KEYFRAME_INTERVAL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
* GstVp8Encoder:max-quality:
*
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_MAX_QUALITY,
g_param_spec_int ("max-quality", "Max Quality Level",
"Set upper quality limit (lower number equates to higher quality but more bits)",
VP8_MIN_QUALITY, VP8_MAX_QUALITY, VP8_MAX_QUALITY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
* GstVp8Encoder:min-quality:
*
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_MIN_QUALITY,
g_param_spec_int ("min-quality", "Min Quality Level",
"Set lower quality limit (lower number equates to higher quality but more bits)",
VP8_MIN_QUALITY, VP8_MAX_QUALITY, VP8_MIN_QUALITY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
* GstVp8Encoder:bitrate:
*
*
* Since: 1.2x
*/
g_object_class_install_property (object_class, PROP_BITRATE,
g_param_spec_uint64 ("bitrate", "Targeted bitrate",
"Set bitrate target",
0, UINT_MAX, VP8_DEFAULT_BITRATE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
}

View file

@ -0,0 +1,81 @@
/* GStreamer
* Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
*
* 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.
*/
#ifndef __GST_VP8_ENCODER_H__
#define __GST_VP8_ENCODER_H__
#include <gst/codecs/codecs-prelude.h>
#include <gst/video/video.h>
#include <gst/video/gstvideoencoder.h>
#include "gstvp8frame.h"
G_BEGIN_DECLS
#define GST_TYPE_VP8_ENCODER (gst_vp8_encoder_get_type())
#define GST_VP8_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VP8_ENCODER,GstVp8Encoder))
#define GST_VP8_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VP8_ENCODER,GstVp8EncoderClass))
#define GST_VP8_ENCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_VP8_ENCODER,GstVp8EncoderClass))
#define GST_IS_VP8_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VP8_ENCODER))
#define GST_IS_VP8_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VP8_ENCODER))
#define GST_VP8_ENCODER_CAST(obj) ((GstVp8Encoder*)obj)
typedef struct _GstVp8Encoder GstVp8Encoder;
typedef struct _GstVp8EncoderClass GstVp8EncoderClass;
typedef struct _GstVp8EncoderPrivate GstVp8EncoderPrivate;
/**
* GstVp8Encoder:
*
* The opaque #GstVp8Encoder data structure.
*/
struct _GstVp8Encoder
{
/*< private > */
GstVideoEncoder parent;
/*< private > */
GstVp8EncoderPrivate *priv;
gpointer padding[GST_PADDING_LARGE];
};
/**
* GstVp8EncoderClass:
*/
struct _GstVp8EncoderClass
{
GstVideoEncoderClass parent_class;
/**
* GstVp8EncoderClass::encode_frame:
* @encoder: a #GstVp8Encoder
* @frame: a #GstVp8Frame
*
* Provide the frame to be encoded with the encode parameters (to be defined)
*/
GstFlowReturn (*encode_frame) (GstVp8Encoder * encoder,
GstVp8Frame * frame);
/*< private > */
gpointer padding[GST_PADDING_LARGE];
};
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstVp8Encoder, gst_object_unref)
GST_CODECS_API GType gst_vp8_encoder_get_type (void);
G_END_DECLS
#endif /* __GST_VP8_ENCODER_H__ */

View file

@ -0,0 +1,66 @@
/* GStreamer
* Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
*
* 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 "gstvp8frame.h"
GST_DEBUG_CATEGORY_EXTERN (gst_vp8_encoder_debug);
#define GST_CAT_DEFAULT gst_vp8_encoder_debug
GST_DEFINE_MINI_OBJECT_TYPE (GstVp8Frame, gst_vp8_frame);
static void
_gst_vp8_frame_free (GstVp8Frame * frame)
{
GST_TRACE ("Free frame %p", frame);
gst_video_codec_frame_unref (frame->frame);
g_free (frame);
}
/**
* gst_vp8_frame_new:
*
* Create new #GstVp8Frame
*
* Returns: a new #GstVp8Frame
*/
GstVp8Frame *
gst_vp8_frame_new (GstVideoCodecFrame * f)
{
GstVp8Frame *frame;
if (!f)
return NULL;
frame = g_new0 (GstVp8Frame, 1);
gst_mini_object_init (GST_MINI_OBJECT_CAST (frame), 0,
GST_TYPE_VP8_FRAME, NULL, NULL,
(GstMiniObjectFreeFunction) _gst_vp8_frame_free);
frame->frame = gst_video_codec_frame_ref (f);
GST_TRACE ("New frame %p", frame);
return frame;
}

View file

@ -0,0 +1,70 @@
/* GStreamer
* Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
*
* 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.
*/
#ifndef __GTS_VP8_FRAME_H__
#define __GTS_VP8_FRAME_H__
#include <gst/codecs/codecs-prelude.h>
#include <gst/video/video.h>
G_BEGIN_DECLS
#define GST_TYPE_VP8_FRAME (gst_vp8_frame_get_type())
#define GST_IS_VP8_FRAME(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_VP8_FRAME))
#define GST_VP8_FRAME(obj) ((GstVp8Frame *)obj)
#define GST_VP8_FRAME_CAST(obj) (GST_VP8_FRAME(obj))
typedef struct _GstVp8Frame GstVp8Frame;
enum
{
GstVp8Keyframe,
GstVp8Inter,
};
struct _GstVp8Frame
{
GstMiniObject parent;
gint type;
gint quality;
GstVideoCodecFrame *frame;
};
GST_CODECS_API
GType gst_vp8_frame_get_type (void);
GST_CODECS_API
GstVp8Frame * gst_vp8_frame_new (GstVideoCodecFrame *f);
static inline GstVp8Frame *
gst_vp8_frame_ref (GstVp8Frame * frame)
{
return (GstVp8Frame *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (frame));
}
static inline void
gst_vp8_frame_unref (GstVp8Frame * frame)
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (frame));
}
G_END_DECLS
#endif /* __GTS_VP8_FRAME_H__ */

View file

@ -13,6 +13,11 @@ codecs_sources = files(
'gstav1picture.c',
'gstvp9statefulparser.c',
'gstcodecpicture.c',
'gstvp8encoder.c',
'gstvp8frame.c',
'gsth264encoder.c',
'gsth264frame.c',
'gstratecontroller.c',
)
codecs_headers = files(
@ -30,6 +35,10 @@ codecs_headers = files(
'gstav1picture.h',
'gstvp9statefulparser.h',
'gstcodecpicture.h',
'gstvp8encoder.h',
'gstvp8frame.h',
'gsth264encoder.h',
'gsth264frame.h',
)
cp_args = [
@ -68,6 +77,8 @@ if build_gir
'--c-include=gst/codecs/gstvp9decoder.h',
'--c-include=gst/codecs/gstvp8decoder.h',
'--c-include=gst/codecs/gstmpeg2decoder.h',
'--c-include=gst/codecs/gstvp8encoder.h',
'--c-include=gst/codecs/gsth264encoder.h',
],
'dependencies' : [gstvideo_dep, gstcodecparsers_dep]
}

View file

@ -49,6 +49,7 @@ struct _GstV4l2CodecAllocator
gboolean flushing;
GstV4l2Decoder *decoder;
GstV4l2Encoder *encoder;
GstPadDirection direction;
};
@ -110,6 +111,44 @@ gst_v4l2_codec_buffer_new (GstAllocator * allocator, GstV4l2Decoder * decoder,
return buf;
}
static GstV4l2CodecBuffer *
gst_v4l2_codec_encoder_buffer_new (GstAllocator * allocator,
GstV4l2Encoder * encoder, GstPadDirection direction, gint index)
{
GstV4l2CodecBuffer *buf;
guint i, num_mems;
gint fds[GST_VIDEO_MAX_PLANES];
gsize sizes[GST_VIDEO_MAX_PLANES];
gsize offsets[GST_VIDEO_MAX_PLANES];
if (!gst_v4l2_encoder_export_buffer (encoder, direction, index, fds, sizes,
offsets, &num_mems))
return NULL;
buf = g_new0 (GstV4l2CodecBuffer, 1);
buf->index = index;
buf->num_mems = num_mems;
for (i = 0; i < buf->num_mems; i++) {
GstMemory *mem = gst_fd_allocator_alloc (allocator, fds[i], sizes[i],
GST_FD_MEMORY_FLAG_KEEP_MAPPED);
gst_memory_resize (mem, offsets[i], sizes[i] - offsets[i]);
GST_MINI_OBJECT (mem)->dispose = gst_v4l2_codec_allocator_release;
gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
gst_v4l2_codec_buffer_quark (), buf, NULL);
/* On outstanding memory keeps a reference on the allocator, this is
* needed to break the cycle. */
gst_object_unref (mem->allocator);
buf->mem[i] = mem;
}
GST_DEBUG_OBJECT (allocator, "Create buffer %i with %i memory fds",
buf->index, buf->num_mems);
return buf;
}
static void
gst_v4l2_codec_buffer_free (GstV4l2CodecBuffer * buf)
{
@ -169,7 +208,7 @@ gst_v4l2_codec_allocator_release (GstMiniObject * mini_object)
}
static gboolean
gst_v4l2_codec_allocator_prepare (GstV4l2CodecAllocator * self)
gst_v4l2_codec_decoder_allocator_prepare (GstV4l2CodecAllocator * self)
{
GstV4l2Decoder *decoder = self->decoder;
GstPadDirection direction = self->direction;
@ -198,6 +237,44 @@ failed:
return FALSE;
}
static gboolean
gst_v4l2_codec_encoder_allocator_prepare (GstV4l2CodecAllocator * self)
{
GstV4l2Encoder *encoder = self->encoder;
GstPadDirection direction = self->direction;
gint ret;
guint i;
ret = gst_v4l2_encoder_request_buffers (encoder, direction, self->pool_size,
V4L2_MEMORY_MMAP);
if (ret < self->pool_size) {
if (ret >= 0)
GST_ERROR_OBJECT (self,
"%i buffer was needed, but only %i could be allocated",
self->pool_size, ret);
goto failed;
}
for (i = 0; i < self->pool_size; i++) {
GstV4l2CodecBuffer *buf =
gst_v4l2_codec_encoder_buffer_new (GST_ALLOCATOR (self),
encoder, direction, i);
g_queue_push_tail (&self->pool, buf);
}
if (direction == GST_PAD_SINK) {
gst_v4l2_codec_allocator_detach (self);
gst_v4l2_encoder_request_buffers (encoder, direction,
VIDEO_MAX_FRAME, V4L2_MEMORY_DMABUF);
}
return TRUE;
failed:
gst_v4l2_encoder_request_buffers (encoder, direction, 0, V4L2_MEMORY_MMAP);
return FALSE;
}
static void
gst_v4l2_codec_allocator_init (GstV4l2CodecAllocator * self)
{
@ -218,6 +295,11 @@ gst_v4l2_codec_allocator_dispose (GObject * object)
gst_clear_object (&self->decoder);
}
if (self->encoder) {
gst_v4l2_codec_allocator_detach (self);
gst_clear_object (&self->encoder);
}
G_OBJECT_CLASS (gst_v4l2_codec_allocator_parent_class)->dispose (object);
}
@ -253,7 +335,26 @@ gst_v4l2_codec_allocator_new (GstV4l2Decoder * decoder,
self->direction = direction;
self->pool_size = num_buffers;
if (!gst_v4l2_codec_allocator_prepare (self)) {
if (!gst_v4l2_codec_decoder_allocator_prepare (self)) {
g_object_unref (self);
return NULL;
}
return self;
}
GstV4l2CodecAllocator *
gst_v4l2_codec_encoder_allocator_new (GstV4l2Encoder * encoder,
GstPadDirection direction, guint num_buffers)
{
GstV4l2CodecAllocator *self =
g_object_new (GST_TYPE_V4L2_CODEC_ALLOCATOR, NULL);
self->encoder = g_object_ref (encoder);
self->direction = direction;
self->pool_size = num_buffers;
if (!gst_v4l2_codec_encoder_allocator_prepare (self)) {
g_object_unref (self);
return NULL;
}
@ -348,7 +449,13 @@ gst_v4l2_codec_allocator_detach (GstV4l2CodecAllocator * self)
GST_OBJECT_LOCK (self);
if (!self->detached) {
self->detached = TRUE;
gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0);
if (self->decoder) {
gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0);
}
if (self->encoder) {
gst_v4l2_encoder_request_buffers (self->encoder, self->direction, 0,
V4L2_MEMORY_MMAP);
}
}
GST_OBJECT_UNLOCK (self);
}

View file

@ -26,6 +26,7 @@
#include "gstv4l2codecdevice.h"
#include "gstv4l2decoder.h"
#include "gstv4l2encoder.h"
#define GST_TYPE_V4L2_CODEC_ALLOCATOR gst_v4l2_codec_allocator_get_type ()
G_DECLARE_FINAL_TYPE (GstV4l2CodecAllocator, gst_v4l2_codec_allocator,
@ -36,10 +37,12 @@ GstV4l2CodecAllocator *gst_v4l2_codec_allocator_new (GstV4l2Decoder * decoder,
GstPadDirection direction,
guint num_buffers);
GstV4l2CodecAllocator *gst_v4l2_codec_encoder_allocator_new (GstV4l2Encoder * encoder,
GstPadDirection direction,
guint num_buffers);
GstMemory *gst_v4l2_codec_allocator_alloc (GstV4l2CodecAllocator * allocator);
gboolean gst_v4l2_codec_allocator_create_buffer (GstV4l2CodecAllocator * self);
gboolean gst_v4l2_codec_allocator_wait_for_buffer (GstV4l2CodecAllocator * self);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,54 @@
/* GStreamer
* Copyright (C) 2023 Michael Grzeschik <m.grzeschik@pengutronix.de>
*
* 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.
*/
#ifndef __GST_V4L2_CODEC_H264_ENC_H__
#define __GST_V4L2_CODEC_H264_ENC_H__
#define GST_USE_UNSTABLE_API
#include <gst/codecs/gsth264encoder.h>
#include "gstv4l2encoder.h"
G_BEGIN_DECLS
#define GST_TYPE_V4L2_CODEC_H264_ENC (gst_v4l2_codec_h264_enc_get_type())
#define GST_V4L2_CODEC_H264_ENC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_CODEC_H264_ENC,GstV4l2CodecH264Enc))
#define GST_V4L2_CODEC_H264_ENC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_CODEC_H264_ENC,GstV4l2CodecH264EncClass))
#define GST_V4L2_CODEC_H264_ENC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_V4L2_CODEC_H264_ENC, GstV4l2CodecH264EncClass))
#define GST_IS_V4L2_CODEC_H264_ENC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_CODEC_H264_ENC))
#define GST_IS_V4L2_CODEC_H264_ENC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_CODEC_H264_ENC))
typedef struct _GstV4l2CodecH264Enc GstV4l2CodecH264Enc;
typedef struct _GstV4l2CodecH264EncClass GstV4l2CodecH264EncClass;
struct _GstV4l2CodecH264EncClass
{
GstH264EncoderClass parent_class;
GstV4l2CodecDevice *device;
};
GType gst_v4l2_codec_h264_enc_get_type (void);
void gst_v4l2_codec_h264_enc_register (GstPlugin * plugin,
GstV4l2Encoder * encoder,
GstV4l2CodecDevice * device,
guint rank);
G_END_DECLS
#endif /* __GST_V4L2_CODEC_H264_ENC_H__ */

View file

@ -0,0 +1,745 @@
/* GStreamer
* Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
*
* 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 "gstv4l2codecallocator.h"
#include "gstv4l2codecpool.h"
#include "gstv4l2codecvp8enc.h"
#include "gstv4l2format.h"
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#define V4L2_MIN_KERNEL_VER_MAJOR 5
#define V4L2_MIN_KERNEL_VER_MINOR 17
#define V4L2_MIN_KERNEL_VERSION KERNEL_VERSION(V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR, 0)
GST_DEBUG_CATEGORY_STATIC (v4l2_vp8enc_debug);
#define GST_CAT_DEFAULT v4l2_vp8enc_debug
enum
{
PROP_0,
PROP_LAST = PROP_0
};
static GstStaticPadTemplate sink_template =
GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SINK_NAME,
GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_V4L2_DEFAULT_VIDEO_FORMATS)));
static GstStaticPadTemplate src_template =
GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SRC_NAME,
GST_PAD_SRC, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-vp8"));
struct _vp8_frame_hdr
{
guint8 tag[3];
guint8 start_code[3];
guint16 width;
guint16 height;
};
#define VP8_FRAME_HDR_INTRA_FLAG 0x0
#define VP8_FRAME_HDR_INTER_FLAG 0x1
#define VP8_FRAME_HDR_VERSION1_FLAG (1 << 1)
#define VP8_FRAME_HDR_SHOW_FRAME (1 << 4)
#define VP8_FRAME_HDR_WIDTH_MASK 0x3fff
#define VP8_FRAME_HDR_HEIGHT_MASK 0x3fff
struct _GstV4l2CodecVp8Enc
{
GstVp8Encoder parent;
GstV4l2Encoder *encoder;
GstVideoCodecState *output_state;
GstVideoInfo vinfo;
gint width;
gint height;
guint qp_max, qp_min;
GstV4l2CodecAllocator *sink_allocator;
GstV4l2CodecAllocator *src_allocator;
GstV4l2CodecPool *sink_pool;
GstV4l2CodecPool *src_pool;
struct v4l2_ctrl_vp8_encode_params encode_params;
};
G_DEFINE_ABSTRACT_TYPE (GstV4l2CodecVp8Enc, gst_v4l2_codec_vp8_enc,
GST_TYPE_VP8_ENCODER);
#define parent_class gst_v4l2_codec_vp8_enc_parent_class
static gboolean
gst_v4l2_codec_vp8_enc_open (GstVideoEncoder * encoder)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
guint version;
if (!gst_v4l2_encoder_open (self->encoder)) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE,
("Failed to open VP8 encoder"),
("gst_v4l2_encoder_open() failed: %s", g_strerror (errno)));
return FALSE;
}
version = gst_v4l2_encoder_get_version (self->encoder);
if (version < V4L2_MIN_KERNEL_VERSION)
GST_WARNING_OBJECT (self,
"V4L2 API v%u.%u too old, at least v%u.%u required",
(version >> 16) & 0xff, (version >> 8) & 0xff,
V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR);
GST_DEBUG_OBJECT (self, "open vp8 encoder");
return TRUE;
}
static gboolean
gst_v4l2_codec_vp8_enc_api_check (GstV4l2Encoder * encoder)
{
guint i, ret_size;
/* *INDENT-OFF* */
#define SET_ID(cid) .id = (cid), .name = #cid
struct
{
const gchar *name;
unsigned int id;
unsigned int size;
gboolean optional;
} controls[] = {
{
SET_ID (V4L2_CID_STATELESS_VP8_ENCODE_PARAMS),
.size = sizeof(struct v4l2_ctrl_vp8_encode_params),
}, {
SET_ID (V4L2_CID_STATELESS_VP8_ENCODE_QP),
.size = sizeof(__u32),
},
};
#undef SET_ID
/* *INDENT-ON* */
/*
* Compatibility check: make sure the pointer controls are
* the right size.
*/
for (i = 0; i < G_N_ELEMENTS (controls); i++) {
gboolean control_found;
control_found = gst_v4l2_encoder_query_control_size (encoder,
controls[i].id, &ret_size);
if (!controls[i].optional && !control_found) {
GST_WARNING ("Driver is missing %s support.", controls[i].name);
return FALSE;
}
if (control_found && ret_size != controls[i].size) {
GST_WARNING ("%s control size mismatch: got %d bytes but %d expected.",
controls[i].name, ret_size, controls[i].size);
return FALSE;
}
}
return TRUE;
}
static gboolean
gst_v4l2_codec_vp8_enc_close (GstVideoEncoder * encoder)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
gst_v4l2_encoder_close (self->encoder);
return TRUE;
}
static void
gst_v4l2_codec_vp8_enc_reset_allocation (GstV4l2CodecVp8Enc * self)
{
if (self->sink_allocator) {
gst_v4l2_codec_allocator_detach (self->sink_allocator);
g_clear_object (&self->sink_allocator);
g_clear_object (&self->sink_pool);
}
if (self->src_allocator) {
gst_v4l2_codec_allocator_detach (self->src_allocator);
g_clear_object (&self->src_allocator);
g_clear_object (&self->src_pool);
}
}
static gboolean
gst_v4l2_codec_vp8_enc_start (GstVideoEncoder * encoder)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
GST_DEBUG_OBJECT (self, "start");
return GST_VIDEO_ENCODER_CLASS (parent_class)->start (encoder);
}
static gboolean
gst_v4l2_codec_vp8_enc_stop (GstVideoEncoder * encoder)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
GST_DEBUG_OBJECT (self, "stop");
gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SINK);
gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SRC);
gst_v4l2_codec_vp8_enc_reset_allocation (self);
if (self->output_state)
gst_video_codec_state_unref (self->output_state);
self->output_state = NULL;
return GST_VIDEO_ENCODER_CLASS (parent_class)->stop (encoder);
}
static GstCaps *
gst_v4l2_codec_vp8_enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
GstCaps *caps, *result;
caps = gst_v4l2_encoder_list_sink_formats (self->encoder);
GST_DEBUG_OBJECT (self, "Supported input formats: %" GST_PTR_FORMAT, caps);
result = gst_video_encoder_proxy_getcaps (encoder, caps, filter);
if (caps)
gst_caps_unref (caps);
GST_DEBUG_OBJECT (self, "Returning sink caps: %" GST_PTR_FORMAT, result);
return result;
}
static gboolean
gst_v4l2_codec_vp8_enc_propose_allocation (GstVideoEncoder * encoder,
GstQuery * query)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
gst_query_add_allocation_pool (query, NULL, self->vinfo.size, 2, 0);
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
query);
}
static gboolean
gst_v4l2_codec_vp8_enc_buffers_allocation (GstVideoEncoder * encoder)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
GST_DEBUG_OBJECT (self, "buffers allocation");
g_clear_object (&self->sink_pool);
g_clear_object (&self->src_pool);
g_clear_object (&self->src_allocator);
self->sink_allocator = gst_v4l2_codec_encoder_allocator_new (self->encoder,
GST_PAD_SINK, 4);
if (!self->sink_allocator) {
GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
("Not enough memory to allocate sink buffers."), (NULL));
return FALSE;
}
self->sink_pool =
gst_v4l2_codec_pool_new (self->sink_allocator, &self->vinfo);
self->src_allocator = gst_v4l2_codec_encoder_allocator_new (self->encoder,
GST_PAD_SRC, 4);
if (!self->src_allocator) {
GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
("Not enough memory to allocate source buffers."), (NULL));
g_clear_object (&self->sink_allocator);
return FALSE;
}
self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo);
return TRUE;
}
static gboolean
gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder,
GstVideoCodecState * state)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
GstCaps *caps;
GST_DEBUG_OBJECT (self, "Set format");
gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SINK);
gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SRC);
gst_v4l2_codec_vp8_enc_reset_allocation (self);
if (!gst_v4l2_encoder_set_src_fmt (self->encoder, &state->info,
V4L2_PIX_FMT_VP8_FRAME)) {
GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported pixel format"),
("No support for %ux%u format VP8", state->info.width,
state->info.height));
return FALSE;
}
if (!gst_v4l2_encoder_select_sink_format (self->encoder, &state->info,
&self->vinfo)) {
GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
("Failed to configure VP8 encoder"),
("gst_v4l2_encoder_select_sink_format() failed: %s",
g_strerror (errno)));
gst_v4l2_encoder_close (self->encoder);
return FALSE;
}
self->width = state->info.width;
self->height = state->info.height;
gst_v4l2_codec_vp8_enc_buffers_allocation (encoder);
if (self->output_state)
gst_video_codec_state_unref (self->output_state);
caps = gst_caps_new_empty_simple ("video/x-vp8");
self->output_state =
gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self),
caps, state);
if (gst_video_encoder_negotiate (encoder)) {
if (!gst_v4l2_encoder_streamon (self->encoder, GST_PAD_SINK)) {
GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
("Could not enable the encoder driver."),
("VIDIOC_STREAMON(SINK) failed: %s", g_strerror (errno)));
return FALSE;
}
if (!gst_v4l2_encoder_streamon (self->encoder, GST_PAD_SRC)) {
GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
("Could not enable the encoder driver."),
("VIDIOC_STREAMON(SRC) failed: %s", g_strerror (errno)));
return FALSE;
}
gst_v4l2_codec_vp8_enc_get_qp_range (self->encoder, &self->qp_min,
&self->qp_max);
return TRUE;
}
return FALSE;
}
static void
gst_v4l2_codec_vp8_enc_set_flushing (GstV4l2CodecVp8Enc * self,
gboolean flushing)
{
if (self->sink_allocator)
gst_v4l2_codec_allocator_set_flushing (self->sink_allocator, flushing);
if (self->src_allocator)
gst_v4l2_codec_allocator_set_flushing (self->src_allocator, flushing);
}
static gboolean
gst_v4l2_codec_vp8_enc_flush (GstVideoEncoder * encoder)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
GST_DEBUG_OBJECT (self, "Flushing encoder state.");
gst_v4l2_encoder_flush (self->encoder);
gst_v4l2_codec_vp8_enc_set_flushing (self, FALSE);
return GST_VIDEO_ENCODER_CLASS (parent_class)->flush (encoder);
}
static gboolean
gst_v4l2_codec_vp8_enc_sink_event (GstVideoEncoder * encoder, GstEvent * event)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_START:
GST_DEBUG_OBJECT (self, "flush start");
gst_v4l2_codec_vp8_enc_set_flushing (self, TRUE);
break;
default:
break;
}
return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_event (encoder, event);
}
static GstStateChangeReturn
gst_v4l2_codec_vp8_enc_change_state (GstElement * element,
GstStateChange transition)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (element);
if (transition == GST_STATE_CHANGE_PAUSED_TO_READY)
gst_v4l2_codec_vp8_enc_set_flushing (self, TRUE);
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
}
static void
gst_v4l2_codec_vp8_enc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (object);
GObject *dec = G_OBJECT (self->encoder);
switch (prop_id) {
default:
gst_v4l2_encoder_set_property (dec, prop_id - PROP_LAST, value, pspec);
break;
}
}
static void
gst_v4l2_codec_vp8_enc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (object);
GObject *dec = G_OBJECT (self->encoder);
switch (prop_id) {
default:
gst_v4l2_encoder_get_property (dec, prop_id - PROP_LAST, value, pspec);
break;
}
}
static gboolean
gst_v4l2_codec_vp8_enc_copy_input_buffer (GstV4l2CodecVp8Enc * self,
GstVideoCodecFrame * frame)
{
GstVideoFrame src_frame;
GstVideoFrame dest_frame;
GstVideoInfo src_vinfo;
GstBuffer *buffer;
GstFlowReturn flow_ret;
gst_video_info_set_format (&src_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo),
self->width, self->height);
flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->sink_pool),
&buffer, NULL);
if (flow_ret != GST_FLOW_OK) {
if (flow_ret == GST_FLOW_FLUSHING)
GST_DEBUG_OBJECT (self, "Frame encoding aborted, we are flushing.");
else
GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
("No more picture buffer available."), (NULL));
return FALSE;
}
if (!buffer)
goto fail;
if (!gst_video_frame_map (&src_frame, &src_vinfo,
frame->input_buffer, GST_MAP_READ))
goto fail;
if (!gst_video_frame_map (&dest_frame, &self->vinfo, buffer, GST_MAP_WRITE)) {
gst_video_frame_unmap (&dest_frame);
goto fail;
}
if (!gst_video_frame_copy (&dest_frame, &src_frame)) {
gst_video_frame_unmap (&src_frame);
gst_video_frame_unmap (&dest_frame);
goto fail;
}
gst_video_frame_unmap (&src_frame);
gst_video_frame_unmap (&dest_frame);
gst_buffer_replace (&frame->input_buffer, buffer);
gst_buffer_unref (buffer);
return TRUE;
fail:
GST_ERROR_OBJECT (self, "Failed copy input buffer.");
return FALSE;
}
static gboolean
gst_v4l2_codec_vp8_enc_ensure_output_bitstream (GstV4l2CodecVp8Enc * self,
GstVideoCodecFrame * frame)
{
GstFlowReturn flow_ret;
flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->src_pool),
&frame->output_buffer, NULL);
if (flow_ret != GST_FLOW_OK) {
if (flow_ret == GST_FLOW_FLUSHING)
GST_DEBUG_OBJECT (self, "Frame encoding aborted, we are flushing.");
else
GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
("No more encoded buffer available."), (NULL));
return FALSE;
}
if (!frame->output_buffer)
return FALSE;
return TRUE;
}
static void
gst_v4l2_codec_vp8_enc_fill_encode_params (GstVp8Encoder * encoder,
GstVp8Frame * vp8_frame)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
switch (vp8_frame->type) {
case GstVp8Keyframe:
self->encode_params.frame_type = V4L2_VP8_FRAME_TYPE_KEYFRAME;
self->encode_params.loop_filter_level = 26;
break;
case GstVp8Inter:
default:
self->encode_params.frame_type = V4L2_VP8_FRAME_TYPE_INTER;
self->encode_params.loop_filter_level = 12;
break;
}
self->encode_params.flags = V4L2_VP8_FRAME_FLAG_SHOWFRAME;
}
static guint
gst_v4l2_codec_vp8_enc_check_qp_range (GstV4l2CodecVp8Enc * self,
GstVp8Frame * vp8_frame)
{
if (vp8_frame->quality > self->qp_max)
return self->qp_max;
if (vp8_frame->quality < self->qp_min)
return self->qp_min;
return vp8_frame->quality;
}
static GstFlowReturn
gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder,
GstVp8Frame * vp8_frame)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
GstVideoEncoder *venc = GST_VIDEO_ENCODER (encoder);
GstV4l2Request *request = NULL;
GstFlowReturn ret = GST_FLOW_ERROR;
GstVideoCodecFrame *frame = vp8_frame->frame;
GstBuffer *resized_buffer;
guint32 bytesused;
guint32 flags;
/* *INDENT-OFF* */
struct v4l2_ext_control control[] = {
{
.id = V4L2_CID_STATELESS_VP8_ENCODE_PARAMS,
.ptr = &self->encode_params,
.size = sizeof (self->encode_params),
}, {
.id = V4L2_CID_STATELESS_VP8_ENCODE_QP,
.value = gst_v4l2_codec_vp8_enc_check_qp_range (self, vp8_frame),
.size = sizeof (guint),
},
};
/* *INDENT-ON* */
GST_DEBUG_OBJECT (self, "encode vp8 frame with quality = %d",
vp8_frame->quality);
if (!gst_v4l2_codec_vp8_enc_ensure_output_bitstream (self, frame)) {
GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
("Failed to allocate output buffer."), (NULL));
goto done;
}
request = gst_v4l2_encoder_alloc_request (self->encoder,
frame->system_frame_number, frame->input_buffer, frame->output_buffer);
if (!request) {
GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
("Failed to allocate a media request object."), (NULL));
goto done;
}
gst_v4l2_codec_vp8_enc_fill_encode_params (encoder, vp8_frame);
if (!gst_v4l2_encoder_set_controls (self->encoder, request, control,
G_N_ELEMENTS (control))) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
("Driver did not accept the control parameters."), (NULL));
goto done;
}
if (!gst_v4l2_encoder_request_queue (request, 0)) {
if (!gst_v4l2_codec_vp8_enc_copy_input_buffer (self, frame)) {
GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
("Failed to allocate/copy input buffer."), (NULL));
goto done;
}
gst_v4l2_encoder_request_replace_pic_buf (request, frame->input_buffer);
if (!gst_v4l2_encoder_request_queue (request, 0)) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
("Driver did not accept the encode request."), (NULL));
goto done;
}
}
if (!gst_v4l2_encoder_request_set_done (request, &bytesused, &flags)) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
("Driver did not ack the request."), (NULL));
goto done;
}
gst_v4l2_encoder_request_unref (request);
resized_buffer = gst_buffer_copy_region (frame->output_buffer,
GST_BUFFER_COPY_MEMORY | GST_BUFFER_COPY_DEEP, 0, bytesused);
gst_buffer_replace (&frame->output_buffer, resized_buffer);
gst_buffer_unref (resized_buffer);
if (flags & V4L2_BUF_FLAG_KEYFRAME) {
GST_BUFFER_FLAG_UNSET (frame->output_buffer, GST_BUFFER_FLAG_DELTA_UNIT);
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
} else {
GST_BUFFER_FLAG_SET (frame->output_buffer, GST_BUFFER_FLAG_DELTA_UNIT);
GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame);
}
return gst_video_encoder_finish_frame (venc, frame);
done:
if (request)
gst_v4l2_encoder_request_unref (request);
return ret;
}
static void
gst_v4l2_codec_vp8_enc_init (GstV4l2CodecVp8Enc * self)
{
}
static void
gst_v4l2_codec_vp8_enc_subinit (GstV4l2CodecVp8Enc * self,
GstV4l2CodecVp8EncClass * klass)
{
self->encoder = gst_v4l2_encoder_new (klass->device);
}
static void
gst_v4l2_codec_vp8_enc_dispose (GObject * object)
{
GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (object);
g_clear_object (&self->encoder);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_v4l2_codec_vp8_enc_class_init (GstV4l2CodecVp8EncClass * klass)
{
}
static void
gst_v4l2_codec_vp8_enc_subclass_init (GstV4l2CodecVp8EncClass * klass,
GstV4l2CodecDevice * device)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstVideoEncoderClass *encoder_class = GST_VIDEO_ENCODER_CLASS (klass);
GstVp8EncoderClass *vp8encoder_class = GST_VP8_ENCODER_CLASS (klass);
gobject_class->set_property = gst_v4l2_codec_vp8_enc_set_property;
gobject_class->get_property = gst_v4l2_codec_vp8_enc_get_property;
gobject_class->dispose = gst_v4l2_codec_vp8_enc_dispose;
gst_element_class_set_static_metadata (element_class,
"V4L2 Stateless VP8 Video Encoder",
"Codec/Encoder/Video/Hardware",
"A V4L2 based VP8 video encoder",
"Benjamin Gaignard <benjamin.gaignard@collabora.com>");
gst_element_class_add_static_pad_template (element_class, &sink_template);
gst_element_class_add_static_pad_template (element_class, &src_template);
element_class->change_state =
GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_change_state);
encoder_class->open = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_open);
encoder_class->close = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_close);
encoder_class->start = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_start);
encoder_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_stop);
encoder_class->set_format =
GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_set_format);
encoder_class->flush = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_flush);
encoder_class->sink_event =
GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_sink_event);
encoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_getcaps);
encoder_class->propose_allocation =
GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_propose_allocation);
vp8encoder_class->encode_frame =
GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_encode_frame);
klass->device = device;
gst_v4l2_encoder_install_properties (gobject_class, PROP_LAST, device);
}
void
gst_v4l2_codec_vp8_enc_register (GstPlugin * plugin, GstV4l2Encoder * encoder,
GstV4l2CodecDevice * device, guint rank)
{
gchar *element_name;
guint version;
GST_DEBUG_CATEGORY_INIT (v4l2_vp8enc_debug, "v4l2codecs-vp8enc", 0,
"V4L2 stateless VP8 encoder");
version = gst_v4l2_encoder_get_version (encoder);
if (version < V4L2_MIN_KERNEL_VERSION)
GST_WARNING ("V4L2 API v%u.%u too old, at least v%u.%u required",
(version >> 16) & 0xff, (version >> 8) & 0xff,
V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR);
if (!gst_v4l2_codec_vp8_enc_api_check (encoder)) {
GST_WARNING ("Not registering VP8 encoder as it failed ABI check.");
return;
}
gst_v4l2_encoder_register (plugin, GST_TYPE_V4L2_CODEC_VP8_ENC,
(GClassInitFunc) gst_v4l2_codec_vp8_enc_subclass_init,
gst_mini_object_ref (GST_MINI_OBJECT (device)),
(GInstanceInitFunc) gst_v4l2_codec_vp8_enc_subinit,
"v4l2sl%svp8enc", device, rank, &element_name);
}

View file

@ -0,0 +1,54 @@
/* GStreamer
* Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
*
* 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.
*/
#ifndef __GST_V4L2_CODEC_VP8_ENC_H__
#define __GST_V4L2_CODEC_VP8_ENC_H__
#define GST_USE_UNSTABLE_API
#include <gst/codecs/gstvp8encoder.h>
#include "gstv4l2encoder.h"
G_BEGIN_DECLS
#define GST_TYPE_V4L2_CODEC_VP8_ENC (gst_v4l2_codec_vp8_enc_get_type())
#define GST_V4L2_CODEC_VP8_ENC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_CODEC_VP8_ENC,GstV4l2CodecVp8Enc))
#define GST_V4L2_CODEC_VP8_ENC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_CODEC_VP8_ENC,GstV4l2CodecVp8EncClass))
#define GST_V4L2_CODEC_VP8_ENC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_V4L2_CODEC_VP8_ENC, GstV4l2CodecVp8EncClass))
#define GST_IS_V4L2_CODEC_VP8_ENC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_CODEC_VP8_ENC))
#define GST_IS_V4L2_CODEC_VP8_ENC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_CODEC_VP8_ENC))
typedef struct _GstV4l2CodecVp8Enc GstV4l2CodecVp8Enc;
typedef struct _GstV4l2CodecVp8EncClass GstV4l2CodecVp8EncClass;
struct _GstV4l2CodecVp8EncClass
{
GstVp8EncoderClass parent_class;
GstV4l2CodecDevice *device;
};
GType gst_v4l2_codec_vp8_enc_get_type (void);
void gst_v4l2_codec_vp8_enc_register (GstPlugin * plugin,
GstV4l2Encoder * encoder,
GstV4l2CodecDevice * device,
guint rank);
G_END_DECLS
#endif /* __GST_V4L2_CODEC_VP8_ENC_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,145 @@
/* GStreamer
* Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
*
* 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.
*/
#ifndef __GST_V4L2_ENCODER_H__
#define __GST_V4L2_ENCODER_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstv4l2codecdevice.h"
#include "linux/videodev2.h"
G_BEGIN_DECLS
#define GST_TYPE_V4L2_ENCODER gst_v4l2_encoder_get_type ()
G_DECLARE_FINAL_TYPE (GstV4l2Encoder, gst_v4l2_encoder, GST, V4L2_ENCODER, GstObject);
typedef struct _GstV4l2Request GstV4l2Request;
GstV4l2Encoder * gst_v4l2_encoder_new (GstV4l2CodecDevice * device);
guint gst_v4l2_encoder_get_version (GstV4l2Encoder * self);
gboolean gst_v4l2_encoder_open (GstV4l2Encoder * decoder);
gboolean gst_v4l2_encoder_close (GstV4l2Encoder * decoder);
gboolean gst_v4l2_encoder_streamon (GstV4l2Encoder * self,
GstPadDirection direction);
gboolean gst_v4l2_encoder_streamoff (GstV4l2Encoder * self,
GstPadDirection direction);
gboolean gst_v4l2_encoder_flush (GstV4l2Encoder * self);
gboolean gst_v4l2_encoder_enum_sink_fmt (GstV4l2Encoder * self,
gint i, guint32 * out_fmt);
GstCaps * gst_v4l2_encoder_list_sink_formats (GstV4l2Encoder * self);
gboolean gst_v4l2_encoder_select_sink_format (GstV4l2Encoder * self,
GstVideoInfo * in, GstVideoInfo * out);
gboolean gst_v4l2_encoder_enum_src_formats (GstV4l2Encoder * self,
gint i, guint32 * out_fmt);
gboolean gst_v4l2_encoder_set_src_fmt (GstV4l2Encoder * self, GstVideoInfo * info, guint32 pix_fmt);
gint gst_v4l2_encoder_request_buffers (GstV4l2Encoder * self,
GstPadDirection direction,
guint num_buffers,
guint mem_type);
gboolean gst_v4l2_encoder_export_buffer (GstV4l2Encoder * self,
GstPadDirection directon,
gint index,
gint * fds,
gsize * sizes,
gsize * offsets,
guint *num_fds);
gboolean gst_v4l2_encoder_set_controls (GstV4l2Encoder * self,
GstV4l2Request * request,
struct v4l2_ext_control *control,
guint count);
gboolean gst_v4l2_encoder_get_controls (GstV4l2Encoder * self,
GstV4l2Request * request,
struct v4l2_ext_control * control,
guint count);
gboolean gst_v4l2_encoder_query_control_size (GstV4l2Encoder * self,
unsigned int control_id,
unsigned int *control_size);
void gst_v4l2_encoder_install_properties (GObjectClass * gobject_class,
gint prop_offset,
GstV4l2CodecDevice * device);
void gst_v4l2_encoder_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
void gst_v4l2_encoder_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
void gst_v4l2_encoder_register (GstPlugin * plugin,
GType dec_type,
GClassInitFunc class_init,
gconstpointer class_data,
GInstanceInitFunc instance_init,
const gchar *element_name_tmpl,
GstV4l2CodecDevice * device,
guint rank,
gchar ** element_name);
GstV4l2Request *gst_v4l2_encoder_alloc_request (GstV4l2Encoder * self,
guint32 frame_num,
GstBuffer * pic_buf,
GstBuffer * bitstream);
GstV4l2Request *gst_v4l2_encoder_alloc_ro_request (GstV4l2Encoder * self);
GstV4l2Request *gst_v4l2_encoder_alloc_sub_request (GstV4l2Encoder * self,
GstV4l2Request * prev_request,
GstBuffer * bitstream);
GstV4l2Request * gst_v4l2_encoder_request_ref (GstV4l2Request * request);
void gst_v4l2_encoder_request_unref (GstV4l2Request * request);
void gst_v4l2_encoder_ro_request_unref (GstV4l2Request * request);
gboolean gst_v4l2_encoder_request_queue (GstV4l2Request * request,
guint flags);
gint gst_v4l2_encoder_request_set_done (GstV4l2Request * request, guint32 * bytesused, guint32 * flags);
void gst_v4l2_encoder_request_replace_pic_buf (GstV4l2Request * request,
GstBuffer * pic_buf);
gboolean gst_v4l2_codec_vp8_enc_get_qp_range (GstV4l2Encoder * self, guint * qp_min, guint * qp_max);
gboolean gst_v4l2_codec_h264_enc_get_qp_range (GstV4l2Encoder * self, guint * qp_min, guint * qp_max);
gboolean gst_v4l2_encoder_uses_zero_copy (GstV4l2Encoder * self);
G_END_DECLS
#endif /* __GST_V4L2_ENCODER_H__ */

View file

@ -32,6 +32,7 @@ struct FormatEntry
};
static struct FormatEntry format_map[] = {
{V4L2_PIX_FMT_NV12M, 2, GST_VIDEO_FORMAT_NV12, 8, 420},
{V4L2_PIX_FMT_NV12, 1, GST_VIDEO_FORMAT_NV12, 8, 420},
{V4L2_PIX_FMT_YUYV, 1, GST_VIDEO_FORMAT_YUY2, 8, 422},
{V4L2_PIX_FMT_SUNXI_TILED_NV12, 1, GST_VIDEO_FORMAT_NV12_32L32, 8, 422},
@ -42,6 +43,13 @@ static struct FormatEntry format_map[] = {
{V4L2_PIX_FMT_NV15_4L4, 1, GST_VIDEO_FORMAT_NV12_10LE40_4L4, 10, 420},
{V4L2_PIX_FMT_MT2110T, 2, GST_VIDEO_FORMAT_MT2110T, 10, 420},
{V4L2_PIX_FMT_MT2110R, 2, GST_VIDEO_FORMAT_MT2110R, 10, 420},
{V4L2_PIX_FMT_RGB565, 1, GST_VIDEO_FORMAT_RGB16, 16, 0},
{V4L2_PIX_FMT_XBGR32, 1, GST_VIDEO_FORMAT_BGRx, 32, 0},
{V4L2_PIX_FMT_BGR32, 1, GST_VIDEO_FORMAT_BGRx, 32, 0},
{V4L2_PIX_FMT_RGBX32, 1, GST_VIDEO_FORMAT_RGBx, 32, 0},
{V4L2_PIX_FMT_BGRX32, 1, GST_VIDEO_FORMAT_xBGR, 32, 0},
{V4L2_PIX_FMT_XRGB32, 1, GST_VIDEO_FORMAT_xRGB, 32, 0},
{V4L2_PIX_FMT_RGB32, 1, GST_VIDEO_FORMAT_xRGB, 32, 0},
{0,}
};
@ -203,3 +211,23 @@ gst_v4l2_format_from_video_format (GstVideoFormat format, guint32 * out_pix_fmt)
*out_pix_fmt = entry->v4l2_pix_fmt;
return TRUE;
}
gboolean
gst_v4l2_format_equivalent (guint32 pix_fmt_a, guint32 pix_fmt_b)
{
struct FormatEntry *entry_a, *entry_b;
if (pix_fmt_a == pix_fmt_b)
return TRUE;
entry_a = lookup_v4l2_fmt (pix_fmt_a);
entry_b = lookup_v4l2_fmt (pix_fmt_b);
if (!entry_a || !entry_b)
return FALSE;
if (entry_a->gst_fmt == entry_b->gst_fmt)
return TRUE;
return FALSE;
}

View file

@ -26,7 +26,8 @@
#define GST_V4L2_DEFAULT_VIDEO_FORMATS "{ " \
"P010_10LE, NV12_10LE40_4L4, MT2110T, MT2110R," \
"NV12, YUY2, NV12_4L4, NV12_32L32, NV12_16L32S, I420" \
"NV12, YUY2, NV12_4L4, NV12_32L32, NV12_16L32S, I420," \
"RGB16, BGRx, RGBx, xBGR, xRGB" \
"}"
gboolean gst_v4l2_format_to_video_info (struct v4l2_format * fmt,
@ -38,4 +39,6 @@ gboolean gst_v4l2_format_to_video_format (guint32 pix_fmt,
gboolean gst_v4l2_format_from_video_format (GstVideoFormat format,
guint32 * out_pix_fmt);
gboolean gst_v4l2_format_equivalent (guint32 pix_fmt_a, guint32 pix_fmt_b);
#endif /* __GST_V4L2_FORMAT_H__ */

View file

@ -1675,6 +1675,91 @@ struct v4l2_ctrl_h264_decode_params {
__u32 flags;
};
#define V4L2_H264_NAL_CODED_SLICE_NON_IDR_PIC 1
#define V4L2_H264_NAL_CODED_SLICE_IDR_PIC 5
#define V4L2_CID_STATELESS_H264_ENCODE_PARAMS (V4L2_CID_CODEC_STATELESS_BASE + 8)
/**
* struct v4l2_ctrl_h264_encode_params
* @slice_type: selects slice type. Set to one of V4L2_H264_SLICE_TYPE_{}
* @pic_parameter_set_id: identifies the picture parameter set that is referred to
* in the slice header. The value shall be in the range of 0 to 255, inclusive.
* @frame_num: an identifier for pictures.
* @idr_pic_id: identifies an IDR picture.
* @cabac_init_idc: index for determining the initialization table used in the
* initialization process for context variables. The value of cabac_init_idc
* shall be in the range of 0 to 2, inclusive.
* @disable_deblocking_filter_idc: specifies whether the operation of the
* deblocking filter shall be disabled across some block edges of the slice and
* specifies for which edges the filtering is disabled.
* @slice_alpha_c0_offset_div2: offset used in accessing the alpha and tC0
* deblocking filter tables for filtering operations controlled by the macroblocks
* within the slice.
* @slice_beta_offset_div2: offset used in accessing the beta deblocking filter
* table for filtering operations controlled by the macroblocks within the slice.
* @slice_size_mb_rows: number of macroblock rows in a slice.
* @pic_init_qp_minus26: initial value minus 26 of luma qp for each slice.
* @chroma_qp_index_offset: offset that shall be added to qp luma for addressing the
* table of qp chroma values for the Cb chroma component.
* @flags: combination of V4L2_H264_SLICE_FLAG_{} flags.
*/
struct v4l2_ctrl_h264_encode_params {
/* Slice parameters */
__u8 slice_type;
__u8 pic_parameter_set_id;
__u16 frame_num;
__u16 idr_pic_id;
__u8 cabac_init_idc;
__u8 disable_deblocking_filter_idc;
__s8 slice_alpha_c0_offset_div2;
__s8 slice_beta_offset_div2;
__s32 slice_size_mb_rows;
/* PPS parameters */
__s8 pic_init_qp_minus26;
__s8 chroma_qp_index_offset;
__u32 flags; /* V4L2_H264_ENCODE_FLAG_ */
/* Nal parameters */
__u8 nal_reference_idc; // 2 bit
__u8 nalu_type; // 5 bit
/* Reference */
__u64 reference_ts;
};
#define V4L2_H264_ENCODE_FLAG_ENTROPY_CABAC 0x01
#define V4L2_H264_ENCODE_FLAG_TRANSFORM_8X8_MODE 0x02
#define V4L2_H264_ENCODE_FLAG_CONSTRAINED_INTRA_PRED 0x04
#define V4L2_H264_ENCODE_FLAG_MARK_LONGTERM 0x08
#define V4L2_CID_STATELESS_H264_ENCODE_RC (V4L2_CID_CODEC_STATELESS_BASE + 9)
/**
* struct v4l2_ctrl_h264_encode_rc
*
* @qp: quantization parameter for the currently encoded slice
*
* TODO: other fields likely not needed
*/
struct v4l2_ctrl_h264_encode_rc {
__u32 qp;
__u32 qp_min;
__u32 qp_max;
__s32 mad_qp_delta;
__u32 mad_threshold;
__u32 cp_distance_mbs;
__u32 cp_target[10];
__s32 cp_target_error[6];
__s32 cp_qp_delta[7];
};
/* Stateless FWHT control, used by the vicodec driver */
@ -3473,6 +3558,96 @@ struct v4l2_ctrl_av1_film_grain {
__u16 cr_offset;
__u8 reserved[4];
};
#define V4L2_CID_STATELESS_VP8_ENCODE_PARAMS (V4L2_CID_CODEC_STATELESS_BASE + 601)
#define V4L2_VP8_FRAME_FLAG_SHOWFRAME 0x1
#define V4L2_VP8_FRAME_FLAG_GOLDEN_REFRESH 0x2
#define V4L2_VP8_FRAME_FLAG_ALTREF_REFRESH 0x4
#define V4L2_VP8_FRAME_FLAG_SEGMENT_ENABLED 0x8
#define V4L2_VP8_FRAME_FLAG_LOOP_FILTER_ADJ_ENABLED 0x10
#define V4L2_VP8_FRAME_FLAG_REFRESH_ENTROPY_PROBS 0x20
#define V4L2_VP8_FRAME_TYPE_KEYFRAME 0
#define V4L2_VP8_FRAME_TYPE_INTER 1
#define V4L2_VP8_FRAME_COLOR_SPACE_YUV 0
#define V4L2_VP8_FRAME_COLOR_SPACE_RESERVED 1
#define V4L2_VP8_FRAME_CLAMPING_REQUIRED 0
#define V4L2_VP8_FRAME_CLAMPING_NO 1
#define V4L2_VP8_FRAME_FILTER_TYPE_NORMAL 0
#define V4L2_VP8_FRAME_FILTER_TYPE_SIMPLE 1
#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_1 0
#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_2 1
#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_4 2
#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_8 3
#define V4L2_VP8_FRAME_GOLDEN_KEEP 0
#define V4L2_VP8_FRAME_GOLDEN_LASTFRAME 1
#define V4L2_VP8_FRAME_GOLDEN_ALTREF 2
#define V4L2_VP8_FRAME_ALTREF_KEEP 0
#define V4L2_VP8_FRAME_ALTREF_LASTFRAME 1
#define V4L2_VP8_FRAME_ALTREF_GOLDEN 2
#define V4L2_VP8_FRAME_REF_LAST 0
#define V4L2_VP8_FRAME_REF_GOLDEN 1
#define V4L2_VP8_FRAME_REF_ALT 2
/**
* struct v4l2_ctrl_vp8_encode_params - VP8 encode parameters
* @flags: combination of V4L2_VP8_FRAME_FLAG_{} flags.
* @frame_type: specifies the frame type (key or inter).
* Set to one of V4L2_VP8_FRAME_TYPE_{}.
* @color_space: defines the YUV color space of the sequence.
* V4L2_VP8_FRAME_TYPE_INTER frames shall set this field to zero.
* Set to one of V4L2_VP8_FRAME_COLOR_SPACE_{}.
* @clamping_type: defines pixel value clamping type.
* V4L2_VP8_FRAME_TYPE_INTER frames shall set this field to zero.
* Set to one of V4L2_VP8_FRAME_CLAMPING_{}.
* @loop_filter_type: selects the type of loop filter applied.
* Set to one of V4L2_VP8_FRAME_FILTER_TYPE_{}.
* @loop_filter_level: sets the strength of the applied loop filter.
* Set to a value from the rage 0..63.
* @sharpness_level: sets the sharpness of the applied loop filter.
* Set to a value from the range 0..7.
* @log2_nbr_of_dct_partitions: determines the number of separate partitions
* containing the DCT coefficients of macroblocks.
* Set to one of V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_{}.
* @prob_intra: indicates the probability of an intra macroblock.
* Set to a value from the range 0..255.
* @prob_last: indicates the probability that the last reference frame is used for inter-prediction.
* Set to a value from the range 0..255.
* @prob_gf: indicates the probability that the golden reference frame is used for inter-prediction.
* Set to a value from the range 0..255.
* @copy_buffer_to_golden: specifies the golden frame refresh strategy.
* Set to one of V4L2_VP8_FRAME_FLAG_GOLDEN_{}.
* @copy_buffer_to_alternate: specifies the atlref frame refresh strategy.
* Set to one of V4L2_VP8_FRAME_FLAG_ALTREF_{}.
* @reference_type: specifies what kind of reference to use for current inter frame.
* V4L2_VP8_FRAME_TYPE_KEYFRAME shall set this field to zero.
* Set to one of V4L2_VP8_FRAME_REF_{}.
*/
struct v4l2_ctrl_vp8_encode_params {
__u32 flags;
__u8 frame_type;
__u8 color_space;
__u8 clamping_type;
__u8 loop_filter_type;
__u8 loop_filter_level;
__u8 sharpness_level;
__u8 log2_nbr_of_dct_partitions;
__u8 prob_intra;
__u8 prob_last;
__u8 prob_gf;
__u8 copy_buffer_to_golden;
__u8 copy_buffer_to_alternate;
__u8 reference_type;
};
#define V4L2_CID_STATELESS_VP8_ENCODE_QP (V4L2_CID_CODEC_STATELESS_BASE + 602)
/* MPEG-compression definitions kept for backwards compatibility */
#define V4L2_CTRL_CLASS_MPEG V4L2_CTRL_CLASS_CODEC

View file

@ -1784,6 +1784,7 @@ struct v4l2_ext_control {
struct v4l2_ctrl_av1_tile_group_entry *p_av1_tile_group_entry;
struct v4l2_ctrl_av1_frame *p_av1_frame;
struct v4l2_ctrl_av1_film_grain *p_av1_film_grain;
struct v4l2_ctrl_vp8_encode_params *p_vp8_encode_params;
void *ptr;
};
} __attribute__ ((packed));
@ -1836,6 +1837,9 @@ enum v4l2_ctrl_type {
V4L2_CTRL_TYPE_H264_SLICE_PARAMS = 0x0203,
V4L2_CTRL_TYPE_H264_DECODE_PARAMS = 0x0204,
V4L2_CTRL_TYPE_H264_PRED_WEIGHTS = 0x0205,
V4L2_CTRL_TYPE_H264_ENCODE_PARAMS = 0x0206,
V4L2_CTRL_TYPE_H264_ENCODE_RC = 0x0207,
V4L2_CTRL_TYPE_H264_ENCODE_FEEDBACK = 0x0208,
V4L2_CTRL_TYPE_FWHT_PARAMS = 0x0220,
@ -1858,6 +1862,7 @@ enum v4l2_ctrl_type {
V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY = 0x281,
V4L2_CTRL_TYPE_AV1_FRAME = 0x282,
V4L2_CTRL_TYPE_AV1_FILM_GRAIN = 0x283,
V4L2_CTRL_TYPE_VP8_ENCODE_PARAMS = 0x0280,
};
/* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */

View file

@ -12,6 +12,9 @@ v4l2codecs_sources = [
'gstv4l2decoder.c',
'gstv4l2format.c',
'gstv4l2codecalphadecodebin.c',
'gstv4l2encoder.c',
'gstv4l2codecvp8enc.c',
'gstv4l2codech264enc.c',
]
libgudev_dep = dependency('gudev-1.0', required: get_option('v4l2codecs'), allow_fallback: true)

View file

@ -25,11 +25,14 @@
#include "gstv4l2codecav1dec.h"
#include "gstv4l2codecdevice.h"
#include "gstv4l2codech264dec.h"
#include "gstv4l2codech264enc.h"
#include "gstv4l2codech265dec.h"
#include "gstv4l2codecmpeg2dec.h"
#include "gstv4l2codecvp8dec.h"
#include "gstv4l2codecvp8enc.h"
#include "gstv4l2codecvp9dec.h"
#include "gstv4l2decoder.h"
#include "gstv4l2encoder.h"
#include "linux/v4l2-controls.h"
#include "linux/media.h"
@ -97,6 +100,42 @@ register_video_decoder (GstPlugin * plugin, GstV4l2CodecDevice * device)
g_object_unref (decoder);
}
static void
register_video_encoder (GstPlugin * plugin, GstV4l2CodecDevice * device)
{
GstV4l2Encoder *encoder = gst_v4l2_encoder_new (device);
gint i;
guint32 fmt;
if (!gst_v4l2_encoder_open (encoder)) {
g_object_unref (encoder);
return;
}
for (i = 0; gst_v4l2_encoder_enum_src_formats (encoder, i, &fmt); i++) {
switch (fmt) {
case V4L2_PIX_FMT_VP8_FRAME:
GST_INFO_OBJECT (encoder, "Registering %s as VP8 Encoder",
device->name);
gst_v4l2_codec_vp8_enc_register (plugin, encoder, device,
GST_RANK_PRIMARY + 1);
break;
case V4L2_PIX_FMT_H264_SLICE:
GST_INFO_OBJECT (encoder, "Registering %s as H264 Encoder",
device->name);
gst_v4l2_codec_h264_enc_register (plugin, encoder, device,
GST_RANK_PRIMARY + 1);
break;
default:
GST_FIXME_OBJECT (encoder, "%" GST_FOURCC_FORMAT " is not supported.",
GST_FOURCC_ARGS (fmt));
break;
}
}
g_object_unref (encoder);
}
static gboolean
plugin_init (GstPlugin * plugin)
{
@ -118,6 +157,9 @@ plugin_init (GstPlugin * plugin)
if (device->function == MEDIA_ENT_F_PROC_VIDEO_DECODER)
register_video_decoder (plugin, device);
if (device->function == MEDIA_ENT_F_PROC_VIDEO_ENCODER)
register_video_encoder (plugin, device);
}
gst_v4l2_codec_device_list_free (devices);

View file

@ -319,8 +319,6 @@ gst_video_frame_copy_plane (GstVideoFrame * dest, const GstVideoFrame * src,
finfo = dinfo->finfo;
g_return_val_if_fail (dinfo->width <= sinfo->width
&& dinfo->height <= sinfo->height, FALSE);
g_return_val_if_fail (finfo->n_planes > plane, FALSE);
sp = src->data[plane];
@ -333,15 +331,19 @@ gst_video_frame_copy_plane (GstVideoFrame * dest, const GstVideoFrame * src,
}
gst_video_format_info_component (finfo, plane, comp);
w = GST_VIDEO_FRAME_COMP_WIDTH (dest,
comp[0]) * GST_VIDEO_FRAME_COMP_PSTRIDE (dest, comp[0]);
w = MIN (GST_VIDEO_FRAME_COMP_WIDTH (dest, comp[0]),
GST_VIDEO_FRAME_COMP_WIDTH (src, comp[0])) *
GST_VIDEO_FRAME_COMP_PSTRIDE (dest, comp[0]);
/* FIXME: workaround for complex formats like v210, UYVP and IYU1 that have
* pstride == 0 */
if (w == 0)
w = MIN (GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane),
GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane));
h = GST_VIDEO_FRAME_COMP_HEIGHT (dest, comp[0]);
h = MIN (GST_VIDEO_FRAME_COMP_HEIGHT (dest, comp[0]),
GST_VIDEO_FRAME_COMP_HEIGHT (src, comp[0]));
ss = GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane);
ds = GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane);
@ -409,19 +411,12 @@ gboolean
gst_video_frame_copy (GstVideoFrame * dest, const GstVideoFrame * src)
{
guint i, n_planes;
const GstVideoInfo *sinfo;
GstVideoInfo *dinfo;
g_return_val_if_fail (dest != NULL, FALSE);
g_return_val_if_fail (src != NULL, FALSE);
sinfo = &src->info;
dinfo = &dest->info;
g_return_val_if_fail (dinfo->finfo->format == sinfo->finfo->format, FALSE);
g_return_val_if_fail (dinfo->width <= sinfo->width
&& dinfo->height <= sinfo->height, FALSE);
n_planes = dinfo->finfo->n_planes;
for (i = 0; i < n_planes; i++)