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   gboolean tried_loading;
226 
227   FARPROC AvSetMmThreadCharacteristics;
228   FARPROC AvRevertMmThreadCharacteristics;
229 } _gst_audio_avrt_tbl = { 0 };
230 /* *INDENT-ON* */
231 #endif
232 
233 static gboolean
__gst_audio_init_thread_priority(void)234 __gst_audio_init_thread_priority (void)
235 {
236 #ifdef G_OS_WIN32
237   if (_gst_audio_avrt_tbl.tried_loading)
238     return _gst_audio_avrt_tbl.dll != NULL;
239 
240   if (!_gst_audio_avrt_tbl.dll)
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     _gst_audio_avrt_tbl.tried_loading = TRUE;
246     return FALSE;
247   }
248 
249   _gst_audio_avrt_tbl.AvSetMmThreadCharacteristics =
250       GetProcAddress (_gst_audio_avrt_tbl.dll, "AvSetMmThreadCharacteristicsA");
251   _gst_audio_avrt_tbl.AvRevertMmThreadCharacteristics =
252       GetProcAddress (_gst_audio_avrt_tbl.dll,
253       "AvRevertMmThreadCharacteristics");
254 
255   _gst_audio_avrt_tbl.tried_loading = TRUE;
256 #endif
257 
258   return TRUE;
259 }
260 
261 /*
262  * Increases the priority of the thread it's called from
263  */
264 gboolean
__gst_audio_set_thread_priority(void)265 __gst_audio_set_thread_priority (void)
266 {
267 #ifdef G_OS_WIN32
268   DWORD taskIndex = 0;
269 #endif
270 
271   if (!__gst_audio_init_thread_priority ())
272     return FALSE;
273 
274 #ifdef G_OS_WIN32
275   /* This is only used from ringbuffer thread functions, so we don't need to
276    * ever need to revert the thread priorities. */
277   return _gst_audio_avrt_tbl.AvSetMmThreadCharacteristics (TEXT ("Pro Audio"),
278       &taskIndex) != 0;
279 #else
280   return TRUE;
281 #endif
282 }
283