1/*
2 * Copyright (C) 2018 Metrological Group B.V.
3 * Copyright (C) 2018 Igalia S.L. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * aint with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21/* NOTE: This file respects GStreamer coding style as we might want to upstream
22 * that element in the future */
23
24#include "config.h"
25
26#if ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)
27#include "GStreamerVideoEncoder.h"
28
29GST_DEBUG_CATEGORY (gst_webrtcenc_debug);
30#define GST_CAT_DEFAULT gst_webrtcenc_debug
31
32#define KBIT_TO_BIT 1024
33
34static GstStaticPadTemplate sinkTemplate = GST_STATIC_PAD_TEMPLATE ("sink",
35 GST_PAD_SINK,
36 GST_PAD_ALWAYS,
37 GST_STATIC_CAPS ("video/x-raw(ANY);"));
38
39static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE ("src",
40 GST_PAD_SRC,
41 GST_PAD_ALWAYS,
42 GST_STATIC_CAPS ("video/x-h264;video/x-vp8"));
43
44typedef void (*SetBitrateFunc) (GObject * encoder, const gchar * propname,
45 gint bitrate);
46typedef void (*SetupFunc) (GstWebrtcVideoEncoder * self);
47typedef struct
48{
49 gboolean avalaible;
50 GstCaps *caps;
51 const gchar *name;
52 const gchar *parser_name;
53 GstCaps *encoded_format;
54 SetBitrateFunc setBitrate;
55 SetupFunc setupEncoder;
56 const gchar *bitrate_propname;
57 const gchar *keyframe_interval_propname;
58} EncoderDefinition;
59
60typedef enum
61{
62 ENCODER_NONE = 0,
63 ENCODER_X264,
64 ENCODER_OPENH264,
65 ENCODER_OMXH264,
66 ENCODER_VP8,
67 ENCODER_LAST,
68} EncoderId;
69
70EncoderDefinition encoders[ENCODER_LAST] = {
71 FALSE,
72 NULL,
73 NULL,
74 NULL,
75 NULL,
76 NULL,
77 NULL,
78 NULL,
79 NULL,
80};
81
82typedef struct
83{
84 EncoderId encoderId;
85 GstElement *encoder;
86 GstElement *parser;
87 GstElement *capsfilter;
88 guint bitrate;
89} GstWebrtcVideoEncoderPrivate;
90
91/* *INDENT-OFF* */
92G_DEFINE_TYPE_WITH_PRIVATE (GstWebrtcVideoEncoder, gst_webrtc_video_encoder,
93 GST_TYPE_BIN)
94#define PRIV(self) ((GstWebrtcVideoEncoderPrivate*)gst_webrtc_video_encoder_get_instance_private(self))
95/* *INDENT-ON* */
96
97enum
98{
99 PROP_0,
100 PROP_FORMAT,
101 PROP_ENCODER,
102 PROP_BITRATE,
103 PROP_KEYFRAME_INTERVAL,
104 N_PROPS
105};
106
107static void
108gst_webrtc_video_encoder_finalize (GObject * object)
109{
110 G_OBJECT_CLASS (gst_webrtc_video_encoder_parent_class)->finalize (object);
111}
112
113static void
114gst_webrtc_video_encoder_get_property (GObject * object,
115 guint prop_id, GValue * value, GParamSpec * pspec)
116{
117 GstWebrtcVideoEncoder *self = GST_WEBRTC_VIDEO_ENCODER (object);
118 GstWebrtcVideoEncoderPrivate *priv = PRIV (self);
119
120 switch (prop_id) {
121 case PROP_FORMAT:
122 if (priv->encoderId != ENCODER_NONE)
123 g_value_set_boxed (value, encoders[priv->encoderId].caps);
124 else
125 g_value_set_boxed (value, NULL);
126 break;
127 case PROP_ENCODER:
128 g_value_set_object (value, priv->encoder);
129 break;
130 case PROP_BITRATE:
131 g_value_set_uint (value, priv->bitrate);
132 break;
133 case PROP_KEYFRAME_INTERVAL:
134 if (priv->encoder)
135 g_object_get_property (G_OBJECT (priv->encoder),
136 encoders[priv->encoderId].keyframe_interval_propname, value);
137 break;
138 default:
139 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
140 }
141}
142
143static void
144gst_webrtc_video_encoder_set_bitrate (GstWebrtcVideoEncoder * self,
145 guint bitrate)
146{
147 GstWebrtcVideoEncoderPrivate *priv = PRIV (self);
148
149 priv->bitrate = bitrate;
150 if (priv->encoder) {
151 encoders[priv->encoderId].setBitrate (G_OBJECT (priv->encoder),
152 encoders[priv->encoderId].bitrate_propname, priv->bitrate);
153 }
154}
155
156static void
157gst_webrtc_video_encoder_set_format (GstWebrtcVideoEncoder * self,
158 const GstCaps * caps)
159{
160 gint i;
161 GstWebrtcVideoEncoderPrivate *priv = PRIV (self);
162 g_return_if_fail (priv->encoderId == ENCODER_NONE);
163 g_return_if_fail (caps);
164
165 for (i = 1; i < ENCODER_LAST; i++) {
166 if (encoders[i].avalaible
167 && gst_caps_can_intersect (encoders[i].caps, caps)) {
168 GstPad *tmppad;
169 priv->encoderId = (EncoderId) i;
170 priv->encoder = gst_element_factory_make (encoders[i].name, NULL);
171
172 if (encoders[i].parser_name)
173 priv->parser = gst_element_factory_make (encoders[i].parser_name, NULL);
174
175 encoders[priv->encoderId].setupEncoder (self);
176 if (encoders[i].encoded_format) {
177 priv->capsfilter = gst_element_factory_make ("capsfilter", NULL);
178 g_object_set (priv->capsfilter, "caps", encoders[i].encoded_format,
179 NULL);
180 }
181
182 gst_bin_add (GST_BIN (self), priv->encoder);
183
184 tmppad = gst_element_get_static_pad (priv->encoder, "sink");
185 gst_ghost_pad_set_target (GST_GHOST_PAD (GST_ELEMENT (self)->
186 sinkpads->data), tmppad);
187 gst_object_unref (tmppad);
188
189 tmppad = gst_element_get_static_pad (priv->encoder, "src");
190 if (priv->parser) {
191 gst_bin_add (GST_BIN (self), priv->parser);
192 gst_element_link (priv->encoder, priv->parser);
193 gst_object_unref (tmppad);
194 tmppad = gst_element_get_static_pad (priv->parser, "src");
195 }
196
197 if (priv->capsfilter) {
198 GstPad *tmppad2 = gst_element_get_static_pad (priv->capsfilter, "sink");
199
200 gst_bin_add (GST_BIN (self), priv->capsfilter);
201 gst_pad_link (tmppad, tmppad2);
202 gst_object_unref (tmppad);
203 tmppad = gst_element_get_static_pad (priv->capsfilter, "src");
204 gst_object_unref (tmppad2);
205 }
206
207 g_assert (gst_ghost_pad_set_target (GST_GHOST_PAD (GST_ELEMENT
208 (self)->srcpads->data), tmppad));
209 gst_object_unref (tmppad);
210
211 gst_webrtc_video_encoder_set_bitrate (self, priv->bitrate);
212 return;
213 }
214 }
215
216 GST_ERROR ("No encoder found for format %" GST_PTR_FORMAT, caps);
217}
218
219static void
220gst_webrtc_video_encoder_set_property (GObject * object,
221 guint prop_id, const GValue * value, GParamSpec * pspec)
222{
223 GstWebrtcVideoEncoder *self = GST_WEBRTC_VIDEO_ENCODER (object);
224 GstWebrtcVideoEncoderPrivate *priv = PRIV (self);
225
226 switch (prop_id) {
227 case PROP_FORMAT:
228 gst_webrtc_video_encoder_set_format (self, gst_value_get_caps (value));
229 break;
230 case PROP_BITRATE:
231 gst_webrtc_video_encoder_set_bitrate (self, g_value_get_uint (value));
232 break;
233 case PROP_KEYFRAME_INTERVAL:
234 if (priv->encoder)
235 g_object_set (priv->encoder,
236 encoders[priv->encoderId].keyframe_interval_propname,
237 g_value_get_uint (value), NULL);
238
239 break;
240 default:
241 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
242 }
243}
244
245static void
246register_known_encoder (EncoderId encId, const gchar * name,
247 const gchar * parser_name, const gchar * caps, const gchar * encoded_format,
248 SetupFunc setupEncoder, const gchar * bitrate_propname,
249 SetBitrateFunc setBitrate, const gchar * keyframe_interval_propname)
250{
251 GstPluginFeature *feature =
252 gst_registry_lookup_feature (gst_registry_get (), name);
253 if (!feature) {
254 GST_WARNING ("Could not find %s", name);
255 encoders[encId].avalaible = FALSE;
256
257 return;
258 }
259 gst_object_unref (feature);
260
261 encoders[encId].avalaible = TRUE;
262 encoders[encId].name = name;
263 encoders[encId].parser_name = parser_name;
264 encoders[encId].caps = gst_caps_from_string (caps);
265 if (encoded_format)
266 encoders[encId].encoded_format = gst_caps_from_string (encoded_format);
267 else
268 encoders[encId].encoded_format = NULL;
269 encoders[encId].setupEncoder = setupEncoder;
270 encoders[encId].bitrate_propname = bitrate_propname;
271 encoders[encId].setBitrate = setBitrate;
272 encoders[encId].keyframe_interval_propname = keyframe_interval_propname;
273}
274
275static void
276setup_x264enc (GstWebrtcVideoEncoder * self)
277{
278 gst_util_set_object_arg (G_OBJECT (PRIV (self)->encoder), "tune",
279 "zerolatency");
280 g_object_set (PRIV (self)->parser, "config-interval", 1, NULL);
281}
282
283static void
284setup_openh264enc (GstWebrtcVideoEncoder * self)
285{
286 g_object_set (PRIV (self)->parser, "config-interval", 1, NULL);
287}
288
289static void
290setup_omxh264enc (GstWebrtcVideoEncoder * self)
291{
292 gst_util_set_object_arg (G_OBJECT (PRIV (self)->encoder), "control-rate",
293 "variable");
294 g_object_set (PRIV (self)->parser, "config-interval", 1, NULL);
295}
296
297
298static void
299set_bitrate_kbit_per_sec (GObject * encoder, const gchar * prop_name,
300 gint bitrate)
301{
302 g_object_set (encoder, prop_name, bitrate, NULL);
303}
304
305static void
306set_bitrate_bit_per_sec (GObject * encoder, const gchar * prop_name,
307 gint bitrate)
308{
309 g_object_set (encoder, prop_name, bitrate * KBIT_TO_BIT, NULL);
310}
311
312static void
313gst_webrtc_video_encoder_class_init (GstWebrtcVideoEncoderClass * klass)
314{
315 GObjectClass *object_class = G_OBJECT_CLASS (klass);
316
317 GST_DEBUG_CATEGORY_INIT (gst_webrtcenc_debug, "webrtcencoder", 0,
318 "Video encoder for WebRTC");
319
320 object_class->finalize = gst_webrtc_video_encoder_finalize;
321 object_class->get_property = gst_webrtc_video_encoder_get_property;
322 object_class->set_property = gst_webrtc_video_encoder_set_property;
323
324 g_object_class_install_property (object_class, PROP_FORMAT,
325 g_param_spec_boxed ("format", "Format as caps",
326 "Set the caps of the format to be used.",
327 GST_TYPE_CAPS,
328 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
329
330 g_object_class_install_property (object_class, PROP_ENCODER,
331 g_param_spec_object ("encoder", "The actual encoder element",
332 "The encoder element", GST_TYPE_ELEMENT,
333 (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
334
335 g_object_class_install_property (object_class, PROP_BITRATE,
336 g_param_spec_uint ("bitrate", "Bitrate",
337 "The bitrate in kbit per second", 0, G_MAXINT, 2048,
338 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
339 G_PARAM_CONSTRUCT)));
340
341 g_object_class_install_property (object_class, PROP_KEYFRAME_INTERVAL,
342 g_param_spec_uint ("keyframe-interval", "Keyframe interval",
343 "The interval between keyframes", 0, G_MAXINT, 0,
344 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
345 G_PARAM_CONSTRUCT)));
346
347 register_known_encoder (ENCODER_OMXH264, "omxh264enc", "h264parse",
348 "video/x-h264",
349 "video/x-h264,alignment=au,stream-format=byte-stream,profile=baseline",
350 setup_omxh264enc, "target-bitrate", set_bitrate_bit_per_sec, "interval-intraframes");
351 register_known_encoder (ENCODER_X264, "x264enc", "h264parse", "video/x-h264",
352 "video/x-h264,alignment=au,stream-format=byte-stream,profile=baseline",
353 setup_x264enc, "bitrate", set_bitrate_kbit_per_sec, "key-int-max");
354 register_known_encoder (ENCODER_OPENH264, "openh264enc", "h264parse",
355 "video/x-h264",
356 "video/x-h264,alignment=au,stream-format=byte-stream,profile=baseline",
357 setup_openh264enc, "bitrate", set_bitrate_kbit_per_sec, "gop-size");
358}
359
360static void
361gst_webrtc_video_encoder_init (GstWebrtcVideoEncoder * self)
362{
363 GstWebrtcVideoEncoderPrivate *priv = PRIV (self);
364
365 priv->encoderId = ENCODER_NONE;
366 gst_element_add_pad (GST_ELEMENT (self),
367 gst_ghost_pad_new_no_target_from_template ("sink",
368 gst_static_pad_template_get (&sinkTemplate)));
369
370 gst_element_add_pad (GST_ELEMENT (self),
371 gst_ghost_pad_new_no_target_from_template ("src",
372 gst_static_pad_template_get (&srcTemplate)));
373}
374
375#endif // ENABLE(VIDEO) && ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)
376