• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Opus Depayloader 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 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25 
26 #include <string.h>
27 #include <stdlib.h>
28 #include <gst/rtp/gstrtpbuffer.h>
29 #include <gst/audio/audio.h>
30 #include "gstrtpelements.h"
31 #include "gstrtpopusdepay.h"
32 #include "gstrtputils.h"
33 
34 GST_DEBUG_CATEGORY_STATIC (rtpopusdepay_debug);
35 #define GST_CAT_DEFAULT (rtpopusdepay_debug)
36 
37 static GstStaticPadTemplate gst_rtp_opus_depay_sink_template =
38 GST_STATIC_PAD_TEMPLATE ("sink",
39     GST_PAD_SINK,
40     GST_PAD_ALWAYS,
41     GST_STATIC_CAPS ("application/x-rtp, "
42         "media = (string) \"audio\", "
43         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ","
44         "clock-rate = (int) 48000, "
45         "encoding-name = (string) { \"OPUS\", \"X-GST-OPUS-DRAFT-SPITTKA-00\", \"multiopus\" }")
46     );
47 
48 static GstStaticPadTemplate gst_rtp_opus_depay_src_template =
49 GST_STATIC_PAD_TEMPLATE ("src",
50     GST_PAD_SRC,
51     GST_PAD_ALWAYS,
52     GST_STATIC_CAPS ("audio/x-opus, channel-mapping-family = (int) [ 0, 1 ]")
53     );
54 
55 static GstBuffer *gst_rtp_opus_depay_process (GstRTPBaseDepayload * depayload,
56     GstRTPBuffer * rtp_buffer);
57 static gboolean gst_rtp_opus_depay_setcaps (GstRTPBaseDepayload * depayload,
58     GstCaps * caps);
59 
60 G_DEFINE_TYPE (GstRTPOpusDepay, gst_rtp_opus_depay,
61     GST_TYPE_RTP_BASE_DEPAYLOAD);
62 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtpopusdepay, "rtpopusdepay",
63     GST_RANK_PRIMARY, GST_TYPE_RTP_OPUS_DEPAY, rtp_element_init (plugin));
64 
65 static void
gst_rtp_opus_depay_class_init(GstRTPOpusDepayClass * klass)66 gst_rtp_opus_depay_class_init (GstRTPOpusDepayClass * klass)
67 {
68   GstRTPBaseDepayloadClass *gstbasertpdepayload_class;
69   GstElementClass *element_class;
70 
71   element_class = GST_ELEMENT_CLASS (klass);
72   gstbasertpdepayload_class = (GstRTPBaseDepayloadClass *) klass;
73 
74   gst_element_class_add_static_pad_template (element_class,
75       &gst_rtp_opus_depay_src_template);
76   gst_element_class_add_static_pad_template (element_class,
77       &gst_rtp_opus_depay_sink_template);
78   gst_element_class_set_static_metadata (element_class,
79       "RTP Opus packet depayloader", "Codec/Depayloader/Network/RTP",
80       "Extracts Opus audio from RTP packets",
81       "Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk>");
82 
83   gstbasertpdepayload_class->process_rtp_packet = gst_rtp_opus_depay_process;
84   gstbasertpdepayload_class->set_caps = gst_rtp_opus_depay_setcaps;
85 
86   GST_DEBUG_CATEGORY_INIT (rtpopusdepay_debug, "rtpopusdepay", 0,
87       "Opus RTP Depayloader");
88 }
89 
90 static void
gst_rtp_opus_depay_init(GstRTPOpusDepay * rtpopusdepay)91 gst_rtp_opus_depay_init (GstRTPOpusDepay * rtpopusdepay)
92 {
93 
94 }
95 
96 static gboolean
gst_rtp_opus_depay_setcaps(GstRTPBaseDepayload * depayload,GstCaps * caps)97 gst_rtp_opus_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
98 {
99   GstCaps *srccaps;
100   GstStructure *s;
101   gboolean ret;
102   const gchar *sprop_maxcapturerate;
103 
104   srccaps = gst_caps_new_empty_simple ("audio/x-opus");
105 
106   s = gst_caps_get_structure (caps, 0);
107 
108   if (g_str_equal (gst_structure_get_string (s, "encoding-name"), "multiopus")) {
109     gint channels;
110     gint stream_count;
111     gint coupled_count;
112     const gchar *encoding_params;
113     const gchar *num_streams;
114     const gchar *coupled_streams;
115     const gchar *channel_mapping;
116     gchar *endptr;
117 
118     if (!gst_structure_has_field_typed (s, "encoding-params", G_TYPE_STRING) ||
119         !gst_structure_has_field_typed (s, "num_streams", G_TYPE_STRING) ||
120         !gst_structure_has_field_typed (s, "coupled_streams", G_TYPE_STRING) ||
121         !gst_structure_has_field_typed (s, "channel_mapping", G_TYPE_STRING)) {
122       GST_WARNING_OBJECT (depayload, "Encoding name 'multiopus' requires "
123           "encoding-params, num_streams, coupled_streams and channel_mapping "
124           "as string fields in caps.");
125       goto reject_caps;
126     }
127 
128     gst_caps_set_simple (srccaps, "channel-mapping-family", G_TYPE_INT, 1,
129         NULL);
130 
131     encoding_params = gst_structure_get_string (s, "encoding-params");
132     channels = g_ascii_strtoull (encoding_params, &endptr, 10);
133     if (*endptr != '\0' || channels > 255) {
134       GST_WARNING_OBJECT (depayload, "Invalid encoding-params value '%s'",
135           encoding_params);
136       goto reject_caps;
137     }
138     gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, channels, NULL);
139 
140     num_streams = gst_structure_get_string (s, "num_streams");
141     stream_count = g_ascii_strtoull (num_streams, &endptr, 10);
142     if (*endptr != '\0' || stream_count > channels) {
143       GST_WARNING_OBJECT (depayload, "Invalid num_streams value '%s'",
144           num_streams);
145       goto reject_caps;
146     }
147     gst_caps_set_simple (srccaps, "stream-count", G_TYPE_INT, stream_count,
148         NULL);
149 
150     coupled_streams = gst_structure_get_string (s, "coupled_streams");
151     coupled_count = g_ascii_strtoull (coupled_streams, &endptr, 10);
152     if (*endptr != '\0' || coupled_count > stream_count) {
153       GST_WARNING_OBJECT (depayload, "Invalid coupled_streams value '%s'",
154           coupled_streams);
155       goto reject_caps;
156     }
157     gst_caps_set_simple (srccaps, "coupled-count", G_TYPE_INT, coupled_count,
158         NULL);
159 
160     channel_mapping = gst_structure_get_string (s, "channel_mapping");
161     {
162       gchar **split;
163       gchar **ptr;
164       GValue mapping = G_VALUE_INIT;
165       GValue v = G_VALUE_INIT;
166 
167       split = g_strsplit (channel_mapping, ",", -1);
168 
169       g_value_init (&mapping, GST_TYPE_ARRAY);
170       g_value_init (&v, G_TYPE_INT);
171 
172       for (ptr = split; *ptr; ++ptr) {
173         gint channel = g_ascii_strtoull (*ptr, &endptr, 10);
174         if (*endptr != '\0' || channel > channels) {
175           GST_WARNING_OBJECT (depayload, "Invalid channel_mapping value '%s'",
176               channel_mapping);
177           g_value_unset (&mapping);
178           break;
179         }
180         g_value_set_int (&v, channel);
181         gst_value_array_append_value (&mapping, &v);
182       }
183 
184       g_value_unset (&v);
185       g_strfreev (split);
186 
187       if (G_IS_VALUE (&mapping)) {
188         gst_caps_set_value (srccaps, "channel-mapping", &mapping);
189         g_value_unset (&mapping);
190       } else {
191         goto reject_caps;
192       }
193     }
194   } else {
195     const gchar *sprop_stereo;
196 
197     gst_caps_set_simple (srccaps, "channel-mapping-family", G_TYPE_INT, 0,
198         NULL);
199 
200     if ((sprop_stereo = gst_structure_get_string (s, "sprop-stereo"))) {
201       if (strcmp (sprop_stereo, "0") == 0)
202         gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, 1, NULL);
203       else if (strcmp (sprop_stereo, "1") == 0)
204         gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, 2, NULL);
205       else
206         GST_WARNING_OBJECT (depayload, "Unknown sprop-stereo value '%s'",
207             sprop_stereo);
208     } else {
209       /* Although sprop-stereo defaults to mono as per RFC 7587, this just means
210          that the signal is likely mono and can be safely downmixed, it may
211          still be stereo at times. */
212       gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, 2, NULL);
213     }
214   }
215 
216   if ((sprop_maxcapturerate =
217           gst_structure_get_string (s, "sprop-maxcapturerate"))) {
218     gulong rate;
219     gchar *tailptr;
220 
221     rate = strtoul (sprop_maxcapturerate, &tailptr, 10);
222     if (rate > INT_MAX || *tailptr != '\0') {
223       GST_WARNING_OBJECT (depayload,
224           "Failed to parse sprop-maxcapturerate value '%s'",
225           sprop_maxcapturerate);
226     } else {
227       gst_caps_set_simple (srccaps, "rate", G_TYPE_INT, rate, NULL);
228     }
229   }
230 
231   ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps);
232 
233   GST_DEBUG_OBJECT (depayload,
234       "set caps on source: %" GST_PTR_FORMAT " (ret=%d)", srccaps, ret);
235   gst_caps_unref (srccaps);
236 
237   depayload->clock_rate = 48000;
238 
239   return ret;
240 
241 reject_caps:
242   gst_caps_unref (srccaps);
243 
244   return FALSE;
245 }
246 
247 static GstBuffer *
gst_rtp_opus_depay_process(GstRTPBaseDepayload * depayload,GstRTPBuffer * rtp_buffer)248 gst_rtp_opus_depay_process (GstRTPBaseDepayload * depayload,
249     GstRTPBuffer * rtp_buffer)
250 {
251   GstBuffer *outbuf;
252 
253   outbuf = gst_rtp_buffer_get_payload_buffer (rtp_buffer);
254 
255   gst_rtp_drop_non_audio_meta (depayload, outbuf);
256 
257   return outbuf;
258 }
259