1 /* GStreamer
2 * Copyright (C) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>.
3 * Copyright (C) 2011 Nokia Corporation. All rights reserved.
4 * Contact: Stefan Kost <stefan.kost@nokia.com>
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 <gst/audio/audio.h>
27 #ifdef G_OS_WIN32
28 #include <windows.h>
29 #endif
30
31 #include "gstaudioutilsprivate.h"
32
33 /*
34 * Takes caps and copies its audio fields to tmpl_caps
35 */
36 static GstCaps *
__gst_audio_element_proxy_caps(GstElement * element,GstCaps * templ_caps,GstCaps * caps)37 __gst_audio_element_proxy_caps (GstElement * element, GstCaps * templ_caps,
38 GstCaps * caps)
39 {
40 GstCaps *result = gst_caps_new_empty ();
41 gint i, j;
42 gint templ_caps_size = gst_caps_get_size (templ_caps);
43 gint caps_size = gst_caps_get_size (caps);
44
45 for (i = 0; i < templ_caps_size; i++) {
46 GQuark q_name =
47 gst_structure_get_name_id (gst_caps_get_structure (templ_caps, i));
48 GstCapsFeatures *features = gst_caps_get_features (templ_caps, i);
49
50 for (j = 0; j < caps_size; j++) {
51 const GstStructure *caps_s = gst_caps_get_structure (caps, j);
52 const GValue *val;
53 GstStructure *s;
54 GstCaps *tmp = gst_caps_new_empty ();
55
56 s = gst_structure_new_id_empty (q_name);
57 if ((val = gst_structure_get_value (caps_s, "rate")))
58 gst_structure_set_value (s, "rate", val);
59 if ((val = gst_structure_get_value (caps_s, "channels")))
60 gst_structure_set_value (s, "channels", val);
61 if ((val = gst_structure_get_value (caps_s, "channels-mask")))
62 gst_structure_set_value (s, "channels-mask", val);
63
64 gst_caps_append_structure_full (tmp, s,
65 gst_caps_features_copy (features));
66 result = gst_caps_merge (result, tmp);
67 }
68 }
69
70 return result;
71 }
72
73 /**
74 * __gst_audio_element_proxy_getcaps:
75 * @element: a #GstElement
76 * @sinkpad: the element's sink #GstPad
77 * @srcpad: the element's source #GstPad
78 * @initial_caps: initial caps
79 * @filter: filter caps
80 *
81 * Returns caps that express @initial_caps (or sink template caps if
82 * @initial_caps == NULL) restricted to rate/channels/...
83 * combinations supported by downstream elements (e.g. muxers).
84 *
85 * Returns: a #GstCaps owned by caller
86 */
87 GstCaps *
__gst_audio_element_proxy_getcaps(GstElement * element,GstPad * sinkpad,GstPad * srcpad,GstCaps * initial_caps,GstCaps * filter)88 __gst_audio_element_proxy_getcaps (GstElement * element, GstPad * sinkpad,
89 GstPad * srcpad, GstCaps * initial_caps, GstCaps * filter)
90 {
91 GstCaps *templ_caps, *src_templ_caps;
92 GstCaps *peer_caps;
93 GstCaps *allowed;
94 GstCaps *fcaps, *filter_caps;
95
96 /* Allow downstream to specify rate/channels constraints
97 * and forward them upstream for audio converters to handle
98 */
99 templ_caps = initial_caps ? gst_caps_ref (initial_caps) :
100 gst_pad_get_pad_template_caps (sinkpad);
101 src_templ_caps = gst_pad_get_pad_template_caps (srcpad);
102 if (filter && !gst_caps_is_any (filter)) {
103 GstCaps *proxy_filter =
104 __gst_audio_element_proxy_caps (element, src_templ_caps, filter);
105
106 peer_caps = gst_pad_peer_query_caps (srcpad, proxy_filter);
107 gst_caps_unref (proxy_filter);
108 } else {
109 peer_caps = gst_pad_peer_query_caps (srcpad, NULL);
110 }
111
112 allowed = gst_caps_intersect_full (peer_caps, src_templ_caps,
113 GST_CAPS_INTERSECT_FIRST);
114
115 gst_caps_unref (src_templ_caps);
116 gst_caps_unref (peer_caps);
117
118 if (!allowed || gst_caps_is_any (allowed)) {
119 fcaps = templ_caps;
120 goto done;
121 } else if (gst_caps_is_empty (allowed)) {
122 fcaps = gst_caps_ref (allowed);
123 goto done;
124 }
125
126 GST_LOG_OBJECT (element, "template caps %" GST_PTR_FORMAT, templ_caps);
127 GST_LOG_OBJECT (element, "allowed caps %" GST_PTR_FORMAT, allowed);
128
129 filter_caps = __gst_audio_element_proxy_caps (element, templ_caps, allowed);
130
131 fcaps = gst_caps_intersect (filter_caps, templ_caps);
132 gst_caps_unref (filter_caps);
133 gst_caps_unref (templ_caps);
134
135 if (filter) {
136 GST_LOG_OBJECT (element, "intersecting with %" GST_PTR_FORMAT, filter);
137 filter_caps = gst_caps_intersect (fcaps, filter);
138 gst_caps_unref (fcaps);
139 fcaps = filter_caps;
140 }
141
142 done:
143 gst_caps_replace (&allowed, NULL);
144
145 GST_LOG_OBJECT (element, "proxy caps %" GST_PTR_FORMAT, fcaps);
146
147 return fcaps;
148 }
149
150 /**
151 * __gst_audio_encoded_audio_convert:
152 * @fmt: audio format of the encoded audio
153 * @bytes: number of encoded bytes
154 * @samples: number of encoded samples
155 * @src_format: source format
156 * @src_value: source value
157 * @dest_format: destination format
158 * @dest_value: destination format
159 *
160 * Helper function to convert @src_value in @src_format to @dest_value in
161 * @dest_format for encoded audio data. Conversion is possible between
162 * BYTE and TIME format by using estimated bitrate based on
163 * @samples and @bytes (and @fmt).
164 */
165 gboolean
__gst_audio_encoded_audio_convert(GstAudioInfo * fmt,gint64 bytes,gint64 samples,GstFormat src_format,gint64 src_value,GstFormat * dest_format,gint64 * dest_value)166 __gst_audio_encoded_audio_convert (GstAudioInfo * fmt,
167 gint64 bytes, gint64 samples, GstFormat src_format,
168 gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
169 {
170 gboolean res = FALSE;
171
172 g_return_val_if_fail (dest_format != NULL, FALSE);
173 g_return_val_if_fail (dest_value != NULL, FALSE);
174
175 if (G_UNLIKELY (src_format == *dest_format || src_value == 0 ||
176 src_value == -1)) {
177 if (dest_value)
178 *dest_value = src_value;
179 return TRUE;
180 }
181
182 if (samples == 0 || bytes == 0 || fmt->rate == 0) {
183 GST_DEBUG ("not enough metadata yet to convert");
184 goto exit;
185 }
186
187 bytes *= fmt->rate;
188
189 switch (src_format) {
190 case GST_FORMAT_BYTES:
191 switch (*dest_format) {
192 case GST_FORMAT_TIME:
193 *dest_value = gst_util_uint64_scale (src_value,
194 GST_SECOND * samples, bytes);
195 res = TRUE;
196 break;
197 default:
198 res = FALSE;
199 }
200 break;
201 case GST_FORMAT_TIME:
202 switch (*dest_format) {
203 case GST_FORMAT_BYTES:
204 *dest_value = gst_util_uint64_scale (src_value, bytes,
205 samples * GST_SECOND);
206 res = TRUE;
207 break;
208 default:
209 res = FALSE;
210 }
211 break;
212 default:
213 res = FALSE;
214 }
215
216 exit:
217 return res;
218 }
219
220 #ifdef G_OS_WIN32
221 /* *INDENT-OFF* */
222 static struct
223 {
224 HMODULE dll;
225
226 FARPROC AvSetMmThreadCharacteristics;
227 FARPROC AvRevertMmThreadCharacteristics;
228 } _gst_audio_avrt_tbl = { 0 };
229 /* *INDENT-ON* */
230 #endif
231
232 static gboolean
__gst_audio_init_thread_priority(void)233 __gst_audio_init_thread_priority (void)
234 {
235 #ifdef G_OS_WIN32
236 static gsize init_once = 0;
237 static gboolean ret = FALSE;
238
239 if (g_once_init_enter (&init_once)) {
240 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
241 _gst_audio_avrt_tbl.dll = LoadLibrary (TEXT ("avrt.dll"));
242
243 if (!_gst_audio_avrt_tbl.dll) {
244 GST_WARNING ("Failed to set thread priority, can't find avrt.dll");
245 goto done;
246 }
247
248 _gst_audio_avrt_tbl.AvSetMmThreadCharacteristics =
249 GetProcAddress (_gst_audio_avrt_tbl.dll,
250 "AvSetMmThreadCharacteristicsA");
251 if (!_gst_audio_avrt_tbl.AvSetMmThreadCharacteristics) {
252 GST_WARNING ("Cannot load AvSetMmThreadCharacteristicsA symbol");
253 FreeLibrary (_gst_audio_avrt_tbl.dll);
254 goto done;
255 }
256
257 _gst_audio_avrt_tbl.AvRevertMmThreadCharacteristics =
258 GetProcAddress (_gst_audio_avrt_tbl.dll,
259 "AvRevertMmThreadCharacteristics");
260
261 if (!_gst_audio_avrt_tbl.AvRevertMmThreadCharacteristics) {
262 GST_WARNING ("Cannot load AvRevertMmThreadCharacteristics symbol");
263 FreeLibrary (_gst_audio_avrt_tbl.dll);
264 goto done;
265 }
266
267 ret = TRUE;
268
269 done:
270 #endif
271 g_once_init_leave (&init_once, 1);
272 }
273
274 return ret;
275 #endif
276
277 return TRUE;
278 }
279
280 /*
281 * Increases the priority of the thread it's called from
282 */
283 gboolean
__gst_audio_set_thread_priority(gpointer * handle)284 __gst_audio_set_thread_priority (gpointer * handle)
285 {
286 #ifdef G_OS_WIN32
287 DWORD taskIndex = 0;
288 #endif
289
290 g_return_val_if_fail (handle != NULL, FALSE);
291
292 *handle = NULL;
293
294 if (!__gst_audio_init_thread_priority ())
295 return FALSE;
296
297 #ifdef G_OS_WIN32
298 /* This is only used from ringbuffer thread functions */
299 *handle = (gpointer)
300 _gst_audio_avrt_tbl.AvSetMmThreadCharacteristics (TEXT ("Pro Audio"),
301 &taskIndex);
302 if (*handle == 0) {
303 gchar *errorMsg = g_win32_error_message (GetLastError ());
304
305 GST_WARNING
306 ("Failed to set thread priority, AvSetMmThreadCharacteristics returned: %s",
307 errorMsg);
308 g_free (errorMsg);
309 }
310
311 return *handle != 0;
312 #else
313 return TRUE;
314 #endif
315 }
316
317 /*
318 * Restores the priority of the thread that was increased
319 * with __gst_audio_set_thread_priority.
320 * This function must be called from the same thread that called the
321 * __gst_audio_set_thread_priority function.
322 * See https://docs.microsoft.com/en-us/windows/win32/api/avrt/nf-avrt-avsetmmthreadcharacteristicsw#remarks
323 */
324 gboolean
__gst_audio_restore_thread_priority(gpointer handle)325 __gst_audio_restore_thread_priority (gpointer handle)
326 {
327 #ifdef G_OS_WIN32
328 if (!handle)
329 return FALSE;
330
331 return _gst_audio_avrt_tbl.AvRevertMmThreadCharacteristics ((HANDLE) handle);
332 #else
333 return TRUE;
334 #endif
335 }
336