rtpopuspay: Return upstream channel filter based on OPUS vs MULTICAPS
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-good / gst / rtp / gstrtpopuspay.c
1 /*
2  * Opus Payloader Gst Element
3  *
4  *   @author: Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * SECTION:element-rtpopuspay
24  * @title: rtpopuspay
25  *
26  * rtpopuspay encapsulates Opus-encoded audio data into RTP packets following
27  * the payload format described in RFC 7587.
28  *
29  * In addition to the RFC, which assumes only mono and stereo payload,
30  * the element supports multichannel Opus audio streams using a non-standardized
31  * SDP config and "MULTIOPUS" codec developed by Google for libwebrtc. When the
32  * input data have more than 2 channels, rtpopuspay will add extra fields to
33  * output caps that can be used to generate SDP in the syntax understood by
34  * libwebrtc. For example in the case of 5.1 audio:
35  *
36  * |[
37  *  a=rtpmap:96 multiopus/48000/6
38  *  a=fmtp:96 num_streams=4;coupled_streams=2;channel_mapping=0,4,1,2,3,5
39  * ]|
40  *
41  * See https://webrtc-review.googlesource.com/c/src/+/129768 for more details on
42  * multichannel Opus in libwebrtc.
43  */
44
45 #ifdef HAVE_CONFIG_H
46 #  include "config.h"
47 #endif
48
49 #include <string.h>
50
51 #include <gst/rtp/gstrtpbuffer.h>
52 #include <gst/audio/audio.h>
53
54 #include "gstrtpelements.h"
55 #include "gstrtpopuspay.h"
56 #include "gstrtputils.h"
57
58 GST_DEBUG_CATEGORY_STATIC (rtpopuspay_debug);
59 #define GST_CAT_DEFAULT (rtpopuspay_debug)
60
61 enum
62 {
63   PROP_0,
64   PROP_DTX,
65 };
66
67 #define DEFAULT_DTX FALSE
68
69 static GstStaticPadTemplate gst_rtp_opus_pay_sink_template =
70     GST_STATIC_PAD_TEMPLATE ("sink",
71     GST_PAD_SINK,
72     GST_PAD_ALWAYS,
73     GST_STATIC_CAPS ("audio/x-opus, channel-mapping-family = (int) 0;"
74         "audio/x-opus, channel-mapping-family = (int) 0, channels = (int) [1, 2];"
75         "audio/x-opus, channel-mapping-family = (int) 1, channels = (int) [3, 255]")
76     );
77
78 static GstStaticPadTemplate gst_rtp_opus_pay_src_template =
79 GST_STATIC_PAD_TEMPLATE ("src",
80     GST_PAD_SRC,
81     GST_PAD_ALWAYS,
82     GST_STATIC_CAPS ("application/x-rtp, "
83         "media = (string) \"audio\", "
84         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
85         "clock-rate = (int) 48000, "
86         "encoding-name = (string) { \"OPUS\", \"X-GST-OPUS-DRAFT-SPITTKA-00\", \"MULTIOPUS\" }")
87     );
88
89 static gboolean gst_rtp_opus_pay_setcaps (GstRTPBasePayload * payload,
90     GstCaps * caps);
91 static GstCaps *gst_rtp_opus_pay_getcaps (GstRTPBasePayload * payload,
92     GstPad * pad, GstCaps * filter);
93 static GstFlowReturn gst_rtp_opus_pay_handle_buffer (GstRTPBasePayload *
94     payload, GstBuffer * buffer);
95
96 G_DEFINE_TYPE (GstRtpOPUSPay, gst_rtp_opus_pay, GST_TYPE_RTP_BASE_PAYLOAD);
97 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtpopuspay, "rtpopuspay",
98     GST_RANK_PRIMARY, GST_TYPE_RTP_OPUS_PAY, rtp_element_init (plugin));
99
100 #define GST_RTP_OPUS_PAY_CAST(obj) ((GstRtpOPUSPay *)(obj))
101
102 static void
103 gst_rtp_opus_pay_set_property (GObject * object,
104     guint prop_id, const GValue * value, GParamSpec * pspec)
105 {
106   GstRtpOPUSPay *self = GST_RTP_OPUS_PAY (object);
107
108   switch (prop_id) {
109     case PROP_DTX:
110       self->dtx = g_value_get_boolean (value);
111       break;
112     default:
113       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
114       break;
115   }
116 }
117
118 static void
119 gst_rtp_opus_pay_get_property (GObject * object,
120     guint prop_id, GValue * value, GParamSpec * pspec)
121 {
122   GstRtpOPUSPay *self = GST_RTP_OPUS_PAY (object);
123
124   switch (prop_id) {
125     case PROP_DTX:
126       g_value_set_boolean (value, self->dtx);
127       break;
128     default:
129       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130       break;
131   }
132 }
133
134 static GstStateChangeReturn
135 gst_rtp_opus_pay_change_state (GstElement * element, GstStateChange transition)
136 {
137   GstRtpOPUSPay *self = GST_RTP_OPUS_PAY (element);
138   GstStateChangeReturn ret;
139
140   switch (transition) {
141     case GST_STATE_CHANGE_READY_TO_PAUSED:
142       self->marker = TRUE;
143       break;
144     default:
145       break;
146   }
147
148   ret =
149       GST_ELEMENT_CLASS (gst_rtp_opus_pay_parent_class)->change_state (element,
150       transition);
151
152   switch (transition) {
153     default:
154       break;
155   }
156
157   return ret;
158 }
159
160 static void
161 gst_rtp_opus_pay_class_init (GstRtpOPUSPayClass * klass)
162 {
163   GstRTPBasePayloadClass *gstbasertppayload_class;
164   GstElementClass *element_class;
165   GObjectClass *gobject_class;
166
167   gstbasertppayload_class = (GstRTPBasePayloadClass *) klass;
168   element_class = GST_ELEMENT_CLASS (klass);
169   gobject_class = (GObjectClass *) klass;
170
171   element_class->change_state = gst_rtp_opus_pay_change_state;
172
173   gstbasertppayload_class->set_caps = gst_rtp_opus_pay_setcaps;
174   gstbasertppayload_class->get_caps = gst_rtp_opus_pay_getcaps;
175   gstbasertppayload_class->handle_buffer = gst_rtp_opus_pay_handle_buffer;
176
177   gobject_class->set_property = gst_rtp_opus_pay_set_property;
178   gobject_class->get_property = gst_rtp_opus_pay_get_property;
179
180   gst_element_class_add_static_pad_template (element_class,
181       &gst_rtp_opus_pay_src_template);
182   gst_element_class_add_static_pad_template (element_class,
183       &gst_rtp_opus_pay_sink_template);
184
185   /**
186    * GstRtpOPUSPay:dtx:
187    *
188    * If enabled, the payloader will not transmit empty packets.
189    *
190    * Since: 1.20
191    */
192   g_object_class_install_property (gobject_class, PROP_DTX,
193       g_param_spec_boolean ("dtx", "Discontinuous Transmission",
194           "If enabled, the payloader will not transmit empty packets",
195           DEFAULT_DTX,
196           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
197           G_PARAM_STATIC_STRINGS));
198
199   gst_element_class_set_static_metadata (element_class,
200       "RTP Opus payloader",
201       "Codec/Payloader/Network/RTP",
202       "Puts Opus audio in RTP packets",
203       "Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk>");
204
205   GST_DEBUG_CATEGORY_INIT (rtpopuspay_debug, "rtpopuspay", 0,
206       "Opus RTP Payloader");
207 }
208
209 static void
210 gst_rtp_opus_pay_init (GstRtpOPUSPay * rtpopuspay)
211 {
212   rtpopuspay->dtx = DEFAULT_DTX;
213 }
214
215 static gboolean
216 gst_rtp_opus_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
217 {
218   gboolean res;
219   GstCaps *src_caps;
220   GstStructure *s, *outcaps;
221   const char *encoding_name = "OPUS";
222   gint channels = 2;
223   gint rate;
224   gchar *encoding_params;
225
226   outcaps = gst_structure_new_empty ("unused");
227
228   src_caps = gst_pad_get_allowed_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload));
229   if (src_caps) {
230     GstStructure *s;
231     const GValue *value;
232
233     s = gst_caps_get_structure (src_caps, 0);
234
235     if (gst_structure_has_field (s, "encoding-name")) {
236       GValue default_value = G_VALUE_INIT;
237
238       g_value_init (&default_value, G_TYPE_STRING);
239       g_value_set_static_string (&default_value, encoding_name);
240
241       value = gst_structure_get_value (s, "encoding-name");
242       if (!gst_value_can_intersect (&default_value, value))
243         encoding_name = "X-GST-OPUS-DRAFT-SPITTKA-00";
244     }
245     gst_caps_unref (src_caps);
246   }
247
248   s = gst_caps_get_structure (caps, 0);
249   if (gst_structure_get_int (s, "channels", &channels)) {
250     if (channels > 2) {
251       /* Implies channel-mapping-family = 1. */
252
253       gint stream_count, coupled_count;
254       const GValue *channel_mapping_array;
255
256       /* libwebrtc only supports "multiopus" when channels > 2. Mono and stereo
257        * sound must always be payloaded according to RFC 7587. */
258       encoding_name = "MULTIOPUS";
259
260       if (gst_structure_get_int (s, "stream-count", &stream_count)) {
261         char *num_streams = g_strdup_printf ("%d", stream_count);
262         gst_structure_set (outcaps, "num_streams", G_TYPE_STRING, num_streams,
263             NULL);
264         g_free (num_streams);
265       }
266       if (gst_structure_get_int (s, "coupled-count", &coupled_count)) {
267         char *coupled_streams = g_strdup_printf ("%d", coupled_count);
268         gst_structure_set (outcaps, "coupled_streams", G_TYPE_STRING,
269             coupled_streams, NULL);
270         g_free (coupled_streams);
271       }
272
273       channel_mapping_array = gst_structure_get_value (s, "channel-mapping");
274       if (GST_VALUE_HOLDS_ARRAY (channel_mapping_array)) {
275         GString *str = g_string_new (NULL);
276         guint i;
277
278         for (i = 0; i < gst_value_array_get_size (channel_mapping_array); ++i) {
279           if (i != 0) {
280             g_string_append_c (str, ',');
281           }
282           g_string_append_printf (str, "%d",
283               g_value_get_int (gst_value_array_get_value (channel_mapping_array,
284                       i)));
285         }
286
287         gst_structure_set (outcaps, "channel_mapping", G_TYPE_STRING, str->str,
288             NULL);
289
290         g_string_free (str, TRUE);
291       }
292     } else {
293       gst_structure_set (outcaps, "sprop-stereo", G_TYPE_STRING,
294           (channels == 2) ? "1" : "0", NULL);
295       /* RFC 7587 requires the number of channels always be 2. */
296       channels = 2;
297     }
298   }
299
300   encoding_params = g_strdup_printf ("%d", channels);
301   gst_structure_set (outcaps, "encoding-params", G_TYPE_STRING,
302       encoding_params, NULL);
303   g_free (encoding_params);
304
305   if (gst_structure_get_int (s, "rate", &rate)) {
306     gchar *sprop_maxcapturerate = g_strdup_printf ("%d", rate);
307
308     gst_structure_set (outcaps, "sprop-maxcapturerate", G_TYPE_STRING,
309         sprop_maxcapturerate, NULL);
310
311     g_free (sprop_maxcapturerate);
312   }
313
314   gst_rtp_base_payload_set_options (payload, "audio", FALSE,
315       encoding_name, 48000);
316
317   res = gst_rtp_base_payload_set_outcaps_structure (payload, outcaps);
318
319   gst_structure_free (outcaps);
320
321   return res;
322 }
323
324 static GstFlowReturn
325 gst_rtp_opus_pay_handle_buffer (GstRTPBasePayload * basepayload,
326     GstBuffer * buffer)
327 {
328   GstRtpOPUSPay *self = GST_RTP_OPUS_PAY_CAST (basepayload);
329   GstBuffer *outbuf;
330   GstClockTime pts, dts, duration;
331
332   /* DTX packets are zero-length frames, with a 1 or 2-bytes header */
333   if (self->dtx && gst_buffer_get_size (buffer) <= 2) {
334     GST_LOG_OBJECT (self,
335         "discard empty buffer as DTX is enabled: %" GST_PTR_FORMAT, buffer);
336     self->marker = TRUE;
337     gst_buffer_unref (buffer);
338     return GST_FLOW_OK;
339   }
340
341   pts = GST_BUFFER_PTS (buffer);
342   dts = GST_BUFFER_DTS (buffer);
343   duration = GST_BUFFER_DURATION (buffer);
344
345   outbuf = gst_rtp_base_payload_allocate_output_buffer (basepayload, 0, 0, 0);
346
347   gst_rtp_copy_audio_meta (basepayload, outbuf, buffer);
348
349   outbuf = gst_buffer_append (outbuf, buffer);
350
351   GST_BUFFER_PTS (outbuf) = pts;
352   GST_BUFFER_DTS (outbuf) = dts;
353   GST_BUFFER_DURATION (outbuf) = duration;
354
355   if (self->marker) {
356     GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
357
358     gst_rtp_buffer_map (outbuf, GST_MAP_READWRITE, &rtp);
359     gst_rtp_buffer_set_marker (&rtp, TRUE);
360     gst_rtp_buffer_unmap (&rtp);
361
362     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_MARKER);
363     self->marker = FALSE;
364   }
365
366   /* Push out */
367   return gst_rtp_base_payload_push (basepayload, outbuf);
368 }
369
370 static GstCaps *
371 gst_rtp_opus_pay_getcaps (GstRTPBasePayload * payload,
372     GstPad * pad, GstCaps * filter)
373 {
374   GstStructure *s;
375   const gchar *stereo;
376   GstCaps *caps, *peercaps, *tcaps, *tempcaps;
377
378   if (pad == GST_RTP_BASE_PAYLOAD_SRCPAD (payload))
379     return
380         GST_RTP_BASE_PAYLOAD_CLASS (gst_rtp_opus_pay_parent_class)->get_caps
381         (payload, pad, filter);
382
383   tcaps = gst_pad_get_pad_template_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload));
384   peercaps = gst_pad_peer_query_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload),
385       tcaps);
386   gst_caps_unref (tcaps);
387   if (!peercaps)
388     return
389         GST_RTP_BASE_PAYLOAD_CLASS (gst_rtp_opus_pay_parent_class)->get_caps
390         (payload, pad, filter);
391
392   if (gst_caps_is_empty (peercaps))
393     return peercaps;
394
395   caps = gst_pad_get_pad_template_caps (GST_RTP_BASE_PAYLOAD_SINKPAD (payload));
396
397   tempcaps = gst_caps_from_string ("application/x-rtp, "
398       "encoding-name=(string) { \"OPUS\", \"X-GST-OPUS-DRAFT-SPITTKA-00\"}");
399   if (!gst_caps_can_intersect (peercaps, tempcaps)) {
400     GstCaps *multiopuscaps = gst_caps_new_simple ("audio/x-opus",
401         "channel-mapping-family", G_TYPE_INT, 1,
402         "channels", GST_TYPE_INT_RANGE, 3, 255,
403         NULL);
404     GstCaps *intersect_caps;
405
406     intersect_caps = gst_caps_intersect_full (caps, multiopuscaps,
407         GST_CAPS_INTERSECT_FIRST);
408     gst_caps_unref (caps);
409     gst_caps_unref (multiopuscaps);
410     caps = intersect_caps;
411   }
412   gst_caps_unref (tempcaps);
413
414   tempcaps = gst_caps_new_simple ("application/x-rtp",
415       "encoding-name", G_TYPE_STRING, "MULTIOPUS", NULL);
416   if (!gst_caps_can_intersect (peercaps, tempcaps)) {
417     GstCaps *opuscaps = gst_caps_new_simple ("audio/x-opus",
418         "channel-mapping-family", G_TYPE_INT, 0,
419         "channels", GST_TYPE_INT_RANGE, 1, 2,
420         NULL);
421     GstCaps *intersect_caps;
422
423     intersect_caps = gst_caps_intersect_full (caps, opuscaps,
424         GST_CAPS_INTERSECT_FIRST);
425     gst_caps_unref (caps);
426     gst_caps_unref (opuscaps);
427     caps = intersect_caps;
428   }
429   gst_caps_unref (tempcaps);
430
431   s = gst_caps_get_structure (peercaps, 0);
432   stereo = gst_structure_get_string (s, "stereo");
433   if (stereo != NULL) {
434     caps = gst_caps_make_writable (caps);
435
436     if (!strcmp (stereo, "1")) {
437       GstCaps *caps2 = gst_caps_copy (caps);
438
439       gst_caps_set_simple (caps, "channels", G_TYPE_INT, 2, NULL);
440       gst_caps_set_simple (caps2, "channels", G_TYPE_INT, 1, NULL);
441       caps = gst_caps_merge (caps, caps2);
442     } else if (!strcmp (stereo, "0")) {
443       GstCaps *caps2 = gst_caps_copy (caps);
444
445       gst_caps_set_simple (caps, "channels", G_TYPE_INT, 1, NULL);
446       gst_caps_set_simple (caps2, "channels", G_TYPE_INT, 2, NULL);
447       caps = gst_caps_merge (caps, caps2);
448     }
449   }
450   gst_caps_unref (peercaps);
451
452   if (filter) {
453     GstCaps *tmp = gst_caps_intersect_full (caps, filter,
454         GST_CAPS_INTERSECT_FIRST);
455     gst_caps_unref (caps);
456     caps = tmp;
457   }
458
459   GST_DEBUG_OBJECT (payload, "Returning caps: %" GST_PTR_FORMAT, caps);
460   return caps;
461 }