• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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