blob: 42f0cf85711a74d88ea3da273b79f92fa39ee2ba [file] [log] [blame]
/*
* Copyright (C) 2018 Metrological Group B.V.
* Copyright (C) 2018 Igalia S.L. All rights reserved.
*
* 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
* aint with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* NOTE: This file respects GStreamer coding style as we might want to upstream
* that element in the future */
#include "config.h"
#if ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)
#include "GStreamerVideoEncoder.h"
GST_DEBUG_CATEGORY (gst_webrtcenc_debug);
#define GST_CAT_DEFAULT gst_webrtcenc_debug
#define KBIT_TO_BIT 1024
static GstStaticPadTemplate sinkTemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-raw(ANY);"));
static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-h264;video/x-vp8"));
typedef void (*SetBitrateFunc) (GObject * encoder, const gchar * propname,
gint bitrate);
typedef void (*SetupFunc) (GstWebrtcVideoEncoder * self);
typedef struct
{
gboolean avalaible;
GstCaps *caps;
const gchar *name;
const gchar *parser_name;
GstCaps *encoded_format;
SetBitrateFunc setBitrate;
SetupFunc setupEncoder;
const gchar *bitrate_propname;
const gchar *keyframe_interval_propname;
} EncoderDefinition;
typedef enum
{
ENCODER_NONE = 0,
ENCODER_X264,
ENCODER_OPENH264,
ENCODER_OMXH264,
ENCODER_VP8,
ENCODER_LAST,
} EncoderId;
EncoderDefinition encoders[ENCODER_LAST] = {
FALSE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
typedef struct
{
EncoderId encoderId;
GstElement *encoder;
GstElement *parser;
GstElement *capsfilter;
guint bitrate;
} GstWebrtcVideoEncoderPrivate;
/* *INDENT-OFF* */
G_DEFINE_TYPE_WITH_PRIVATE (GstWebrtcVideoEncoder, gst_webrtc_video_encoder,
GST_TYPE_BIN)
#define PRIV(self) ((GstWebrtcVideoEncoderPrivate*)gst_webrtc_video_encoder_get_instance_private(self))
/* *INDENT-ON* */
enum
{
PROP_0,
PROP_FORMAT,
PROP_ENCODER,
PROP_BITRATE,
PROP_KEYFRAME_INTERVAL,
N_PROPS
};
static void
gst_webrtc_video_encoder_finalize (GObject * object)
{
G_OBJECT_CLASS (gst_webrtc_video_encoder_parent_class)->finalize (object);
}
static void
gst_webrtc_video_encoder_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstWebrtcVideoEncoder *self = GST_WEBRTC_VIDEO_ENCODER (object);
GstWebrtcVideoEncoderPrivate *priv = PRIV (self);
switch (prop_id) {
case PROP_FORMAT:
if (priv->encoderId != ENCODER_NONE)
g_value_set_boxed (value, encoders[priv->encoderId].caps);
else
g_value_set_boxed (value, NULL);
break;
case PROP_ENCODER:
g_value_set_object (value, priv->encoder);
break;
case PROP_BITRATE:
g_value_set_uint (value, priv->bitrate);
break;
case PROP_KEYFRAME_INTERVAL:
if (priv->encoder)
g_object_get_property (G_OBJECT (priv->encoder),
encoders[priv->encoderId].keyframe_interval_propname, value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gst_webrtc_video_encoder_set_bitrate (GstWebrtcVideoEncoder * self,
guint bitrate)
{
GstWebrtcVideoEncoderPrivate *priv = PRIV (self);
priv->bitrate = bitrate;
if (priv->encoder) {
encoders[priv->encoderId].setBitrate (G_OBJECT (priv->encoder),
encoders[priv->encoderId].bitrate_propname, priv->bitrate);
}
}
static void
gst_webrtc_video_encoder_set_format (GstWebrtcVideoEncoder * self,
const GstCaps * caps)
{
gint i;
GstWebrtcVideoEncoderPrivate *priv = PRIV (self);
g_return_if_fail (priv->encoderId == ENCODER_NONE);
g_return_if_fail (caps);
for (i = 1; i < ENCODER_LAST; i++) {
if (encoders[i].avalaible
&& gst_caps_can_intersect (encoders[i].caps, caps)) {
GstPad *tmppad;
priv->encoderId = (EncoderId) i;
priv->encoder = gst_element_factory_make (encoders[i].name, NULL);
if (encoders[i].parser_name)
priv->parser = gst_element_factory_make (encoders[i].parser_name, NULL);
encoders[priv->encoderId].setupEncoder (self);
if (encoders[i].encoded_format) {
priv->capsfilter = gst_element_factory_make ("capsfilter", NULL);
g_object_set (priv->capsfilter, "caps", encoders[i].encoded_format,
NULL);
}
gst_bin_add (GST_BIN (self), priv->encoder);
tmppad = gst_element_get_static_pad (priv->encoder, "sink");
gst_ghost_pad_set_target (GST_GHOST_PAD (GST_ELEMENT (self)->
sinkpads->data), tmppad);
gst_object_unref (tmppad);
tmppad = gst_element_get_static_pad (priv->encoder, "src");
if (priv->parser) {
gst_bin_add (GST_BIN (self), priv->parser);
gst_element_link (priv->encoder, priv->parser);
gst_object_unref (tmppad);
tmppad = gst_element_get_static_pad (priv->parser, "src");
}
if (priv->capsfilter) {
GstPad *tmppad2 = gst_element_get_static_pad (priv->capsfilter, "sink");
gst_bin_add (GST_BIN (self), priv->capsfilter);
gst_pad_link (tmppad, tmppad2);
gst_object_unref (tmppad);
tmppad = gst_element_get_static_pad (priv->capsfilter, "src");
gst_object_unref (tmppad2);
}
g_assert (gst_ghost_pad_set_target (GST_GHOST_PAD (GST_ELEMENT
(self)->srcpads->data), tmppad));
gst_object_unref (tmppad);
gst_webrtc_video_encoder_set_bitrate (self, priv->bitrate);
return;
}
}
GST_ERROR ("No encoder found for format %" GST_PTR_FORMAT, caps);
}
static void
gst_webrtc_video_encoder_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstWebrtcVideoEncoder *self = GST_WEBRTC_VIDEO_ENCODER (object);
GstWebrtcVideoEncoderPrivate *priv = PRIV (self);
switch (prop_id) {
case PROP_FORMAT:
gst_webrtc_video_encoder_set_format (self, gst_value_get_caps (value));
break;
case PROP_BITRATE:
gst_webrtc_video_encoder_set_bitrate (self, g_value_get_uint (value));
break;
case PROP_KEYFRAME_INTERVAL:
if (priv->encoder)
g_object_set (priv->encoder,
encoders[priv->encoderId].keyframe_interval_propname,
g_value_get_uint (value), NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
register_known_encoder (EncoderId encId, const gchar * name,
const gchar * parser_name, const gchar * caps, const gchar * encoded_format,
SetupFunc setupEncoder, const gchar * bitrate_propname,
SetBitrateFunc setBitrate, const gchar * keyframe_interval_propname)
{
GstPluginFeature *feature =
gst_registry_lookup_feature (gst_registry_get (), name);
if (!feature) {
GST_WARNING ("Could not find %s", name);
encoders[encId].avalaible = FALSE;
return;
}
gst_object_unref (feature);
encoders[encId].avalaible = TRUE;
encoders[encId].name = name;
encoders[encId].parser_name = parser_name;
encoders[encId].caps = gst_caps_from_string (caps);
if (encoded_format)
encoders[encId].encoded_format = gst_caps_from_string (encoded_format);
else
encoders[encId].encoded_format = NULL;
encoders[encId].setupEncoder = setupEncoder;
encoders[encId].bitrate_propname = bitrate_propname;
encoders[encId].setBitrate = setBitrate;
encoders[encId].keyframe_interval_propname = keyframe_interval_propname;
}
static void
setup_x264enc (GstWebrtcVideoEncoder * self)
{
gst_util_set_object_arg (G_OBJECT (PRIV (self)->encoder), "tune",
"zerolatency");
g_object_set (PRIV (self)->parser, "config-interval", 1, NULL);
}
static void
setup_openh264enc (GstWebrtcVideoEncoder * self)
{
g_object_set (PRIV (self)->parser, "config-interval", 1, NULL);
}
static void
setup_omxh264enc (GstWebrtcVideoEncoder * self)
{
gst_util_set_object_arg (G_OBJECT (PRIV (self)->encoder), "control-rate",
"variable");
g_object_set (PRIV (self)->parser, "config-interval", 1, NULL);
}
static void
set_bitrate_kbit_per_sec (GObject * encoder, const gchar * prop_name,
gint bitrate)
{
g_object_set (encoder, prop_name, bitrate, NULL);
}
static void
set_bitrate_bit_per_sec (GObject * encoder, const gchar * prop_name,
gint bitrate)
{
g_object_set (encoder, prop_name, bitrate * KBIT_TO_BIT, NULL);
}
static void
gst_webrtc_video_encoder_class_init (GstWebrtcVideoEncoderClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (gst_webrtcenc_debug, "webrtcencoder", 0,
"Video encoder for WebRTC");
object_class->finalize = gst_webrtc_video_encoder_finalize;
object_class->get_property = gst_webrtc_video_encoder_get_property;
object_class->set_property = gst_webrtc_video_encoder_set_property;
g_object_class_install_property (object_class, PROP_FORMAT,
g_param_spec_boxed ("format", "Format as caps",
"Set the caps of the format to be used.",
GST_TYPE_CAPS,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_ENCODER,
g_param_spec_object ("encoder", "The actual encoder element",
"The encoder element", GST_TYPE_ELEMENT,
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_BITRATE,
g_param_spec_uint ("bitrate", "Bitrate",
"The bitrate in kbit per second", 0, G_MAXINT, 2048,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT)));
g_object_class_install_property (object_class, PROP_KEYFRAME_INTERVAL,
g_param_spec_uint ("keyframe-interval", "Keyframe interval",
"The interval between keyframes", 0, G_MAXINT, 0,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT)));
register_known_encoder (ENCODER_OMXH264, "omxh264enc", "h264parse",
"video/x-h264",
"video/x-h264,alignment=au,stream-format=byte-stream,profile=baseline",
setup_omxh264enc, "target-bitrate", set_bitrate_bit_per_sec, "interval-intraframes");
register_known_encoder (ENCODER_X264, "x264enc", "h264parse", "video/x-h264",
"video/x-h264,alignment=au,stream-format=byte-stream,profile=baseline",
setup_x264enc, "bitrate", set_bitrate_kbit_per_sec, "key-int-max");
register_known_encoder (ENCODER_OPENH264, "openh264enc", "h264parse",
"video/x-h264",
"video/x-h264,alignment=au,stream-format=byte-stream,profile=baseline",
setup_openh264enc, "bitrate", set_bitrate_bit_per_sec, "gop-size");
}
static void
gst_webrtc_video_encoder_init (GstWebrtcVideoEncoder * self)
{
GstWebrtcVideoEncoderPrivate *priv = PRIV (self);
priv->encoderId = ENCODER_NONE;
gst_element_add_pad (GST_ELEMENT (self),
gst_ghost_pad_new_no_target_from_template ("sink",
gst_static_pad_template_get (&sinkTemplate)));
gst_element_add_pad (GST_ELEMENT (self),
gst_ghost_pad_new_no_target_from_template ("src",
gst_static_pad_template_get (&srcTemplate)));
}
#endif // ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)