• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2018> Havard Graff <havard.graff@gmail.com>
3  * Copyright (C) <2020-2021> Guillaume Desmottes <guillaume.desmottes@collabora.com>
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
14  */
15 
16 /**
17  * SECTION:element-rtphdrextclientaudiolevel
18  * @title: rtphdrextclientaudiolevel
19  * @short_description: Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension
20  *
21  * Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension.
22  * The extension should be automatically created by payloader and depayloaders,
23  * if their `auto-header-extension` property is enabled, if the extension
24  * is part of the RTP caps.
25  *
26  * ## Example pipeline
27  * |[
28  * gst-launch-1.0 pulsesrc ! level audio-level-meta=true ! audiconvert !
29  *   rtpL16pay ! application/x-rtp,
30  *     extmap-1=(string)\< \"\", urn:ietf:params:rtp-hdrext:ssrc-audio-level,
31  *     \"vad=on\" \> ! udpsink
32  * ]|
33  *
34  * Since: 1.20
35  *
36  */
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40 
41 #include "gstrtphdrext-clientaudiolevel.h"
42 
43 #include <gst/audio/audio.h>
44 
45 #define CLIENT_AUDIO_LEVEL_HDR_EXT_URI GST_RTP_HDREXT_BASE"ssrc-audio-level"
46 
47 GST_DEBUG_CATEGORY_STATIC (rtphdrclient_audio_level_debug);
48 #define GST_CAT_DEFAULT (rtphdrclient_audio_level_debug)
49 
50 #define DEFAULT_VAD TRUE
51 
52 enum
53 {
54   PROP_0,
55   PROP_VAD,
56 };
57 
58 struct _GstRTPHeaderExtensionClientAudioLevel
59 {
60   GstRTPHeaderExtension parent;
61 
62   gboolean vad;
63 };
64 
65 G_DEFINE_TYPE_WITH_CODE (GstRTPHeaderExtensionClientAudioLevel,
66     gst_rtp_header_extension_client_audio_level, GST_TYPE_RTP_HEADER_EXTENSION,
67     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtphdrextclientaudiolevel", 0,
68         "RTP RFC 6464 Header Extensions"););
69 GST_ELEMENT_REGISTER_DEFINE (rtphdrextclientaudiolevel,
70     "rtphdrextclientaudiolevel", GST_RANK_MARGINAL,
71     GST_TYPE_RTP_HEADER_EXTENSION_CLIENT_AUDIO_LEVEL);
72 
73 static void
gst_rtp_header_extension_client_audio_level_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)74 gst_rtp_header_extension_client_audio_level_get_property (GObject * object,
75     guint prop_id, GValue * value, GParamSpec * pspec)
76 {
77   GstRTPHeaderExtensionClientAudioLevel *self =
78       GST_RTP_HEADER_EXTENSION_CLIENT_AUDIO_LEVEL (object);
79 
80   switch (prop_id) {
81     case PROP_VAD:
82       g_value_set_boolean (value, self->vad);
83       break;
84     default:
85       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
86       break;
87   }
88 }
89 
90 static GstRTPHeaderExtensionFlags
gst_rtp_header_extension_client_audio_level_get_supported_flags(GstRTPHeaderExtension * ext)91     gst_rtp_header_extension_client_audio_level_get_supported_flags
92     (GstRTPHeaderExtension * ext)
93 {
94   return GST_RTP_HEADER_EXTENSION_ONE_BYTE | GST_RTP_HEADER_EXTENSION_TWO_BYTE;
95 }
96 
97 static gsize
gst_rtp_header_extension_client_audio_level_get_max_size(GstRTPHeaderExtension * ext,const GstBuffer * input_meta)98 gst_rtp_header_extension_client_audio_level_get_max_size (GstRTPHeaderExtension
99     * ext, const GstBuffer * input_meta)
100 {
101   return 2;
102 }
103 
104 static void
set_vad(GstRTPHeaderExtension * ext,gboolean vad)105 set_vad (GstRTPHeaderExtension * ext, gboolean vad)
106 {
107   GstRTPHeaderExtensionClientAudioLevel *self =
108       GST_RTP_HEADER_EXTENSION_CLIENT_AUDIO_LEVEL (ext);
109 
110   if (self->vad == vad)
111     return;
112 
113   GST_DEBUG_OBJECT (ext, "vad: %d", vad);
114   self->vad = vad;
115   g_object_notify (G_OBJECT (self), "vad");
116 }
117 
118 static gboolean
gst_rtp_header_extension_client_audio_level_set_attributes(GstRTPHeaderExtension * ext,GstRTPHeaderExtensionDirection direction,const gchar * attributes)119     gst_rtp_header_extension_client_audio_level_set_attributes
120     (GstRTPHeaderExtension * ext, GstRTPHeaderExtensionDirection direction,
121     const gchar * attributes)
122 {
123   if (g_str_equal (attributes, "vad=on") || g_str_equal (attributes, "")) {
124     set_vad (ext, TRUE);
125   } else if (g_str_equal (attributes, "vad=off")) {
126     set_vad (ext, FALSE);
127   } else {
128     GST_WARNING_OBJECT (ext, "Invalid attribute: %s", attributes);
129     return FALSE;
130   }
131 
132   return TRUE;
133 }
134 
135 static gboolean
gst_rtp_header_extension_client_audio_level_set_caps_from_attributes(GstRTPHeaderExtension * ext,GstCaps * caps)136     gst_rtp_header_extension_client_audio_level_set_caps_from_attributes
137     (GstRTPHeaderExtension * ext, GstCaps * caps)
138 {
139   GstRTPHeaderExtensionClientAudioLevel *self =
140       GST_RTP_HEADER_EXTENSION_CLIENT_AUDIO_LEVEL (ext);
141   const gchar *vad;
142 
143   if (self->vad)
144     vad = "vad=on";
145   else
146     vad = "vad=off";
147 
148   return gst_rtp_header_extension_set_caps_from_attributes_helper (ext, caps,
149       vad);
150 }
151 
152 static gssize
gst_rtp_header_extension_client_audio_level_write(GstRTPHeaderExtension * ext,const GstBuffer * input_meta,GstRTPHeaderExtensionFlags write_flags,GstBuffer * output,guint8 * data,gsize size)153 gst_rtp_header_extension_client_audio_level_write (GstRTPHeaderExtension * ext,
154     const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags,
155     GstBuffer * output, guint8 * data, gsize size)
156 {
157   GstAudioLevelMeta *meta;
158   guint level;
159 
160   g_return_val_if_fail (size >=
161       gst_rtp_header_extension_client_audio_level_get_max_size (ext, NULL), -1);
162   g_return_val_if_fail (write_flags &
163       gst_rtp_header_extension_client_audio_level_get_supported_flags (ext),
164       -1);
165 
166   meta = gst_buffer_get_audio_level_meta ((GstBuffer *) input_meta);
167   if (!meta) {
168     GST_LOG_OBJECT (ext, "no meta");
169     return 0;
170   }
171 
172   level = meta->level;
173   if (level > 127) {
174     GST_LOG_OBJECT (ext, "level from meta is higher than 127: %d, cropping",
175         meta->level);
176     level = 127;
177   }
178 
179   GST_LOG_OBJECT (ext, "writing ext (level: %d voice: %d)", meta->level,
180       meta->voice_activity);
181 
182   /* Both one & two byte use the same format, the second byte being padding */
183   data[0] = (meta->level & 0x7F) | (meta->voice_activity << 7);
184   if (write_flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE) {
185     return 1;
186   }
187   data[1] = 0;
188   return 2;
189 }
190 
191 static gboolean
gst_rtp_header_extension_client_audio_level_read(GstRTPHeaderExtension * ext,GstRTPHeaderExtensionFlags read_flags,const guint8 * data,gsize size,GstBuffer * buffer)192 gst_rtp_header_extension_client_audio_level_read (GstRTPHeaderExtension * ext,
193     GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size,
194     GstBuffer * buffer)
195 {
196   guint8 level;
197   gboolean voice_activity;
198 
199   g_return_val_if_fail (read_flags &
200       gst_rtp_header_extension_client_audio_level_get_supported_flags (ext),
201       -1);
202 
203   /* Both one & two byte use the same format, the second byte being padding */
204   level = data[0] & 0x7F;
205   voice_activity = (data[0] & 0x80) >> 7;
206 
207   GST_LOG_OBJECT (ext, "reading ext (level: %d voice: %d)", level,
208       voice_activity);
209 
210   gst_buffer_add_audio_level_meta (buffer, level, voice_activity);
211 
212   return TRUE;
213 }
214 
215 static void
gst_rtp_header_extension_client_audio_level_class_init(GstRTPHeaderExtensionClientAudioLevelClass * klass)216     gst_rtp_header_extension_client_audio_level_class_init
217     (GstRTPHeaderExtensionClientAudioLevelClass * klass)
218 {
219   GstRTPHeaderExtensionClass *rtp_hdr_class;
220   GstElementClass *gstelement_class;
221   GObjectClass *gobject_class;
222 
223   rtp_hdr_class = GST_RTP_HEADER_EXTENSION_CLASS (klass);
224   gobject_class = (GObjectClass *) klass;
225   gstelement_class = GST_ELEMENT_CLASS (klass);
226 
227   gobject_class->get_property =
228       gst_rtp_header_extension_client_audio_level_get_property;
229 
230   /**
231    * rtphdrextclientaudiolevel:vad:
232    *
233    * If the vad extension attribute is enabled or not, default to %FALSE.
234    *
235    * Since: 1.20
236    */
237   g_object_class_install_property (gobject_class, PROP_VAD,
238       g_param_spec_boolean ("vad", "vad",
239           "If the vad extension attribute is enabled or not",
240           DEFAULT_VAD, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
241 
242   rtp_hdr_class->get_supported_flags =
243       gst_rtp_header_extension_client_audio_level_get_supported_flags;
244   rtp_hdr_class->get_max_size =
245       gst_rtp_header_extension_client_audio_level_get_max_size;
246   rtp_hdr_class->set_attributes =
247       gst_rtp_header_extension_client_audio_level_set_attributes;
248   rtp_hdr_class->set_caps_from_attributes =
249       gst_rtp_header_extension_client_audio_level_set_caps_from_attributes;
250   rtp_hdr_class->write = gst_rtp_header_extension_client_audio_level_write;
251   rtp_hdr_class->read = gst_rtp_header_extension_client_audio_level_read;
252 
253   gst_element_class_set_static_metadata (gstelement_class,
254       "Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension",
255       GST_RTP_HDREXT_ELEMENT_CLASS,
256       "Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension",
257       "Guillaume Desmottes <guillaume.desmottes@collabora.com>");
258   gst_rtp_header_extension_class_set_uri (rtp_hdr_class,
259       CLIENT_AUDIO_LEVEL_HDR_EXT_URI);
260 }
261 
262 static void
gst_rtp_header_extension_client_audio_level_init(GstRTPHeaderExtensionClientAudioLevel * self)263     gst_rtp_header_extension_client_audio_level_init
264     (GstRTPHeaderExtensionClientAudioLevel * self)
265 {
266   GST_DEBUG_OBJECT (self, "creating element");
267   self->vad = DEFAULT_VAD;
268 }
269