| /* |
| * 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) |