16 * You should have received a copy of the GNU Library General Public |
15 * You should have received a copy of the GNU Library General Public |
17 * License along with this library; if not, write to the |
16 * License along with this library; if not, write to the |
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
19 * Boston, MA 02111-1307, USA. |
18 * Boston, MA 02111-1307, USA. |
20 */ |
19 */ |
|
20 /* Element-Checklist-Version: 5 */ |
21 |
21 |
22 /** |
22 /** |
23 * SECTION:element-audioresample |
23 * SECTION:element-audioresample |
24 * |
24 * |
25 * audioresample resamples raw audio buffers to different sample rates using |
25 * <refsect2> |
|
26 * Audioresample resamples raw audio buffers to different sample rates using |
26 * a configurable windowing function to enhance quality. |
27 * a configurable windowing function to enhance quality. |
|
28 * <title>Example launch line</title> |
|
29 * <para> |
|
30 * <programlisting> |
|
31 * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! audio/x-raw-int, rate=8000 ! alsasink |
|
32 * </programlisting> |
|
33 * Decode an Ogg/Vorbis downsample to 8Khz and play sound through alsa. |
|
34 * To create the Ogg/Vorbis file refer to the documentation of vorbisenc. |
|
35 * </para> |
|
36 * </refsect2> |
27 * |
37 * |
28 * <refsect2> |
38 * Last reviewed on 2006-03-02 (0.10.4) |
29 * <title>Example launch line</title> |
|
30 * |[ |
|
31 * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! audio/x-raw-int, rate=8000 ! alsasink |
|
32 * ]| Decode an Ogg/Vorbis downsample to 8Khz and play sound through alsa. |
|
33 * To create the Ogg/Vorbis file refer to the documentation of vorbisenc. |
|
34 * </refsect2> |
|
35 */ |
|
36 |
|
37 /* TODO: |
|
38 * - Enable SSE/ARM optimizations and select at runtime |
|
39 */ |
39 */ |
40 |
40 |
41 #ifdef HAVE_CONFIG_H |
41 #ifdef HAVE_CONFIG_H |
42 #include "config.h" |
42 #include "config.h" |
43 #endif |
43 #endif |
44 |
44 |
45 #include <string.h> |
45 #include <string.h> |
46 #include <math.h> |
46 #include <math.h> |
47 |
47 |
|
48 /*#define DEBUG_ENABLED */ |
48 #include "gstaudioresample.h" |
49 #include "gstaudioresample.h" |
49 #include <gst/audio/audio.h> |
50 #include <gst/audio/audio.h> |
50 #include <gst/base/gstbasetransform.h> |
51 #include <gst/base/gstbasetransform.h> |
51 |
52 |
52 #if defined AUDIORESAMPLE_FORMAT_AUTO |
53 GST_DEBUG_CATEGORY_STATIC (audioresample_debug); |
53 #define OIL_ENABLE_UNSTABLE_API |
54 #define GST_CAT_DEFAULT audioresample_debug |
54 #include <liboil/liboilprofile.h> |
55 |
55 #include <liboil/liboil.h> |
56 /* elementfactory information */ |
56 #endif |
57 static const GstElementDetails gst_audioresample_details = |
57 |
58 GST_ELEMENT_DETAILS ("Audio scaler", |
58 GST_DEBUG_CATEGORY (audio_resample_debug); |
59 "Filter/Converter/Audio", |
59 #define GST_CAT_DEFAULT audio_resample_debug |
60 "Resample audio", |
|
61 "David Schleef <ds@schleef.org>"); |
|
62 |
|
63 #define DEFAULT_FILTERLEN 16 |
60 |
64 |
61 enum |
65 enum |
62 { |
66 { |
63 PROP_0, |
67 PROP_0, |
64 PROP_QUALITY, |
68 PROP_FILTERLEN |
65 PROP_FILTER_LENGTH |
|
66 }; |
69 }; |
67 |
70 |
68 #define SUPPORTED_CAPS \ |
71 #define SUPPORTED_CAPS \ |
69 GST_STATIC_CAPS ( \ |
72 GST_STATIC_CAPS ( \ |
70 "audio/x-raw-float, " \ |
73 "audio/x-raw-int, " \ |
71 "rate = (int) [ 1, MAX ], " \ |
74 "rate = (int) [ 1, MAX ], " \ |
72 "channels = (int) [ 1, MAX ], " \ |
75 "channels = (int) [ 1, MAX ], " \ |
73 "endianness = (int) BYTE_ORDER, " \ |
76 "endianness = (int) BYTE_ORDER, " \ |
74 "width = (int) { 32, 64 }; " \ |
77 "width = (int) 16, " \ |
|
78 "depth = (int) 16, " \ |
|
79 "signed = (boolean) true;" \ |
75 "audio/x-raw-int, " \ |
80 "audio/x-raw-int, " \ |
76 "rate = (int) [ 1, MAX ], " \ |
81 "rate = (int) [ 1, MAX ], " \ |
77 "channels = (int) [ 1, MAX ], " \ |
82 "channels = (int) [ 1, MAX ], " \ |
78 "endianness = (int) BYTE_ORDER, " \ |
83 "endianness = (int) BYTE_ORDER, " \ |
79 "width = (int) 32, " \ |
84 "width = (int) 32, " \ |
80 "depth = (int) 32, " \ |
85 "depth = (int) 32, " \ |
81 "signed = (boolean) true; " \ |
86 "signed = (boolean) true;" \ |
82 "audio/x-raw-int, " \ |
87 "audio/x-raw-float, " \ |
83 "rate = (int) [ 1, MAX ], " \ |
88 "rate = (int) [ 1, MAX ], " \ |
84 "channels = (int) [ 1, MAX ], " \ |
89 "channels = (int) [ 1, MAX ], " \ |
85 "endianness = (int) BYTE_ORDER, " \ |
90 "endianness = (int) BYTE_ORDER, " \ |
86 "width = (int) 24, " \ |
91 "width = (int) 32; " \ |
87 "depth = (int) 24, " \ |
92 "audio/x-raw-float, " \ |
88 "signed = (boolean) true; " \ |
93 "rate = (int) [ 1, MAX ], " \ |
89 "audio/x-raw-int, " \ |
|
90 "rate = (int) [ 1, MAX ], " \ |
|
91 "channels = (int) [ 1, MAX ], " \ |
94 "channels = (int) [ 1, MAX ], " \ |
92 "endianness = (int) BYTE_ORDER, " \ |
95 "endianness = (int) BYTE_ORDER, " \ |
93 "width = (int) 16, " \ |
96 "width = (int) 64" \ |
94 "depth = (int) 16, " \ |
|
95 "signed = (boolean) true; " \ |
|
96 "audio/x-raw-int, " \ |
|
97 "rate = (int) [ 1, MAX ], " \ |
|
98 "channels = (int) [ 1, MAX ], " \ |
|
99 "endianness = (int) BYTE_ORDER, " \ |
|
100 "width = (int) 8, " \ |
|
101 "depth = (int) 8, " \ |
|
102 "signed = (boolean) true" \ |
|
103 ) |
97 ) |
104 |
98 |
105 /* If TRUE integer arithmetic resampling is faster and will be used if appropiate */ |
99 static GstStaticPadTemplate gst_audioresample_sink_template = |
106 #if defined AUDIORESAMPLE_FORMAT_INT |
|
107 static gboolean gst_audio_resample_use_int = TRUE; |
|
108 #elif defined AUDIORESAMPLE_FORMAT_FLOAT |
|
109 static gboolean gst_audio_resample_use_int = FALSE; |
|
110 #else |
|
111 static gboolean gst_audio_resample_use_int = FALSE; |
|
112 #endif |
|
113 |
|
114 static GstStaticPadTemplate gst_audio_resample_sink_template = |
|
115 GST_STATIC_PAD_TEMPLATE ("sink", |
100 GST_STATIC_PAD_TEMPLATE ("sink", |
116 GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS); |
101 GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS); |
117 |
102 |
118 static GstStaticPadTemplate gst_audio_resample_src_template = |
103 static GstStaticPadTemplate gst_audioresample_src_template = |
119 GST_STATIC_PAD_TEMPLATE ("src", |
104 GST_STATIC_PAD_TEMPLATE ("src", |
120 GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS); |
105 GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS); |
121 |
106 |
122 static void gst_audio_resample_set_property (GObject * object, |
107 static void gst_audioresample_set_property (GObject * object, |
123 guint prop_id, const GValue * value, GParamSpec * pspec); |
108 guint prop_id, const GValue * value, GParamSpec * pspec); |
124 static void gst_audio_resample_get_property (GObject * object, |
109 static void gst_audioresample_get_property (GObject * object, |
125 guint prop_id, GValue * value, GParamSpec * pspec); |
110 guint prop_id, GValue * value, GParamSpec * pspec); |
126 |
111 |
127 /* vmethods */ |
112 /* vmethods */ |
128 static gboolean gst_audio_resample_get_unit_size (GstBaseTransform * base, |
113 static gboolean audioresample_get_unit_size (GstBaseTransform * base, |
129 GstCaps * caps, guint * size); |
114 GstCaps * caps, guint * size); |
130 static GstCaps *gst_audio_resample_transform_caps (GstBaseTransform * base, |
115 static GstCaps *audioresample_transform_caps (GstBaseTransform * base, |
131 GstPadDirection direction, GstCaps * caps); |
116 GstPadDirection direction, GstCaps * caps); |
132 static void gst_audio_resample_fixate_caps (GstBaseTransform * base, |
117 static gboolean audioresample_transform_size (GstBaseTransform * trans, |
133 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps); |
|
134 static gboolean gst_audio_resample_transform_size (GstBaseTransform * trans, |
|
135 GstPadDirection direction, GstCaps * incaps, guint insize, |
118 GstPadDirection direction, GstCaps * incaps, guint insize, |
136 GstCaps * outcaps, guint * outsize); |
119 GstCaps * outcaps, guint * outsize); |
137 static gboolean gst_audio_resample_set_caps (GstBaseTransform * base, |
120 static gboolean audioresample_set_caps (GstBaseTransform * base, |
138 GstCaps * incaps, GstCaps * outcaps); |
121 GstCaps * incaps, GstCaps * outcaps); |
139 static GstFlowReturn gst_audio_resample_transform (GstBaseTransform * base, |
122 static GstFlowReturn audioresample_pushthrough (GstAudioresample * |
|
123 audioresample); |
|
124 static GstFlowReturn audioresample_transform (GstBaseTransform * base, |
140 GstBuffer * inbuf, GstBuffer * outbuf); |
125 GstBuffer * inbuf, GstBuffer * outbuf); |
141 static gboolean gst_audio_resample_event (GstBaseTransform * base, |
126 static gboolean audioresample_event (GstBaseTransform * base, GstEvent * event); |
142 GstEvent * event); |
127 static gboolean audioresample_start (GstBaseTransform * base); |
143 static gboolean gst_audio_resample_start (GstBaseTransform * base); |
128 static gboolean audioresample_stop (GstBaseTransform * base); |
144 static gboolean gst_audio_resample_stop (GstBaseTransform * base); |
129 |
145 static gboolean gst_audio_resample_query (GstPad * pad, GstQuery * query); |
130 static gboolean audioresample_query (GstPad * pad, GstQuery * query); |
146 static const GstQueryType *gst_audio_resample_query_type (GstPad * pad); |
131 static const GstQueryType *audioresample_query_type (GstPad * pad); |
147 |
132 |
148 GST_BOILERPLATE (GstAudioResample, gst_audio_resample, GstBaseTransform, |
133 #define DEBUG_INIT(bla) \ |
149 GST_TYPE_BASE_TRANSFORM); |
134 GST_DEBUG_CATEGORY_INIT (audioresample_debug, "audioresample", 0, "audio resampling element"); |
|
135 |
|
136 GST_BOILERPLATE_FULL (GstAudioresample, gst_audioresample, GstBaseTransform, |
|
137 GST_TYPE_BASE_TRANSFORM, DEBUG_INIT); |
150 |
138 |
151 static void |
139 static void |
152 gst_audio_resample_base_init (gpointer g_class) |
140 gst_audioresample_base_init (gpointer g_class) |
153 { |
141 { |
154 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); |
142 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); |
155 |
143 |
156 gst_element_class_add_pad_template (gstelement_class, |
144 gst_element_class_add_pad_template (gstelement_class, |
157 gst_static_pad_template_get (&gst_audio_resample_src_template)); |
145 gst_static_pad_template_get (&gst_audioresample_src_template)); |
158 gst_element_class_add_pad_template (gstelement_class, |
146 gst_element_class_add_pad_template (gstelement_class, |
159 gst_static_pad_template_get (&gst_audio_resample_sink_template)); |
147 gst_static_pad_template_get (&gst_audioresample_sink_template)); |
160 |
148 |
161 gst_element_class_set_details_simple (gstelement_class, "Audio resampler", |
149 gst_element_class_set_details (gstelement_class, &gst_audioresample_details); |
162 "Filter/Converter/Audio", "Resamples audio", |
|
163 "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); |
|
164 } |
150 } |
165 |
151 |
166 static void |
152 static void |
167 gst_audio_resample_class_init (GstAudioResampleClass * klass) |
153 gst_audioresample_class_init (GstAudioresampleClass * klass) |
168 { |
154 { |
169 GObjectClass *gobject_class = (GObjectClass *) klass; |
155 GObjectClass *gobject_class; |
170 |
156 |
171 gobject_class->set_property = gst_audio_resample_set_property; |
157 gobject_class = (GObjectClass *) klass; |
172 gobject_class->get_property = gst_audio_resample_get_property; |
158 |
173 |
159 gobject_class->set_property = gst_audioresample_set_property; |
174 g_object_class_install_property (gobject_class, PROP_QUALITY, |
160 gobject_class->get_property = gst_audioresample_get_property; |
175 g_param_spec_int ("quality", "Quality", "Resample quality with 0 being " |
161 |
176 "the lowest and 10 being the best", |
162 g_object_class_install_property (gobject_class, PROP_FILTERLEN, |
177 SPEEX_RESAMPLER_QUALITY_MIN, SPEEX_RESAMPLER_QUALITY_MAX, |
163 g_param_spec_int ("filter_length", "filter_length", "filter_length", |
178 SPEEX_RESAMPLER_QUALITY_DEFAULT, |
164 0, G_MAXINT, DEFAULT_FILTERLEN, |
179 G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); |
165 G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); |
180 |
166 |
181 /* FIXME 0.11: Remove this property, it's just for compatibility |
|
182 * with old audioresample |
|
183 */ |
|
184 /** |
|
185 * GstAudioResample:filter-length: |
|
186 * |
|
187 * Length of the resample filter |
|
188 * |
|
189 * Deprectated: Use #GstAudioResample:quality property instead |
|
190 */ |
|
191 g_object_class_install_property (gobject_class, PROP_FILTER_LENGTH, |
|
192 g_param_spec_int ("filter-length", "Filter length", |
|
193 "Length of the resample filter", 0, G_MAXINT, 64, G_PARAM_READWRITE)); |
|
194 |
|
195 GST_BASE_TRANSFORM_CLASS (klass)->start = |
167 GST_BASE_TRANSFORM_CLASS (klass)->start = |
196 GST_DEBUG_FUNCPTR (gst_audio_resample_start); |
168 GST_DEBUG_FUNCPTR (audioresample_start); |
197 GST_BASE_TRANSFORM_CLASS (klass)->stop = |
169 GST_BASE_TRANSFORM_CLASS (klass)->stop = |
198 GST_DEBUG_FUNCPTR (gst_audio_resample_stop); |
170 GST_DEBUG_FUNCPTR (audioresample_stop); |
199 GST_BASE_TRANSFORM_CLASS (klass)->transform_size = |
171 GST_BASE_TRANSFORM_CLASS (klass)->transform_size = |
200 GST_DEBUG_FUNCPTR (gst_audio_resample_transform_size); |
172 GST_DEBUG_FUNCPTR (audioresample_transform_size); |
201 GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = |
173 GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = |
202 GST_DEBUG_FUNCPTR (gst_audio_resample_get_unit_size); |
174 GST_DEBUG_FUNCPTR (audioresample_get_unit_size); |
203 GST_BASE_TRANSFORM_CLASS (klass)->transform_caps = |
175 GST_BASE_TRANSFORM_CLASS (klass)->transform_caps = |
204 GST_DEBUG_FUNCPTR (gst_audio_resample_transform_caps); |
176 GST_DEBUG_FUNCPTR (audioresample_transform_caps); |
205 GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps = |
|
206 GST_DEBUG_FUNCPTR (gst_audio_resample_fixate_caps); |
|
207 GST_BASE_TRANSFORM_CLASS (klass)->set_caps = |
177 GST_BASE_TRANSFORM_CLASS (klass)->set_caps = |
208 GST_DEBUG_FUNCPTR (gst_audio_resample_set_caps); |
178 GST_DEBUG_FUNCPTR (audioresample_set_caps); |
209 GST_BASE_TRANSFORM_CLASS (klass)->transform = |
179 GST_BASE_TRANSFORM_CLASS (klass)->transform = |
210 GST_DEBUG_FUNCPTR (gst_audio_resample_transform); |
180 GST_DEBUG_FUNCPTR (audioresample_transform); |
211 GST_BASE_TRANSFORM_CLASS (klass)->event = |
181 GST_BASE_TRANSFORM_CLASS (klass)->event = |
212 GST_DEBUG_FUNCPTR (gst_audio_resample_event); |
182 GST_DEBUG_FUNCPTR (audioresample_event); |
213 |
183 |
214 GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE; |
184 GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE; |
215 } |
185 } |
216 |
186 |
|
187 |
217 static void |
188 static void |
218 gst_audio_resample_init (GstAudioResample * resample, |
189 gst_audioresample_init (GstAudioresample * audioresample, |
219 GstAudioResampleClass * klass) |
190 GstAudioresampleClass * klass) |
220 { |
191 { |
221 GstBaseTransform *trans = GST_BASE_TRANSFORM (resample); |
192 GstBaseTransform *trans; |
222 |
193 |
223 resample->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT; |
194 trans = GST_BASE_TRANSFORM (audioresample); |
224 |
195 |
225 resample->need_discont = FALSE; |
196 /* buffer alloc passthrough is too impossible. FIXME, it |
226 |
197 * is trivial in the passthrough case. */ |
227 gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query); |
198 gst_pad_set_bufferalloc_function (trans->sinkpad, NULL); |
228 gst_pad_set_query_type_function (trans->srcpad, |
199 |
229 gst_audio_resample_query_type); |
200 audioresample->filter_length = DEFAULT_FILTERLEN; |
|
201 |
|
202 audioresample->need_discont = FALSE; |
|
203 |
|
204 gst_pad_set_query_function (trans->srcpad, audioresample_query); |
|
205 gst_pad_set_query_type_function (trans->srcpad, audioresample_query_type); |
230 } |
206 } |
231 |
207 |
232 /* vmethods */ |
208 /* vmethods */ |
233 static gboolean |
209 static gboolean |
234 gst_audio_resample_start (GstBaseTransform * base) |
210 audioresample_start (GstBaseTransform * base) |
235 { |
211 { |
236 GstAudioResample *resample = GST_AUDIO_RESAMPLE (base); |
212 GstAudioresample *audioresample = GST_AUDIORESAMPLE (base); |
237 |
213 |
238 resample->next_offset = -1; |
214 audioresample->resample = resample_new (); |
239 resample->next_ts = -1; |
215 audioresample->ts_offset = -1; |
240 resample->next_upstream_ts = -1; |
216 audioresample->offset = -1; |
|
217 audioresample->next_ts = -1; |
|
218 |
|
219 resample_set_filter_length (audioresample->resample, |
|
220 audioresample->filter_length); |
241 |
221 |
242 return TRUE; |
222 return TRUE; |
243 } |
223 } |
244 |
224 |
245 static gboolean |
225 static gboolean |
246 gst_audio_resample_stop (GstBaseTransform * base) |
226 audioresample_stop (GstBaseTransform * base) |
247 { |
227 { |
248 GstAudioResample *resample = GST_AUDIO_RESAMPLE (base); |
228 GstAudioresample *audioresample = GST_AUDIORESAMPLE (base); |
249 |
229 |
250 if (resample->state) { |
230 if (audioresample->resample) { |
251 resample->funcs->destroy (resample->state); |
231 resample_free (audioresample->resample); |
252 resample->state = NULL; |
232 audioresample->resample = NULL; |
253 } |
233 } |
254 |
234 |
255 resample->funcs = NULL; |
235 gst_caps_replace (&audioresample->sinkcaps, NULL); |
256 |
236 gst_caps_replace (&audioresample->srccaps, NULL); |
257 g_free (resample->tmp_in); |
|
258 resample->tmp_in = NULL; |
|
259 resample->tmp_in_size = 0; |
|
260 |
|
261 g_free (resample->tmp_out); |
|
262 resample->tmp_out = NULL; |
|
263 resample->tmp_out_size = 0; |
|
264 |
|
265 gst_caps_replace (&resample->sinkcaps, NULL); |
|
266 gst_caps_replace (&resample->srccaps, NULL); |
|
267 |
237 |
268 return TRUE; |
238 return TRUE; |
269 } |
239 } |
270 |
240 |
271 static gboolean |
241 static gboolean |
272 gst_audio_resample_get_unit_size (GstBaseTransform * base, GstCaps * caps, |
242 audioresample_get_unit_size (GstBaseTransform * base, GstCaps * caps, |
273 guint * size) |
243 guint * size) |
274 { |
244 { |
275 gint width, channels; |
245 gint width, channels; |
276 GstStructure *structure; |
246 GstStructure *structure; |
277 gboolean ret; |
247 gboolean ret; |
278 |
248 |
279 g_return_val_if_fail (size != NULL, FALSE); |
249 g_assert (size); |
280 |
250 |
281 /* this works for both float and int */ |
251 /* this works for both float and int */ |
282 structure = gst_caps_get_structure (caps, 0); |
252 structure = gst_caps_get_structure (caps, 0); |
283 ret = gst_structure_get_int (structure, "width", &width); |
253 ret = gst_structure_get_int (structure, "width", &width); |
284 ret &= gst_structure_get_int (structure, "channels", &channels); |
254 ret &= gst_structure_get_int (structure, "channels", &channels); |
285 |
255 g_return_val_if_fail (ret, FALSE); |
286 if (G_UNLIKELY (!ret)) |
256 |
287 return FALSE; |
257 *size = width * channels / 8; |
288 |
|
289 *size = (width / 8) * channels; |
|
290 |
258 |
291 return TRUE; |
259 return TRUE; |
292 } |
260 } |
293 |
261 |
294 static GstCaps * |
262 static GstCaps * |
295 gst_audio_resample_transform_caps (GstBaseTransform * base, |
263 audioresample_transform_caps (GstBaseTransform * base, |
296 GstPadDirection direction, GstCaps * caps) |
264 GstPadDirection direction, GstCaps * caps) |
297 { |
265 { |
298 const GValue *val; |
|
299 GstStructure *s; |
|
300 GstCaps *res; |
266 GstCaps *res; |
301 |
267 GstStructure *structure; |
302 /* transform single caps into input_caps + input_caps with the rate |
268 |
303 * field set to our supported range. This ensures that upstream knows |
269 /* transform caps gives one single caps so we can just replace |
304 * about downstream's prefered rate(s) and can negotiate accordingly. */ |
270 * the rate property with our range. */ |
305 res = gst_caps_copy (caps); |
271 res = gst_caps_copy (caps); |
306 |
272 structure = gst_caps_get_structure (res, 0); |
307 /* first, however, check if the caps contain a range for the rate field, in |
273 gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL); |
308 * which case that side isn't going to care much about the exact sample rate |
|
309 * chosen and we should just assume things will get fixated to something sane |
|
310 * and we may just as well offer our full range instead of the range in the |
|
311 * caps. If the rate is not an int range value, it's likely to express a |
|
312 * real preference or limitation and we should maintain that structure as |
|
313 * preference by putting it first into the transformed caps, and only add |
|
314 * our full rate range as second option */ |
|
315 s = gst_caps_get_structure (res, 0); |
|
316 val = gst_structure_get_value (s, "rate"); |
|
317 if (val == NULL || GST_VALUE_HOLDS_INT_RANGE (val)) { |
|
318 /* overwrite existing range, or add field if it doesn't exist yet */ |
|
319 gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL); |
|
320 } else { |
|
321 /* append caps with full range to existing caps with non-range rate field */ |
|
322 s = gst_structure_copy (s); |
|
323 gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL); |
|
324 gst_caps_append_structure (res, s); |
|
325 } |
|
326 |
274 |
327 return res; |
275 return res; |
328 } |
276 } |
329 |
277 |
330 /* Fixate rate to the allowed rate that has the smallest difference */ |
278 static gboolean |
331 static void |
279 resample_set_state_from_caps (ResampleState * state, GstCaps * incaps, |
332 gst_audio_resample_fixate_caps (GstBaseTransform * base, |
280 GstCaps * outcaps, gint * channels, gint * inrate, gint * outrate) |
333 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps) |
|
334 { |
|
335 GstStructure *s; |
|
336 gint rate; |
|
337 |
|
338 s = gst_caps_get_structure (caps, 0); |
|
339 if (G_UNLIKELY (!gst_structure_get_int (s, "rate", &rate))) |
|
340 return; |
|
341 |
|
342 s = gst_caps_get_structure (othercaps, 0); |
|
343 gst_structure_fixate_field_nearest_int (s, "rate", rate); |
|
344 } |
|
345 |
|
346 static const SpeexResampleFuncs * |
|
347 gst_audio_resample_get_funcs (gint width, gboolean fp) |
|
348 { |
|
349 const SpeexResampleFuncs *funcs = NULL; |
|
350 |
|
351 if (gst_audio_resample_use_int && (width == 8 || width == 16) && !fp) |
|
352 funcs = &int_funcs; |
|
353 else if ((!gst_audio_resample_use_int && (width == 8 || width == 16) && !fp) |
|
354 || (width == 32 && fp)) |
|
355 funcs = &float_funcs; |
|
356 else if ((width == 64 && fp) || ((width == 32 || width == 24) && !fp)) |
|
357 funcs = &double_funcs; |
|
358 else |
|
359 g_assert_not_reached (); |
|
360 |
|
361 return funcs; |
|
362 } |
|
363 |
|
364 static SpeexResamplerState * |
|
365 gst_audio_resample_init_state (GstAudioResample * resample, gint width, |
|
366 gint channels, gint inrate, gint outrate, gint quality, gboolean fp) |
|
367 { |
|
368 SpeexResamplerState *ret = NULL; |
|
369 gint err = RESAMPLER_ERR_SUCCESS; |
|
370 const SpeexResampleFuncs *funcs = gst_audio_resample_get_funcs (width, fp); |
|
371 |
|
372 ret = funcs->init (channels, inrate, outrate, quality, &err); |
|
373 |
|
374 if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) { |
|
375 GST_ERROR_OBJECT (resample, "Failed to create resampler state: %s", |
|
376 funcs->strerror (err)); |
|
377 return NULL; |
|
378 } |
|
379 |
|
380 funcs->skip_zeros (ret); |
|
381 |
|
382 return ret; |
|
383 } |
|
384 |
|
385 static gboolean |
|
386 gst_audio_resample_update_state (GstAudioResample * resample, gint width, |
|
387 gint channels, gint inrate, gint outrate, gint quality, gboolean fp) |
|
388 { |
|
389 gboolean ret = TRUE; |
|
390 gboolean updated_latency = FALSE; |
|
391 |
|
392 updated_latency = (resample->inrate != inrate |
|
393 || quality != resample->quality) && resample->state != NULL; |
|
394 |
|
395 if (resample->state == NULL) { |
|
396 ret = TRUE; |
|
397 } else if (resample->channels != channels || fp != resample->fp |
|
398 || width != resample->width) { |
|
399 resample->funcs->destroy (resample->state); |
|
400 resample->state = |
|
401 gst_audio_resample_init_state (resample, width, channels, inrate, |
|
402 outrate, quality, fp); |
|
403 |
|
404 resample->funcs = gst_audio_resample_get_funcs (width, fp); |
|
405 ret = (resample->state != NULL); |
|
406 } else if (resample->inrate != inrate || resample->outrate != outrate) { |
|
407 gint err = RESAMPLER_ERR_SUCCESS; |
|
408 |
|
409 err = resample->funcs->set_rate (resample->state, inrate, outrate); |
|
410 |
|
411 if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) |
|
412 GST_ERROR_OBJECT (resample, "Failed to update rate: %s", |
|
413 resample->funcs->strerror (err)); |
|
414 |
|
415 ret = (err == RESAMPLER_ERR_SUCCESS); |
|
416 } else if (quality != resample->quality) { |
|
417 gint err = RESAMPLER_ERR_SUCCESS; |
|
418 |
|
419 err = resample->funcs->set_quality (resample->state, quality); |
|
420 |
|
421 if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) |
|
422 GST_ERROR_OBJECT (resample, "Failed to update quality: %s", |
|
423 resample->funcs->strerror (err)); |
|
424 |
|
425 ret = (err == RESAMPLER_ERR_SUCCESS); |
|
426 } |
|
427 |
|
428 resample->width = width; |
|
429 resample->channels = channels; |
|
430 resample->fp = fp; |
|
431 resample->quality = quality; |
|
432 resample->inrate = inrate; |
|
433 resample->outrate = outrate; |
|
434 |
|
435 if (updated_latency) |
|
436 gst_element_post_message (GST_ELEMENT (resample), |
|
437 gst_message_new_latency (GST_OBJECT (resample))); |
|
438 |
|
439 return ret; |
|
440 } |
|
441 |
|
442 static void |
|
443 gst_audio_resample_reset_state (GstAudioResample * resample) |
|
444 { |
|
445 if (resample->state) |
|
446 resample->funcs->reset_mem (resample->state); |
|
447 } |
|
448 |
|
449 static gboolean |
|
450 gst_audio_resample_parse_caps (GstCaps * incaps, |
|
451 GstCaps * outcaps, gint * width, gint * channels, gint * inrate, |
|
452 gint * outrate, gboolean * fp) |
|
453 { |
281 { |
454 GstStructure *structure; |
282 GstStructure *structure; |
455 gboolean ret; |
283 gboolean ret; |
456 gint mywidth, myinrate, myoutrate, mychannels; |
284 gint myinrate, myoutrate; |
457 gboolean myfp; |
285 int mychannels; |
|
286 gint width, depth; |
|
287 ResampleFormat format; |
458 |
288 |
459 GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %" |
289 GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %" |
460 GST_PTR_FORMAT, incaps, outcaps); |
290 GST_PTR_FORMAT, incaps, outcaps); |
461 |
291 |
462 structure = gst_caps_get_structure (incaps, 0); |
292 structure = gst_caps_get_structure (incaps, 0); |
463 |
293 |
464 if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float")) |
294 /* get width */ |
465 myfp = TRUE; |
295 ret = gst_structure_get_int (structure, "width", &width); |
466 else |
296 if (!ret) |
467 myfp = FALSE; |
297 goto no_width; |
468 |
298 |
|
299 /* figure out the format */ |
|
300 if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float")) { |
|
301 if (width == 32) |
|
302 format = RESAMPLE_FORMAT_F32; |
|
303 else if (width == 64) |
|
304 format = RESAMPLE_FORMAT_F64; |
|
305 else |
|
306 goto wrong_depth; |
|
307 } else { |
|
308 /* for int, depth and width must be the same */ |
|
309 ret = gst_structure_get_int (structure, "depth", &depth); |
|
310 if (!ret || width != depth) |
|
311 goto not_equal; |
|
312 |
|
313 if (width == 16) |
|
314 format = RESAMPLE_FORMAT_S16; |
|
315 else if (width == 32) |
|
316 format = RESAMPLE_FORMAT_S32; |
|
317 else |
|
318 goto wrong_depth; |
|
319 } |
469 ret = gst_structure_get_int (structure, "rate", &myinrate); |
320 ret = gst_structure_get_int (structure, "rate", &myinrate); |
470 ret &= gst_structure_get_int (structure, "channels", &mychannels); |
321 ret &= gst_structure_get_int (structure, "channels", &mychannels); |
471 ret &= gst_structure_get_int (structure, "width", &mywidth); |
322 if (!ret) |
472 if (G_UNLIKELY (!ret)) |
|
473 goto no_in_rate_channels; |
323 goto no_in_rate_channels; |
474 |
324 |
475 structure = gst_caps_get_structure (outcaps, 0); |
325 structure = gst_caps_get_structure (outcaps, 0); |
476 ret = gst_structure_get_int (structure, "rate", &myoutrate); |
326 ret = gst_structure_get_int (structure, "rate", &myoutrate); |
477 if (G_UNLIKELY (!ret)) |
327 if (!ret) |
478 goto no_out_rate; |
328 goto no_out_rate; |
479 |
329 |
480 if (channels) |
330 if (channels) |
481 *channels = mychannels; |
331 *channels = mychannels; |
482 if (inrate) |
332 if (inrate) |
483 *inrate = myinrate; |
333 *inrate = myinrate; |
484 if (outrate) |
334 if (outrate) |
485 *outrate = myoutrate; |
335 *outrate = myoutrate; |
486 if (width) |
336 |
487 *width = mywidth; |
337 resample_set_format (state, format); |
488 if (fp) |
338 resample_set_n_channels (state, mychannels); |
489 *fp = myfp; |
339 resample_set_input_rate (state, myinrate); |
|
340 resample_set_output_rate (state, myoutrate); |
490 |
341 |
491 return TRUE; |
342 return TRUE; |
492 |
343 |
493 /* ERRORS */ |
344 /* ERRORS */ |
|
345 no_width: |
|
346 { |
|
347 GST_DEBUG ("failed to get width from caps"); |
|
348 return FALSE; |
|
349 } |
|
350 not_equal: |
|
351 { |
|
352 GST_DEBUG ("width %d and depth %d must be the same", width, depth); |
|
353 return FALSE; |
|
354 } |
|
355 wrong_depth: |
|
356 { |
|
357 GST_DEBUG ("unknown depth %d found", depth); |
|
358 return FALSE; |
|
359 } |
494 no_in_rate_channels: |
360 no_in_rate_channels: |
495 { |
361 { |
496 GST_DEBUG ("could not get input rate and channels"); |
362 GST_DEBUG ("could not get input rate and channels"); |
497 return FALSE; |
363 return FALSE; |
498 } |
364 } |
535 } else { |
388 } else { |
536 sinkcaps = othercaps; |
389 sinkcaps = othercaps; |
537 srccaps = caps; |
390 srccaps = caps; |
538 } |
391 } |
539 |
392 |
540 /* Get sample width -> bytes_per_samp, channels, inrate, outrate */ |
393 /* if the caps are the ones that _set_caps got called with; we can use |
541 ret = |
394 * our own state; otherwise we'll have to create a state */ |
542 gst_audio_resample_parse_caps (caps, othercaps, &bytes_per_samp, |
395 if (gst_caps_is_equal (sinkcaps, audioresample->sinkcaps) && |
543 &channels, &inrate, &outrate, NULL); |
396 gst_caps_is_equal (srccaps, audioresample->srccaps)) { |
544 if (G_UNLIKELY (!ret)) { |
397 use_internal = TRUE; |
545 GST_ERROR_OBJECT (base, "Wrong caps"); |
398 state = audioresample->resample; |
546 return FALSE; |
399 } else { |
547 } |
400 GST_DEBUG_OBJECT (audioresample, |
548 /* Number of samples in either buffer is size / (width*channels) -> |
401 "caps are not the set caps, creating state"); |
549 * calculate the factor */ |
402 state = resample_new (); |
550 bytes_per_samp = bytes_per_samp * channels / 8; |
403 resample_set_filter_length (state, audioresample->filter_length); |
551 /* Convert source buffer size to samples */ |
404 resample_set_state_from_caps (state, sinkcaps, srccaps, NULL, NULL, NULL); |
552 size /= bytes_per_samp; |
405 } |
553 |
|
554 /* Simplify the conversion ratio factors */ |
|
555 gcd = _gcd (inrate, outrate); |
|
556 ratio_num = inrate / gcd; |
|
557 ratio_den = outrate / gcd; |
|
558 |
406 |
559 if (direction == GST_PAD_SINK) { |
407 if (direction == GST_PAD_SINK) { |
560 /* asked to convert size of an incoming buffer. Round up the output size */ |
408 /* asked to convert size of an incoming buffer */ |
561 *othersize = (size * ratio_den + ratio_num - 1) / ratio_num; |
409 *othersize = resample_get_output_size_for_input (state, size); |
562 *othersize *= bytes_per_samp; |
|
563 } else { |
410 } else { |
564 /* asked to convert size of an outgoing buffer. Round down the input size */ |
411 /* asked to convert size of an outgoing buffer */ |
565 *othersize = (size * ratio_num) / ratio_den; |
412 *othersize = resample_get_input_size_for_output (state, size); |
566 *othersize *= bytes_per_samp; |
413 } |
567 } |
414 g_assert (*othersize % state->sample_size == 0); |
568 |
415 |
569 GST_LOG_OBJECT (base, "transformed size %d to %d", size * bytes_per_samp, |
416 /* we make room for one extra sample, given that the resampling filter |
570 *othersize); |
417 * can output an extra one for non-integral i_rate/o_rate */ |
|
418 GST_LOG_OBJECT (base, "transformed size %d to %d", size, *othersize); |
|
419 |
|
420 if (!use_internal) { |
|
421 resample_free (state); |
|
422 } |
571 |
423 |
572 return ret; |
424 return ret; |
573 } |
425 } |
574 |
426 |
575 static gboolean |
427 static gboolean |
576 gst_audio_resample_set_caps (GstBaseTransform * base, GstCaps * incaps, |
428 audioresample_set_caps (GstBaseTransform * base, GstCaps * incaps, |
577 GstCaps * outcaps) |
429 GstCaps * outcaps) |
578 { |
430 { |
579 gboolean ret; |
431 gboolean ret; |
580 gint width = 0, inrate = 0, outrate = 0, channels = 0; |
432 gint inrate, outrate; |
581 gboolean fp; |
433 int channels; |
582 GstAudioResample *resample = GST_AUDIO_RESAMPLE (base); |
434 GstAudioresample *audioresample = GST_AUDIORESAMPLE (base); |
583 |
435 |
584 GST_LOG ("incaps %" GST_PTR_FORMAT ", outcaps %" |
436 GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %" |
585 GST_PTR_FORMAT, incaps, outcaps); |
437 GST_PTR_FORMAT, incaps, outcaps); |
586 |
438 |
587 ret = gst_audio_resample_parse_caps (incaps, outcaps, |
439 ret = resample_set_state_from_caps (audioresample->resample, incaps, outcaps, |
588 &width, &channels, &inrate, &outrate, &fp); |
440 &channels, &inrate, &outrate); |
589 |
441 |
590 if (G_UNLIKELY (!ret)) |
442 g_return_val_if_fail (ret, FALSE); |
591 return FALSE; |
443 |
592 |
444 audioresample->channels = channels; |
593 ret = |
445 GST_DEBUG_OBJECT (audioresample, "set channels to %d", channels); |
594 gst_audio_resample_update_state (resample, width, channels, inrate, |
446 audioresample->i_rate = inrate; |
595 outrate, resample->quality, fp); |
447 GST_DEBUG_OBJECT (audioresample, "set i_rate to %d", inrate); |
596 |
448 audioresample->o_rate = outrate; |
597 if (G_UNLIKELY (!ret)) |
449 GST_DEBUG_OBJECT (audioresample, "set o_rate to %d", outrate); |
598 return FALSE; |
|
599 |
450 |
600 /* save caps so we can short-circuit in the size_transform if the caps |
451 /* save caps so we can short-circuit in the size_transform if the caps |
601 * are the same */ |
452 * are the same */ |
602 gst_caps_replace (&resample->sinkcaps, incaps); |
453 gst_caps_replace (&audioresample->sinkcaps, incaps); |
603 gst_caps_replace (&resample->srccaps, outcaps); |
454 gst_caps_replace (&audioresample->srccaps, outcaps); |
604 |
455 |
605 return TRUE; |
456 return TRUE; |
606 } |
457 } |
607 |
458 |
608 #define GST_MAXINT24 (8388607) |
459 static gboolean |
609 #define GST_MININT24 (-8388608) |
460 audioresample_event (GstBaseTransform * base, GstEvent * event) |
610 |
461 { |
611 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN) |
462 GstAudioresample *audioresample; |
612 #define GST_READ_UINT24 GST_READ_UINT24_LE |
463 |
613 #define GST_WRITE_UINT24 GST_WRITE_UINT24_LE |
464 audioresample = GST_AUDIORESAMPLE (base); |
614 #else |
465 |
615 #define GST_READ_UINT24 GST_READ_UINT24_BE |
466 switch (GST_EVENT_TYPE (event)) { |
616 #define GST_WRITE_UINT24 GST_WRITE_UINT24_BE |
467 case GST_EVENT_FLUSH_START: |
617 #endif |
468 break; |
618 |
469 case GST_EVENT_FLUSH_STOP: |
619 static void |
470 resample_input_flush (audioresample->resample); |
620 gst_audio_resample_convert_buffer (GstAudioResample * resample, |
471 audioresample->ts_offset = -1; |
621 const guint8 * in, guint8 * out, guint len, gboolean inverse) |
472 audioresample->next_ts = -1; |
622 { |
473 audioresample->offset = -1; |
623 len *= resample->channels; |
474 break; |
624 |
475 case GST_EVENT_NEWSEGMENT: |
625 if (inverse) { |
476 resample_input_pushthrough (audioresample->resample); |
626 if (gst_audio_resample_use_int && resample->width == 8 && !resample->fp) { |
477 audioresample_pushthrough (audioresample); |
627 gint8 *o = (gint8 *) out; |
478 audioresample->ts_offset = -1; |
628 gint16 *i = (gint16 *) in; |
479 audioresample->next_ts = -1; |
629 gint32 tmp; |
480 audioresample->offset = -1; |
630 |
481 break; |
631 while (len) { |
482 case GST_EVENT_EOS: |
632 tmp = *i + (G_MAXINT8 >> 1); |
483 resample_input_eos (audioresample->resample); |
633 *o = CLAMP (tmp >> 8, G_MININT8, G_MAXINT8); |
484 audioresample_pushthrough (audioresample); |
634 o++; |
485 break; |
635 i++; |
486 default: |
636 len--; |
487 break; |
637 } |
488 } |
638 } else if (!gst_audio_resample_use_int && resample->width == 8 |
489 parent_class->event (base, event); |
639 && !resample->fp) { |
490 |
640 gint8 *o = (gint8 *) out; |
491 return TRUE; |
641 gfloat *i = (gfloat *) in; |
492 } |
642 gfloat tmp; |
493 |
643 |
494 static GstFlowReturn |
644 while (len) { |
495 audioresample_do_output (GstAudioresample * audioresample, GstBuffer * outbuf) |
645 tmp = *i; |
496 { |
646 *o = (gint8) CLAMP (tmp * G_MAXINT8 + 0.5, G_MININT8, G_MAXINT8); |
497 int outsize; |
647 o++; |
498 int outsamples; |
648 i++; |
499 ResampleState *r; |
649 len--; |
500 |
650 } |
501 r = audioresample->resample; |
651 } else if (!gst_audio_resample_use_int && resample->width == 16 |
502 |
652 && !resample->fp) { |
503 outsize = resample_get_output_size (r); |
653 gint16 *o = (gint16 *) out; |
504 GST_LOG_OBJECT (audioresample, "audioresample can give me %d bytes", outsize); |
654 gfloat *i = (gfloat *) in; |
505 |
655 gfloat tmp; |
506 /* protect against mem corruption */ |
656 |
507 if (outsize > GST_BUFFER_SIZE (outbuf)) { |
657 while (len) { |
508 GST_WARNING_OBJECT (audioresample, |
658 tmp = *i; |
509 "overriding audioresample's outsize %d with outbuffer's size %d", |
659 *o = (gint16) CLAMP (tmp * G_MAXINT16 + 0.5, G_MININT16, G_MAXINT16); |
510 outsize, GST_BUFFER_SIZE (outbuf)); |
660 o++; |
511 outsize = GST_BUFFER_SIZE (outbuf); |
661 i++; |
512 } |
662 len--; |
513 /* catch possibly wrong size differences */ |
663 } |
514 if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) { |
664 } else if (resample->width == 24 && !resample->fp) { |
515 GST_WARNING_OBJECT (audioresample, |
665 guint8 *o = (guint8 *) out; |
516 "audioresample's outsize %d too far from outbuffer's size %d", |
666 gdouble *i = (gdouble *) in; |
517 outsize, GST_BUFFER_SIZE (outbuf)); |
667 gdouble tmp; |
518 } |
668 |
519 |
669 while (len) { |
520 outsize = resample_get_output_data (r, GST_BUFFER_DATA (outbuf), outsize); |
670 tmp = *i; |
521 outsamples = outsize / r->sample_size; |
671 GST_WRITE_UINT24 (o, (gint32) CLAMP (tmp * GST_MAXINT24 + 0.5, |
522 GST_LOG_OBJECT (audioresample, "resample gave me %d bytes or %d samples", |
672 GST_MININT24, GST_MAXINT24)); |
523 outsize, outsamples); |
673 o += 3; |
524 |
674 i++; |
525 GST_BUFFER_OFFSET (outbuf) = audioresample->offset; |
675 len--; |
526 GST_BUFFER_TIMESTAMP (outbuf) = audioresample->next_ts; |
676 } |
527 |
677 } else if (resample->width == 32 && !resample->fp) { |
528 if (audioresample->ts_offset != -1) { |
678 gint32 *o = (gint32 *) out; |
529 audioresample->offset += outsamples; |
679 gdouble *i = (gdouble *) in; |
530 audioresample->ts_offset += outsamples; |
680 gdouble tmp; |
531 audioresample->next_ts = |
681 |
532 gst_util_uint64_scale_int (audioresample->ts_offset, GST_SECOND, |
682 while (len) { |
533 audioresample->o_rate); |
683 tmp = *i; |
534 GST_BUFFER_OFFSET_END (outbuf) = audioresample->offset; |
684 *o = (gint32) CLAMP (tmp * G_MAXINT32 + 0.5, G_MININT32, G_MAXINT32); |
535 |
685 o++; |
536 /* we calculate DURATION as the difference between "next" timestamp |
686 i++; |
537 * and current timestamp so we ensure a contiguous stream, instead of |
687 len--; |
538 * having rounding errors. */ |
688 } |
539 GST_BUFFER_DURATION (outbuf) = audioresample->next_ts - |
689 } else { |
540 GST_BUFFER_TIMESTAMP (outbuf); |
690 g_assert_not_reached (); |
|
691 } |
|
692 } else { |
541 } else { |
693 if (gst_audio_resample_use_int && resample->width == 8 && !resample->fp) { |
542 /* no valid offset know, we can still sortof calculate the duration though */ |
694 gint8 *i = (gint8 *) in; |
543 GST_BUFFER_DURATION (outbuf) = |
695 gint16 *o = (gint16 *) out; |
544 gst_util_uint64_scale_int (outsamples, GST_SECOND, |
696 gint32 tmp; |
545 audioresample->o_rate); |
697 |
546 } |
698 while (len) { |
547 |
699 tmp = *i; |
548 /* check for possible mem corruption */ |
700 *o = tmp << 8; |
549 if (outsize > GST_BUFFER_SIZE (outbuf)) { |
701 o++; |
550 /* this is an error that when it happens, would need fixing in the |
702 i++; |
551 * resample library; we told it we wanted only GST_BUFFER_SIZE (outbuf), |
703 len--; |
552 * and it gave us more ! */ |
704 } |
553 GST_WARNING_OBJECT (audioresample, |
705 } else if (!gst_audio_resample_use_int && resample->width == 8 |
554 "audioresample, you memory corrupting bastard. " |
706 && !resample->fp) { |
555 "you gave me outsize %d while my buffer was size %d", |
707 gint8 *i = (gint8 *) in; |
556 outsize, GST_BUFFER_SIZE (outbuf)); |
708 gfloat *o = (gfloat *) out; |
557 return GST_FLOW_ERROR; |
709 gfloat tmp; |
558 } |
710 |
559 /* catch possibly wrong size differences */ |
711 while (len) { |
560 if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) { |
712 tmp = *i; |
561 GST_WARNING_OBJECT (audioresample, |
713 *o = tmp / G_MAXINT8; |
562 "audioresample's written outsize %d too far from outbuffer's size %d", |
714 o++; |
563 outsize, GST_BUFFER_SIZE (outbuf)); |
715 i++; |
564 } |
716 len--; |
565 GST_BUFFER_SIZE (outbuf) = outsize; |
717 } |
566 |
718 } else if (!gst_audio_resample_use_int && resample->width == 16 |
567 if (G_UNLIKELY (audioresample->need_discont)) { |
719 && !resample->fp) { |
568 GST_DEBUG_OBJECT (audioresample, |
720 gint16 *i = (gint16 *) in; |
569 "marking this buffer with the DISCONT flag"); |
721 gfloat *o = (gfloat *) out; |
570 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); |
722 gfloat tmp; |
571 audioresample->need_discont = FALSE; |
723 |
572 } |
724 while (len) { |
573 |
725 tmp = *i; |
574 GST_LOG_OBJECT (audioresample, "transformed to buffer of %d bytes, ts %" |
726 *o = tmp / G_MAXINT16; |
575 GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %" |
727 o++; |
576 G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT, |
728 i++; |
577 outsize, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), |
729 len--; |
578 GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), |
730 } |
579 GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf)); |
731 } else if (resample->width == 24 && !resample->fp) { |
580 |
732 guint8 *i = (guint8 *) in; |
581 |
733 gdouble *o = (gdouble *) out; |
582 return GST_FLOW_OK; |
734 gdouble tmp; |
583 } |
735 guint32 tmp2; |
584 |
736 |
585 static gboolean |
737 while (len) { |
586 audioresample_check_discont (GstAudioresample * audioresample, |
738 tmp2 = GST_READ_UINT24 (i); |
|
739 if (tmp2 & 0x00800000) |
|
740 tmp2 |= 0xff000000; |
|
741 tmp = (gint32) tmp2; |
|
742 *o = tmp / GST_MAXINT24; |
|
743 o++; |
|
744 i += 3; |
|
745 len--; |
|
746 } |
|
747 } else if (resample->width == 32 && !resample->fp) { |
|
748 gint32 *i = (gint32 *) in; |
|
749 gdouble *o = (gdouble *) out; |
|
750 gdouble tmp; |
|
751 |
|
752 while (len) { |
|
753 tmp = *i; |
|
754 *o = tmp / G_MAXINT32; |
|
755 o++; |
|
756 i++; |
|
757 len--; |
|
758 } |
|
759 } else { |
|
760 g_assert_not_reached (); |
|
761 } |
|
762 } |
|
763 } |
|
764 |
|
765 static void |
|
766 gst_audio_resample_push_drain (GstAudioResample * resample) |
|
767 { |
|
768 GstBuffer *buf; |
|
769 GstBaseTransform *trans = GST_BASE_TRANSFORM (resample); |
|
770 GstFlowReturn res; |
|
771 gint outsize; |
|
772 guint out_len, out_processed; |
|
773 gint err; |
|
774 guint num, den, len; |
|
775 guint8 *outtmp = NULL; |
|
776 gboolean need_convert = FALSE; |
|
777 |
|
778 if (!resample->state) |
|
779 return; |
|
780 |
|
781 /* Don't drain samples if we were resetted. */ |
|
782 if (resample->next_ts == -1) |
|
783 return; |
|
784 |
|
785 need_convert = (resample->funcs->width != resample->width); |
|
786 |
|
787 resample->funcs->get_ratio (resample->state, &num, &den); |
|
788 |
|
789 out_len = resample->funcs->get_input_latency (resample->state); |
|
790 out_len = out_processed = (out_len * den + num - 1) / num; |
|
791 outsize = (resample->width / 8) * out_len * resample->channels; |
|
792 |
|
793 if (need_convert) { |
|
794 guint outsize_tmp = |
|
795 (resample->funcs->width / 8) * out_len * resample->channels; |
|
796 if (outsize_tmp <= resample->tmp_out_size) { |
|
797 outtmp = resample->tmp_out; |
|
798 } else { |
|
799 resample->tmp_out_size = outsize_tmp; |
|
800 resample->tmp_out = outtmp = g_realloc (resample->tmp_out, outsize_tmp); |
|
801 } |
|
802 } |
|
803 |
|
804 res = |
|
805 gst_pad_alloc_buffer_and_set_caps (trans->srcpad, GST_BUFFER_OFFSET_NONE, |
|
806 outsize, GST_PAD_CAPS (trans->srcpad), &buf); |
|
807 |
|
808 if (G_UNLIKELY (res != GST_FLOW_OK)) { |
|
809 GST_WARNING_OBJECT (resample, "failed allocating buffer of %d bytes", |
|
810 outsize); |
|
811 return; |
|
812 } |
|
813 |
|
814 len = resample->funcs->get_input_latency (resample->state); |
|
815 |
|
816 err = |
|
817 resample->funcs->process (resample->state, |
|
818 NULL, &len, (need_convert) ? outtmp : GST_BUFFER_DATA (buf), |
|
819 &out_processed); |
|
820 |
|
821 if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) { |
|
822 GST_WARNING_OBJECT (resample, "Failed to process drain: %s", |
|
823 resample->funcs->strerror (err)); |
|
824 gst_buffer_unref (buf); |
|
825 return; |
|
826 } |
|
827 |
|
828 if (G_UNLIKELY (out_processed == 0)) { |
|
829 GST_WARNING_OBJECT (resample, "Failed to get drain, dropping buffer"); |
|
830 gst_buffer_unref (buf); |
|
831 return; |
|
832 } |
|
833 |
|
834 /* If we wrote more than allocated something is really wrong now |
|
835 * and we should better abort immediately */ |
|
836 g_assert (out_len >= out_processed); |
|
837 |
|
838 if (need_convert) |
|
839 gst_audio_resample_convert_buffer (resample, outtmp, GST_BUFFER_DATA (buf), |
|
840 out_processed, TRUE); |
|
841 |
|
842 GST_BUFFER_DURATION (buf) = |
|
843 GST_FRAMES_TO_CLOCK_TIME (out_processed, resample->outrate); |
|
844 GST_BUFFER_SIZE (buf) = |
|
845 out_processed * resample->channels * (resample->width / 8); |
|
846 |
|
847 if (GST_CLOCK_TIME_IS_VALID (resample->next_ts)) { |
|
848 GST_BUFFER_OFFSET (buf) = resample->next_offset; |
|
849 GST_BUFFER_OFFSET_END (buf) = resample->next_offset + out_processed; |
|
850 GST_BUFFER_TIMESTAMP (buf) = resample->next_ts; |
|
851 |
|
852 resample->next_ts += GST_BUFFER_DURATION (buf); |
|
853 resample->next_offset += out_processed; |
|
854 } |
|
855 |
|
856 GST_LOG_OBJECT (resample, |
|
857 "Pushing drain buffer of %u bytes with timestamp %" GST_TIME_FORMAT |
|
858 " duration %" GST_TIME_FORMAT " offset %" G_GUINT64_FORMAT " offset_end %" |
|
859 G_GUINT64_FORMAT, GST_BUFFER_SIZE (buf), |
|
860 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), |
|
861 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf), |
|
862 GST_BUFFER_OFFSET_END (buf)); |
|
863 |
|
864 res = gst_pad_push (trans->srcpad, buf); |
|
865 |
|
866 if (G_UNLIKELY (res != GST_FLOW_OK)) |
|
867 GST_WARNING_OBJECT (resample, "Failed to push drain: %s", |
|
868 gst_flow_get_name (res)); |
|
869 |
|
870 return; |
|
871 } |
|
872 |
|
873 static gboolean |
|
874 gst_audio_resample_event (GstBaseTransform * base, GstEvent * event) |
|
875 { |
|
876 GstAudioResample *resample = GST_AUDIO_RESAMPLE (base); |
|
877 |
|
878 switch (GST_EVENT_TYPE (event)) { |
|
879 case GST_EVENT_FLUSH_STOP: |
|
880 gst_audio_resample_reset_state (resample); |
|
881 resample->next_offset = -1; |
|
882 resample->next_ts = -1; |
|
883 resample->next_upstream_ts = -1; |
|
884 break; |
|
885 case GST_EVENT_NEWSEGMENT: |
|
886 gst_audio_resample_push_drain (resample); |
|
887 gst_audio_resample_reset_state (resample); |
|
888 resample->next_offset = -1; |
|
889 resample->next_ts = -1; |
|
890 resample->next_upstream_ts = -1; |
|
891 break; |
|
892 case GST_EVENT_EOS: |
|
893 gst_audio_resample_push_drain (resample); |
|
894 gst_audio_resample_reset_state (resample); |
|
895 break; |
|
896 default: |
|
897 break; |
|
898 } |
|
899 |
|
900 return parent_class->event (base, event); |
|
901 } |
|
902 |
|
903 static gboolean |
|
904 gst_audio_resample_check_discont (GstAudioResample * resample, |
|
905 GstClockTime timestamp) |
587 GstClockTime timestamp) |
906 { |
588 { |
907 if (timestamp != GST_CLOCK_TIME_NONE && |
589 if (timestamp != GST_CLOCK_TIME_NONE && |
908 resample->next_upstream_ts != GST_CLOCK_TIME_NONE && |
590 audioresample->prev_ts != GST_CLOCK_TIME_NONE && |
909 timestamp != resample->next_upstream_ts) { |
591 audioresample->prev_duration != GST_CLOCK_TIME_NONE && |
|
592 timestamp != audioresample->prev_ts + audioresample->prev_duration) { |
910 /* Potentially a discontinuous buffer. However, it turns out that many |
593 /* Potentially a discontinuous buffer. However, it turns out that many |
911 * elements generate imperfect streams due to rounding errors, so we permit |
594 * elements generate imperfect streams due to rounding errors, so we permit |
912 * a small error (up to one sample) without triggering a filter |
595 * a small error (up to one sample) without triggering a filter |
913 * flush/restart (if triggered incorrectly, this will be audible) */ |
596 * flush/restart (if triggered incorrectly, this will be audible) */ |
914 GstClockTimeDiff diff = timestamp - resample->next_upstream_ts; |
597 GstClockTimeDiff diff = timestamp - |
915 |
598 (audioresample->prev_ts + audioresample->prev_duration); |
916 if (ABS (diff) > (GST_SECOND + resample->inrate - 1) / resample->inrate) { |
599 |
917 GST_WARNING_OBJECT (resample, |
600 if (ABS (diff) > GST_SECOND / audioresample->i_rate) { |
918 "encountered timestamp discontinuity of %s%" GST_TIME_FORMAT, |
601 GST_WARNING_OBJECT (audioresample, |
919 (diff < 0) ? "-" : "", GST_TIME_ARGS ((GstClockTime) ABS (diff))); |
602 "encountered timestamp discontinuity of %" G_GINT64_FORMAT, diff); |
920 return TRUE; |
603 return TRUE; |
921 } |
604 } |
922 } |
605 } |
923 |
606 |
924 return FALSE; |
607 return FALSE; |
925 } |
608 } |
926 |
609 |
927 static GstFlowReturn |
610 static GstFlowReturn |
928 gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf, |
611 audioresample_transform (GstBaseTransform * base, GstBuffer * inbuf, |
929 GstBuffer * outbuf) |
612 GstBuffer * outbuf) |
930 { |
613 { |
931 guint32 in_len, in_processed; |
614 GstAudioresample *audioresample; |
932 guint32 out_len, out_processed; |
615 ResampleState *r; |
933 gint err = RESAMPLER_ERR_SUCCESS; |
616 guchar *data, *datacopy; |
934 guint8 *in_tmp = NULL, *out_tmp = NULL; |
|
935 gboolean need_convert = (resample->funcs->width != resample->width); |
|
936 |
|
937 in_len = GST_BUFFER_SIZE (inbuf) / resample->channels; |
|
938 out_len = GST_BUFFER_SIZE (outbuf) / resample->channels; |
|
939 |
|
940 in_len /= (resample->width / 8); |
|
941 out_len /= (resample->width / 8); |
|
942 |
|
943 in_processed = in_len; |
|
944 out_processed = out_len; |
|
945 |
|
946 if (need_convert) { |
|
947 guint in_size_tmp = |
|
948 in_len * resample->channels * (resample->funcs->width / 8); |
|
949 guint out_size_tmp = |
|
950 out_len * resample->channels * (resample->funcs->width / 8); |
|
951 |
|
952 if (in_size_tmp <= resample->tmp_in_size) { |
|
953 in_tmp = resample->tmp_in; |
|
954 } else { |
|
955 resample->tmp_in = in_tmp = g_realloc (resample->tmp_in, in_size_tmp); |
|
956 resample->tmp_in_size = in_size_tmp; |
|
957 } |
|
958 |
|
959 gst_audio_resample_convert_buffer (resample, GST_BUFFER_DATA (inbuf), |
|
960 in_tmp, in_len, FALSE); |
|
961 |
|
962 if (out_size_tmp <= resample->tmp_out_size) { |
|
963 out_tmp = resample->tmp_out; |
|
964 } else { |
|
965 resample->tmp_out = out_tmp = g_realloc (resample->tmp_out, out_size_tmp); |
|
966 resample->tmp_out_size = out_size_tmp; |
|
967 } |
|
968 } |
|
969 |
|
970 if (need_convert) { |
|
971 err = resample->funcs->process (resample->state, |
|
972 in_tmp, &in_processed, out_tmp, &out_processed); |
|
973 } else { |
|
974 err = resample->funcs->process (resample->state, |
|
975 (const guint8 *) GST_BUFFER_DATA (inbuf), &in_processed, |
|
976 (guint8 *) GST_BUFFER_DATA (outbuf), &out_processed); |
|
977 } |
|
978 |
|
979 if (G_UNLIKELY (in_len != in_processed)) |
|
980 GST_WARNING_OBJECT (resample, "Converted %d of %d input samples", |
|
981 in_processed, in_len); |
|
982 |
|
983 if (out_len != out_processed) { |
|
984 if (out_processed == 0) { |
|
985 GST_DEBUG_OBJECT (resample, "Converted to 0 samples, buffer dropped"); |
|
986 |
|
987 return GST_BASE_TRANSFORM_FLOW_DROPPED; |
|
988 } |
|
989 |
|
990 /* If we wrote more than allocated something is really wrong now |
|
991 * and we should better abort immediately */ |
|
992 g_assert (out_len >= out_processed); |
|
993 } |
|
994 |
|
995 if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) { |
|
996 GST_ERROR_OBJECT (resample, "Failed to convert data: %s", |
|
997 resample->funcs->strerror (err)); |
|
998 return GST_FLOW_ERROR; |
|
999 } else { |
|
1000 |
|
1001 if (need_convert) |
|
1002 gst_audio_resample_convert_buffer (resample, out_tmp, |
|
1003 GST_BUFFER_DATA (outbuf), out_processed, TRUE); |
|
1004 |
|
1005 GST_BUFFER_DURATION (outbuf) = |
|
1006 GST_FRAMES_TO_CLOCK_TIME (out_processed, resample->outrate); |
|
1007 GST_BUFFER_SIZE (outbuf) = |
|
1008 out_processed * resample->channels * (resample->width / 8); |
|
1009 |
|
1010 if (GST_CLOCK_TIME_IS_VALID (resample->next_ts)) { |
|
1011 GST_BUFFER_TIMESTAMP (outbuf) = resample->next_ts; |
|
1012 GST_BUFFER_OFFSET (outbuf) = resample->next_offset; |
|
1013 GST_BUFFER_OFFSET_END (outbuf) = resample->next_offset + out_processed; |
|
1014 |
|
1015 resample->next_ts += GST_BUFFER_DURATION (outbuf); |
|
1016 resample->next_offset += out_processed; |
|
1017 } |
|
1018 |
|
1019 GST_LOG_OBJECT (resample, |
|
1020 "Converted to buffer of %u bytes with timestamp %" GST_TIME_FORMAT |
|
1021 ", duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT |
|
1022 ", offset_end %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (outbuf), |
|
1023 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), |
|
1024 GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), |
|
1025 GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf)); |
|
1026 |
|
1027 return GST_FLOW_OK; |
|
1028 } |
|
1029 } |
|
1030 |
|
1031 static GstFlowReturn |
|
1032 gst_audio_resample_transform (GstBaseTransform * base, GstBuffer * inbuf, |
|
1033 GstBuffer * outbuf) |
|
1034 { |
|
1035 GstAudioResample *resample = GST_AUDIO_RESAMPLE (base); |
|
1036 guint8 *data; |
|
1037 gulong size; |
617 gulong size; |
1038 GstClockTime timestamp; |
618 GstClockTime timestamp; |
1039 guint outsamples, insamples; |
619 |
1040 GstFlowReturn ret; |
620 audioresample = GST_AUDIORESAMPLE (base); |
1041 |
621 r = audioresample->resample; |
1042 if (resample->state == NULL) { |
|
1043 if (G_UNLIKELY (!(resample->state = |
|
1044 gst_audio_resample_init_state (resample, resample->width, |
|
1045 resample->channels, resample->inrate, resample->outrate, |
|
1046 resample->quality, resample->fp)))) |
|
1047 return GST_FLOW_ERROR; |
|
1048 |
|
1049 resample->funcs = |
|
1050 gst_audio_resample_get_funcs (resample->width, resample->fp); |
|
1051 } |
|
1052 |
622 |
1053 data = GST_BUFFER_DATA (inbuf); |
623 data = GST_BUFFER_DATA (inbuf); |
1054 size = GST_BUFFER_SIZE (inbuf); |
624 size = GST_BUFFER_SIZE (inbuf); |
1055 timestamp = GST_BUFFER_TIMESTAMP (inbuf); |
625 timestamp = GST_BUFFER_TIMESTAMP (inbuf); |
1056 |
626 |
1057 GST_LOG_OBJECT (resample, "transforming buffer of %ld bytes, ts %" |
627 GST_LOG_OBJECT (audioresample, "transforming buffer of %ld bytes, ts %" |
1058 GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %" |
628 GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %" |
1059 G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT, |
629 G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT, |
1060 size, GST_TIME_ARGS (timestamp), |
630 size, GST_TIME_ARGS (timestamp), |
1061 GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)), |
631 GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)), |
1062 GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf)); |
632 GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf)); |
1063 |
633 |
1064 /* check for timestamp discontinuities and flush/reset if needed */ |
634 /* check for timestamp discontinuities and flush/reset if needed */ |
1065 if (G_UNLIKELY (gst_audio_resample_check_discont (resample, timestamp) |
635 if (G_UNLIKELY (audioresample_check_discont (audioresample, timestamp))) { |
1066 || GST_BUFFER_IS_DISCONT (inbuf))) { |
|
1067 /* Flush internal samples */ |
636 /* Flush internal samples */ |
1068 gst_audio_resample_reset_state (resample); |
637 audioresample_pushthrough (audioresample); |
1069 /* Inform downstream element about discontinuity */ |
638 /* Inform downstream element about discontinuity */ |
1070 resample->need_discont = TRUE; |
639 audioresample->need_discont = TRUE; |
1071 /* We want to recalculate the timestamps */ |
640 /* We want to recalculate the offset */ |
1072 resample->next_ts = -1; |
641 audioresample->ts_offset = -1; |
1073 resample->next_upstream_ts = -1; |
642 } |
1074 resample->next_offset = -1; |
643 |
1075 } |
644 if (audioresample->ts_offset == -1) { |
1076 |
645 /* if we don't know the initial offset yet, calculate it based on the |
1077 insamples = GST_BUFFER_SIZE (inbuf) / resample->channels; |
646 * input timestamp. */ |
1078 insamples /= (resample->width / 8); |
647 if (GST_CLOCK_TIME_IS_VALID (timestamp)) { |
1079 |
648 GstClockTime stime; |
1080 outsamples = GST_BUFFER_SIZE (outbuf) / resample->channels; |
649 |
1081 outsamples /= (resample->width / 8); |
650 /* offset used to calculate the timestamps. We use the sample offset for |
1082 |
651 * this to make it more accurate. We want the first buffer to have the |
1083 if (GST_CLOCK_TIME_IS_VALID (timestamp) |
652 * same timestamp as the incoming timestamp. */ |
1084 && !GST_CLOCK_TIME_IS_VALID (resample->next_ts)) { |
653 audioresample->next_ts = timestamp; |
1085 resample->next_ts = timestamp; |
654 audioresample->ts_offset = |
1086 resample->next_offset = |
655 gst_util_uint64_scale_int (timestamp, r->o_rate, GST_SECOND); |
1087 GST_CLOCK_TIME_TO_FRAMES (timestamp, resample->outrate); |
656 /* offset used to set as the buffer offset, this offset is always |
1088 } |
657 * relative to the stream time, note that timestamp is not... */ |
1089 |
658 stime = (timestamp - base->segment.start) + base->segment.time; |
1090 if (G_UNLIKELY (resample->need_discont)) { |
659 audioresample->offset = |
1091 GST_DEBUG_OBJECT (resample, "marking this buffer with the DISCONT flag"); |
660 gst_util_uint64_scale_int (stime, r->o_rate, GST_SECOND); |
1092 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); |
661 } |
1093 resample->need_discont = FALSE; |
662 } |
1094 } |
663 audioresample->prev_ts = timestamp; |
1095 |
664 audioresample->prev_duration = GST_BUFFER_DURATION (inbuf); |
1096 ret = gst_audio_resample_process (resample, inbuf, outbuf); |
665 |
1097 if (G_UNLIKELY (ret != GST_FLOW_OK)) |
666 /* need to memdup, resample takes ownership. */ |
1098 return ret; |
667 datacopy = g_memdup (data, size); |
1099 |
668 resample_add_input_data (r, datacopy, size, g_free, datacopy); |
1100 if (GST_CLOCK_TIME_IS_VALID (timestamp) |
669 |
1101 && !GST_CLOCK_TIME_IS_VALID (resample->next_upstream_ts)) |
670 return audioresample_do_output (audioresample, outbuf); |
1102 resample->next_upstream_ts = timestamp; |
671 } |
1103 |
672 |
1104 if (GST_CLOCK_TIME_IS_VALID (resample->next_upstream_ts)) |
673 /* push remaining data in the buffers out */ |
1105 resample->next_upstream_ts += |
674 static GstFlowReturn |
1106 GST_FRAMES_TO_CLOCK_TIME (insamples, resample->inrate); |
675 audioresample_pushthrough (GstAudioresample * audioresample) |
1107 |
676 { |
1108 return GST_FLOW_OK; |
677 int outsize; |
1109 } |
678 ResampleState *r; |
1110 |
679 GstBuffer *outbuf; |
1111 static gboolean |
680 GstFlowReturn res = GST_FLOW_OK; |
1112 gst_audio_resample_query (GstPad * pad, GstQuery * query) |
681 GstBaseTransform *trans; |
1113 { |
682 |
1114 GstAudioResample *resample = GST_AUDIO_RESAMPLE (gst_pad_get_parent (pad)); |
683 r = audioresample->resample; |
1115 GstBaseTransform *trans = GST_BASE_TRANSFORM (resample); |
684 |
|
685 outsize = resample_get_output_size (r); |
|
686 if (outsize == 0) { |
|
687 GST_DEBUG_OBJECT (audioresample, "no internal buffers needing flush"); |
|
688 goto done; |
|
689 } |
|
690 |
|
691 trans = GST_BASE_TRANSFORM (audioresample); |
|
692 |
|
693 res = gst_pad_alloc_buffer (trans->srcpad, GST_BUFFER_OFFSET_NONE, outsize, |
|
694 GST_PAD_CAPS (trans->srcpad), &outbuf); |
|
695 if (G_UNLIKELY (res != GST_FLOW_OK)) { |
|
696 GST_WARNING_OBJECT (audioresample, "failed allocating buffer of %d bytes", |
|
697 outsize); |
|
698 goto done; |
|
699 } |
|
700 |
|
701 res = audioresample_do_output (audioresample, outbuf); |
|
702 if (G_UNLIKELY (res != GST_FLOW_OK)) |
|
703 goto done; |
|
704 |
|
705 res = gst_pad_push (trans->srcpad, outbuf); |
|
706 |
|
707 done: |
|
708 return res; |
|
709 } |
|
710 |
|
711 static gboolean |
|
712 audioresample_query (GstPad * pad, GstQuery * query) |
|
713 { |
|
714 GstAudioresample *audioresample = |
|
715 GST_AUDIORESAMPLE (gst_pad_get_parent (pad)); |
|
716 GstBaseTransform *trans = GST_BASE_TRANSFORM (audioresample); |
1116 gboolean res = TRUE; |
717 gboolean res = TRUE; |
1117 |
718 |
1118 switch (GST_QUERY_TYPE (query)) { |
719 switch (GST_QUERY_TYPE (query)) { |
1119 case GST_QUERY_LATENCY: |
720 case GST_QUERY_LATENCY: |
1120 { |
721 { |
1121 GstClockTime min, max; |
722 GstClockTime min, max; |
1122 gboolean live; |
723 gboolean live; |
1123 guint64 latency; |
724 guint64 latency; |
1124 GstPad *peer; |
725 GstPad *peer; |
1125 gint rate = resample->inrate; |
726 gint rate = audioresample->i_rate; |
1126 gint resampler_latency; |
727 gint resampler_latency = audioresample->filter_length / 2; |
1127 |
|
1128 if (resample->state) |
|
1129 resampler_latency = |
|
1130 resample->funcs->get_input_latency (resample->state); |
|
1131 else |
|
1132 resampler_latency = 0; |
|
1133 |
728 |
1134 if (gst_base_transform_is_passthrough (trans)) |
729 if (gst_base_transform_is_passthrough (trans)) |
1135 resampler_latency = 0; |
730 resampler_latency = 0; |
1136 |
731 |
1137 if ((peer = gst_pad_get_peer (trans->sinkpad))) { |
732 if ((peer = gst_pad_get_peer (trans->sinkpad))) { |
1138 if ((res = gst_pad_query (peer, query))) { |
733 if ((res = gst_pad_query (peer, query))) { |
1139 gst_query_parse_latency (query, &live, &min, &max); |
734 gst_query_parse_latency (query, &live, &min, &max); |
1140 |
735 |
1141 GST_DEBUG_OBJECT (resample, "Peer latency: min %" |
736 GST_DEBUG ("Peer latency: min %" |
1142 GST_TIME_FORMAT " max %" GST_TIME_FORMAT, |
737 GST_TIME_FORMAT " max %" GST_TIME_FORMAT, |
1143 GST_TIME_ARGS (min), GST_TIME_ARGS (max)); |
738 GST_TIME_ARGS (min), GST_TIME_ARGS (max)); |
1144 |
739 |
1145 /* add our own latency */ |
740 /* add our own latency */ |
1146 if (rate != 0 && resampler_latency != 0) |
741 if (rate != 0 && resampler_latency != 0) |
1147 latency = |
742 latency = |
1148 gst_util_uint64_scale (resampler_latency, GST_SECOND, rate); |
743 gst_util_uint64_scale (resampler_latency, GST_SECOND, rate); |
1149 else |
744 else |
1150 latency = 0; |
745 latency = 0; |
1151 |
746 |
1152 GST_DEBUG_OBJECT (resample, "Our latency: %" GST_TIME_FORMAT, |
747 GST_DEBUG ("Our latency: %" GST_TIME_FORMAT, GST_TIME_ARGS (latency)); |
1153 GST_TIME_ARGS (latency)); |
|
1154 |
748 |
1155 min += latency; |
749 min += latency; |
1156 if (max != GST_CLOCK_TIME_NONE) |
750 if (max != GST_CLOCK_TIME_NONE) |
1157 max += latency; |
751 max += latency; |
1158 |
752 |
1159 GST_DEBUG_OBJECT (resample, "Calculated total latency : min %" |
753 GST_DEBUG ("Calculated total latency : min %" |
1160 GST_TIME_FORMAT " max %" GST_TIME_FORMAT, |
754 GST_TIME_FORMAT " max %" GST_TIME_FORMAT, |
1161 GST_TIME_ARGS (min), GST_TIME_ARGS (max)); |
755 GST_TIME_ARGS (min), GST_TIME_ARGS (max)); |
1162 |
756 |
1163 gst_query_set_latency (query, live, min, max); |
757 gst_query_set_latency (query, live, min, max); |
1164 } |
758 } |
1168 } |
762 } |
1169 default: |
763 default: |
1170 res = gst_pad_query_default (pad, query); |
764 res = gst_pad_query_default (pad, query); |
1171 break; |
765 break; |
1172 } |
766 } |
1173 gst_object_unref (resample); |
767 gst_object_unref (audioresample); |
1174 return res; |
768 return res; |
1175 } |
769 } |
1176 |
770 |
1177 static const GstQueryType * |
771 static const GstQueryType * |
1178 gst_audio_resample_query_type (GstPad * pad) |
772 audioresample_query_type (GstPad * pad) |
1179 { |
773 { |
1180 static const GstQueryType types[] = { |
774 static const GstQueryType types[] = { |
1181 GST_QUERY_LATENCY, |
775 GST_QUERY_LATENCY, |
1182 0 |
776 0 |
1183 }; |
777 }; |
1184 |
778 |
1185 return types; |
779 return types; |
1186 } |
780 } |
1187 |
781 |
1188 static void |
782 static void |
1189 gst_audio_resample_set_property (GObject * object, guint prop_id, |
783 gst_audioresample_set_property (GObject * object, guint prop_id, |
1190 const GValue * value, GParamSpec * pspec) |
784 const GValue * value, GParamSpec * pspec) |
1191 { |
785 { |
1192 GstAudioResample *resample; |
786 GstAudioresample *audioresample; |
1193 |
787 |
1194 resample = GST_AUDIO_RESAMPLE (object); |
788 audioresample = GST_AUDIORESAMPLE (object); |
1195 |
789 |
1196 switch (prop_id) { |
790 switch (prop_id) { |
1197 case PROP_QUALITY: |
791 case PROP_FILTERLEN: |
1198 GST_BASE_TRANSFORM_LOCK (resample); |
792 audioresample->filter_length = g_value_get_int (value); |
1199 resample->quality = g_value_get_int (value); |
793 GST_DEBUG_OBJECT (GST_ELEMENT (audioresample), "new filter length %d", |
1200 GST_DEBUG_OBJECT (resample, "new quality %d", resample->quality); |
794 audioresample->filter_length); |
1201 |
795 if (audioresample->resample) { |
1202 gst_audio_resample_update_state (resample, resample->width, |
796 resample_set_filter_length (audioresample->resample, |
1203 resample->channels, resample->inrate, resample->outrate, |
797 audioresample->filter_length); |
1204 resample->quality, resample->fp); |
798 gst_element_post_message (GST_ELEMENT (audioresample), |
1205 GST_BASE_TRANSFORM_UNLOCK (resample); |
799 gst_message_new_latency (GST_OBJECT (audioresample))); |
1206 break; |
800 } |
1207 case PROP_FILTER_LENGTH:{ |
801 break; |
1208 gint filter_length = g_value_get_int (value); |
|
1209 |
|
1210 GST_BASE_TRANSFORM_LOCK (resample); |
|
1211 if (filter_length <= 8) |
|
1212 resample->quality = 0; |
|
1213 else if (filter_length <= 16) |
|
1214 resample->quality = 1; |
|
1215 else if (filter_length <= 32) |
|
1216 resample->quality = 2; |
|
1217 else if (filter_length <= 48) |
|
1218 resample->quality = 3; |
|
1219 else if (filter_length <= 64) |
|
1220 resample->quality = 4; |
|
1221 else if (filter_length <= 80) |
|
1222 resample->quality = 5; |
|
1223 else if (filter_length <= 96) |
|
1224 resample->quality = 6; |
|
1225 else if (filter_length <= 128) |
|
1226 resample->quality = 7; |
|
1227 else if (filter_length <= 160) |
|
1228 resample->quality = 8; |
|
1229 else if (filter_length <= 192) |
|
1230 resample->quality = 9; |
|
1231 else |
|
1232 resample->quality = 10; |
|
1233 |
|
1234 GST_DEBUG_OBJECT (resample, "new quality %d", resample->quality); |
|
1235 |
|
1236 gst_audio_resample_update_state (resample, resample->width, |
|
1237 resample->channels, resample->inrate, resample->outrate, |
|
1238 resample->quality, resample->fp); |
|
1239 GST_BASE_TRANSFORM_UNLOCK (resample); |
|
1240 break; |
|
1241 } |
|
1242 default: |
802 default: |
1243 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
803 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1244 break; |
804 break; |
1245 } |
805 } |
1246 } |
806 } |
1247 |
807 |
1248 static void |
808 static void |
1249 gst_audio_resample_get_property (GObject * object, guint prop_id, |
809 gst_audioresample_get_property (GObject * object, guint prop_id, |
1250 GValue * value, GParamSpec * pspec) |
810 GValue * value, GParamSpec * pspec) |
1251 { |
811 { |
1252 GstAudioResample *resample; |
812 GstAudioresample *audioresample; |
1253 |
813 |
1254 resample = GST_AUDIO_RESAMPLE (object); |
814 audioresample = GST_AUDIORESAMPLE (object); |
1255 |
815 |
1256 switch (prop_id) { |
816 switch (prop_id) { |
1257 case PROP_QUALITY: |
817 case PROP_FILTERLEN: |
1258 g_value_set_int (value, resample->quality); |
818 g_value_set_int (value, audioresample->filter_length); |
1259 break; |
|
1260 case PROP_FILTER_LENGTH: |
|
1261 switch (resample->quality) { |
|
1262 case 0: |
|
1263 g_value_set_int (value, 8); |
|
1264 break; |
|
1265 case 1: |
|
1266 g_value_set_int (value, 16); |
|
1267 break; |
|
1268 case 2: |
|
1269 g_value_set_int (value, 32); |
|
1270 break; |
|
1271 case 3: |
|
1272 g_value_set_int (value, 48); |
|
1273 break; |
|
1274 case 4: |
|
1275 g_value_set_int (value, 64); |
|
1276 break; |
|
1277 case 5: |
|
1278 g_value_set_int (value, 80); |
|
1279 break; |
|
1280 case 6: |
|
1281 g_value_set_int (value, 96); |
|
1282 break; |
|
1283 case 7: |
|
1284 g_value_set_int (value, 128); |
|
1285 break; |
|
1286 case 8: |
|
1287 g_value_set_int (value, 160); |
|
1288 break; |
|
1289 case 9: |
|
1290 g_value_set_int (value, 192); |
|
1291 break; |
|
1292 case 10: |
|
1293 g_value_set_int (value, 256); |
|
1294 break; |
|
1295 } |
|
1296 break; |
819 break; |
1297 default: |
820 default: |
1298 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
821 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1299 break; |
822 break; |
1300 } |
823 } |
1301 } |
824 } |
1302 |
825 |
1303 #if defined AUDIORESAMPLE_FORMAT_AUTO |
|
1304 #define BENCHMARK_SIZE 512 |
|
1305 |
|
1306 static gboolean |
|
1307 _benchmark_int_float (SpeexResamplerState * st) |
|
1308 { |
|
1309 gint16 in[BENCHMARK_SIZE] = { 0, }, out[BENCHMARK_SIZE / 2]; |
|
1310 gfloat in_tmp[BENCHMARK_SIZE], out_tmp[BENCHMARK_SIZE / 2]; |
|
1311 gint i; |
|
1312 guint32 inlen = BENCHMARK_SIZE, outlen = BENCHMARK_SIZE / 2; |
|
1313 |
|
1314 for (i = 0; i < BENCHMARK_SIZE; i++) { |
|
1315 gfloat tmp = in[i]; |
|
1316 in_tmp[i] = tmp / G_MAXINT16; |
|
1317 } |
|
1318 |
|
1319 resample_float_resampler_process_interleaved_float (st, |
|
1320 (const guint8 *) in_tmp, &inlen, (guint8 *) out_tmp, &outlen); |
|
1321 |
|
1322 if (outlen == 0) { |
|
1323 GST_ERROR ("Failed to use float resampler"); |
|
1324 return FALSE; |
|
1325 } |
|
1326 |
|
1327 for (i = 0; i < outlen; i++) { |
|
1328 gfloat tmp = out_tmp[i]; |
|
1329 out[i] = CLAMP (tmp * G_MAXINT16 + 0.5, G_MININT16, G_MAXINT16); |
|
1330 } |
|
1331 |
|
1332 return TRUE; |
|
1333 } |
|
1334 |
|
1335 static gboolean |
|
1336 _benchmark_int_int (SpeexResamplerState * st) |
|
1337 { |
|
1338 gint16 in[BENCHMARK_SIZE] = { 0, }, out[BENCHMARK_SIZE / 2]; |
|
1339 guint32 inlen = BENCHMARK_SIZE, outlen = BENCHMARK_SIZE / 2; |
|
1340 |
|
1341 resample_int_resampler_process_interleaved_int (st, (const guint8 *) in, |
|
1342 &inlen, (guint8 *) out, &outlen); |
|
1343 |
|
1344 if (outlen == 0) { |
|
1345 GST_ERROR ("Failed to use int resampler"); |
|
1346 return FALSE; |
|
1347 } |
|
1348 |
|
1349 return TRUE; |
|
1350 } |
|
1351 |
|
1352 static gboolean |
|
1353 _benchmark_integer_resampling (void) |
|
1354 { |
|
1355 OilProfile a, b; |
|
1356 gdouble av, bv; |
|
1357 SpeexResamplerState *sta, *stb; |
|
1358 int i; |
|
1359 |
|
1360 oil_profile_init (&a); |
|
1361 oil_profile_init (&b); |
|
1362 |
|
1363 sta = resample_float_resampler_init (1, 48000, 24000, 4, NULL); |
|
1364 if (sta == NULL) { |
|
1365 GST_ERROR ("Failed to create float resampler state"); |
|
1366 return FALSE; |
|
1367 } |
|
1368 |
|
1369 stb = resample_int_resampler_init (1, 48000, 24000, 4, NULL); |
|
1370 if (stb == NULL) { |
|
1371 resample_float_resampler_destroy (sta); |
|
1372 GST_ERROR ("Failed to create int resampler state"); |
|
1373 return FALSE; |
|
1374 } |
|
1375 |
|
1376 /* Benchmark */ |
|
1377 for (i = 0; i < 10; i++) { |
|
1378 oil_profile_start (&a); |
|
1379 if (!_benchmark_int_float (sta)) |
|
1380 goto error; |
|
1381 oil_profile_stop (&a); |
|
1382 } |
|
1383 |
|
1384 /* Benchmark */ |
|
1385 for (i = 0; i < 10; i++) { |
|
1386 oil_profile_start (&b); |
|
1387 if (!_benchmark_int_int (stb)) |
|
1388 goto error; |
|
1389 oil_profile_stop (&b); |
|
1390 } |
|
1391 |
|
1392 /* Handle results */ |
|
1393 oil_profile_get_ave_std (&a, &av, NULL); |
|
1394 oil_profile_get_ave_std (&b, &bv, NULL); |
|
1395 |
|
1396 /* Remember benchmark result in global variable */ |
|
1397 gst_audio_resample_use_int = (av > bv); |
|
1398 resample_float_resampler_destroy (sta); |
|
1399 resample_int_resampler_destroy (stb); |
|
1400 |
|
1401 if (av > bv) |
|
1402 GST_INFO ("Using integer resampler if appropiate: %lf < %lf", bv, av); |
|
1403 else |
|
1404 GST_INFO ("Using float resampler for everything: %lf <= %lf", av, bv); |
|
1405 |
|
1406 return TRUE; |
|
1407 |
|
1408 error: |
|
1409 resample_float_resampler_destroy (sta); |
|
1410 resample_int_resampler_destroy (stb); |
|
1411 |
|
1412 return FALSE; |
|
1413 } |
|
1414 #endif |
|
1415 |
826 |
1416 static gboolean |
827 static gboolean |
1417 plugin_init (GstPlugin * plugin) |
828 plugin_init (GstPlugin * plugin) |
1418 { |
829 { |
1419 GST_DEBUG_CATEGORY_INIT (audio_resample_debug, "audioresample", 0, |
830 resample_init (); |
1420 "audio resampling element"); |
|
1421 #if defined AUDIORESAMPLE_FORMAT_AUTO |
|
1422 oil_init (); |
|
1423 |
|
1424 if (!_benchmark_integer_resampling ()) |
|
1425 return FALSE; |
|
1426 #endif |
|
1427 |
831 |
1428 if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY, |
832 if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY, |
1429 GST_TYPE_AUDIO_RESAMPLE)) { |
833 GST_TYPE_AUDIORESAMPLE)) { |
1430 return FALSE; |
834 return FALSE; |
1431 } |
835 } |
1432 |
836 |
1433 return TRUE; |
837 return TRUE; |
1434 } |
838 } |