mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-05-18 00:12:46 +00:00
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:
commit
9f8169527e
394
subprojects/gst-plugins-bad/gst-libs/gst/codecs/gsth264encoder.c
Normal file
394
subprojects/gst-plugins-bad/gst-libs/gst/codecs/gsth264encoder.c
Normal 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));
|
||||
}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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 ();
|
||||
}
|
|
@ -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
|
351
subprojects/gst-plugins-bad/gst-libs/gst/codecs/gstvp8encoder.c
Normal file
351
subprojects/gst-plugins-bad/gst-libs/gst/codecs/gstvp8encoder.c
Normal 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));
|
||||
}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
1209
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2codech264enc.c
Normal file
1209
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2codech264enc.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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__ */
|
745
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2codecvp8enc.c
Normal file
745
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2codecvp8enc.c
Normal 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);
|
||||
}
|
|
@ -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__ */
|
1370
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2encoder.c
Normal file
1370
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2encoder.c
Normal file
File diff suppressed because it is too large
Load diff
145
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2encoder.h
Normal file
145
subprojects/gst-plugins-bad/sys/v4l2codecs/gstv4l2encoder.h
Normal 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__ */
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++)
|
||||
|
|
Loading…
Reference in a new issue