/src/gpac/src/filters/resample_audio.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2018-2023 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / audio resample filter |
9 | | * |
10 | | * GPAC is free software; you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2, or (at your option) |
13 | | * any later version. |
14 | | * |
15 | | * GPAC is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; see the file COPYING. If not, write to |
22 | | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | | * |
24 | | */ |
25 | | |
26 | | #include <gpac/avparse.h> |
27 | | #include <gpac/constants.h> |
28 | | #include <gpac/filters.h> |
29 | | #include <gpac/internal/compositor_dev.h> |
30 | | |
31 | | #ifndef GPAC_DISABLE_RESAMPLE |
32 | | |
33 | | typedef struct |
34 | | { |
35 | | //opts |
36 | | u32 och, osr, osfmt; |
37 | | |
38 | | //internal |
39 | | GF_FilterPid *ipid, *opid; |
40 | | GF_AudioMixer *mixer; |
41 | | Bool cfg_forced; |
42 | | //output config |
43 | | u32 freq, nb_ch, afmt; |
44 | | u64 ch_cfg; |
45 | | u64 out_cts_plus_one; |
46 | | char *olayout; |
47 | | |
48 | | //source is planar |
49 | | Bool src_is_planar; |
50 | | GF_AudioInterface input_ai; |
51 | | Bool passthrough; |
52 | | u32 timescale; |
53 | | const char *data; |
54 | | u32 size, bytes_consumed; |
55 | | Fixed speed; |
56 | | GF_FilterPacket *in_pck; |
57 | | Bool cfg_changed; |
58 | | } GF_ResampleCtx; |
59 | | |
60 | | |
61 | | static u8 *resample_fetch_frame(void *callback, u32 *size, u32 *planar_stride, u32 audio_delay_ms) |
62 | 0 | { |
63 | 0 | u32 sample_offset; |
64 | 0 | GF_ResampleCtx *ctx = (GF_ResampleCtx *) callback; |
65 | 0 | if (!ctx->data) { |
66 | | //fetch data if none present (we may have drop the previous frame while mixing) |
67 | 0 | assert(!ctx->in_pck); |
68 | 0 | ctx->in_pck = gf_filter_pid_get_packet(ctx->ipid); |
69 | 0 | if (!ctx->in_pck) { |
70 | 0 | *size = 0; |
71 | 0 | return NULL; |
72 | 0 | } |
73 | 0 | ctx->data = gf_filter_pck_get_data(ctx->in_pck, &ctx->size); |
74 | | //note we only update CTS when no packet is present at the start of process() |
75 | |
|
76 | 0 | if (!ctx->data) { |
77 | 0 | *size = 0; |
78 | 0 | return NULL; |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | 0 | assert(ctx->data); |
83 | 0 | *size = ctx->size - ctx->bytes_consumed; |
84 | 0 | sample_offset = ctx->bytes_consumed; |
85 | | //planar mode, bytes consumed correspond to all channels, so move frame pointer |
86 | | //to first sample non consumed = bytes_consumed/nb_channels |
87 | 0 | if (ctx->src_is_planar) { |
88 | 0 | *planar_stride = ctx->size / ctx->input_ai.chan; |
89 | 0 | sample_offset /= ctx->input_ai.chan; |
90 | 0 | } |
91 | 0 | return (char*)ctx->data + sample_offset; |
92 | 0 | } |
93 | | |
94 | | static void resample_release_frame(void *callback, u32 nb_bytes) |
95 | 0 | { |
96 | 0 | GF_ResampleCtx *ctx = (GF_ResampleCtx *) callback; |
97 | 0 | ctx->bytes_consumed += nb_bytes; |
98 | 0 | assert(ctx->bytes_consumed<=ctx->size); |
99 | 0 | if (ctx->bytes_consumed==ctx->size) { |
100 | | //trash packet and get a new one |
101 | 0 | gf_filter_pid_drop_packet(ctx->ipid); |
102 | 0 | ctx->data = NULL; |
103 | 0 | ctx->in_pck = NULL; |
104 | 0 | ctx->size = ctx->bytes_consumed = 0; |
105 | | //do NOT fetch data until needed |
106 | 0 | } |
107 | 0 | } |
108 | | |
109 | | static Bool resample_get_config(struct _audiointerface *ai, Bool for_reconf) |
110 | 0 | { |
111 | 0 | GF_ResampleCtx *ctx = (GF_ResampleCtx *) ai->callback; |
112 | 0 | if (ctx->cfg_changed) { |
113 | 0 | ctx->cfg_changed = GF_FALSE; |
114 | 0 | return GF_FALSE; |
115 | 0 | } |
116 | 0 | return GF_TRUE; |
117 | 0 | } |
118 | | static Bool resample_is_muted(void *callback) |
119 | 0 | { |
120 | 0 | return GF_FALSE; |
121 | 0 | } |
122 | | static Fixed resample_get_speed(void *callback) |
123 | 0 | { |
124 | 0 | GF_ResampleCtx *ctx = (GF_ResampleCtx *) callback; |
125 | 0 | return ctx->speed; |
126 | 0 | } |
127 | | static Bool resample_get_channel_volume(void *callback, Fixed *vol) |
128 | 0 | { |
129 | 0 | u32 i; |
130 | 0 | for (i=0; i<GF_AUDIO_MIXER_MAX_CHANNELS; i++) vol[i] = FIX_ONE; |
131 | 0 | return GF_FALSE; |
132 | 0 | } |
133 | | |
134 | | static GF_Err resample_initialize(GF_Filter *filter) |
135 | 0 | { |
136 | 0 | GF_ResampleCtx *ctx = gf_filter_get_udta(filter); |
137 | 0 | ctx->mixer = gf_mixer_new(NULL); |
138 | 0 | if (!ctx->mixer) return GF_OUT_OF_MEM; |
139 | | |
140 | 0 | ctx->input_ai.callback = ctx; |
141 | 0 | ctx->input_ai.FetchFrame = resample_fetch_frame; |
142 | 0 | ctx->input_ai.ReleaseFrame = resample_release_frame; |
143 | 0 | ctx->input_ai.GetConfig = resample_get_config; |
144 | 0 | ctx->input_ai.IsMuted = resample_is_muted; |
145 | 0 | ctx->input_ai.GetSpeed = resample_get_speed; |
146 | 0 | ctx->input_ai.GetChannelVolume = resample_get_channel_volume; |
147 | 0 | ctx->speed = FIX_ONE; |
148 | 0 | return GF_OK; |
149 | 0 | } |
150 | | |
151 | | static void resample_finalize(GF_Filter *filter) |
152 | 0 | { |
153 | 0 | GF_ResampleCtx *ctx = gf_filter_get_udta(filter); |
154 | 0 | if (ctx->mixer) gf_mixer_del(ctx->mixer); |
155 | 0 | if (ctx->in_pck && ctx->ipid) gf_filter_pid_drop_packet(ctx->ipid); |
156 | 0 | } |
157 | | |
158 | | |
159 | | static GF_Err resample_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove) |
160 | 0 | { |
161 | 0 | const GF_PropertyValue *p; |
162 | 0 | u32 sr, nb_ch, afmt; |
163 | 0 | u64 ch_cfg; |
164 | 0 | GF_ResampleCtx *ctx = gf_filter_get_udta(filter); |
165 | 0 | if (is_remove) { |
166 | 0 | if (ctx->opid) { |
167 | 0 | gf_mixer_remove_input(ctx->mixer, &ctx->input_ai); |
168 | 0 | gf_filter_pid_remove(ctx->opid); |
169 | 0 | ctx->opid = NULL; |
170 | 0 | } |
171 | 0 | if (ctx->in_pck) gf_filter_pid_drop_packet(ctx->ipid); |
172 | 0 | ctx->in_pck = NULL; |
173 | 0 | return GF_OK; |
174 | 0 | } |
175 | 0 | if (! gf_filter_pid_check_caps(pid)) |
176 | 0 | return GF_NOT_SUPPORTED; |
177 | | |
178 | 0 | if (!ctx->opid) { |
179 | 0 | ctx->opid = gf_filter_pid_new(filter); |
180 | 0 | gf_filter_pid_set_max_buffer(ctx->opid, gf_filter_pid_get_max_buffer(pid) ); |
181 | 0 | } |
182 | 0 | if (!ctx->ipid) { |
183 | 0 | ctx->ipid = pid; |
184 | 0 | gf_mixer_add_input(ctx->mixer, &ctx->input_ai); |
185 | 0 | } |
186 | |
|
187 | 0 | sr = ctx->freq; |
188 | 0 | p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE); |
189 | 0 | if (p) sr = p->value.uint; |
190 | 0 | if (!sr) sr = 44100; |
191 | |
|
192 | 0 | nb_ch = ctx->nb_ch; |
193 | 0 | p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS); |
194 | 0 | if (p) nb_ch = p->value.uint; |
195 | 0 | if (!nb_ch) nb_ch = 1; |
196 | |
|
197 | 0 | afmt = ctx->afmt; |
198 | 0 | p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT); |
199 | 0 | if (p) afmt = p->value.uint; |
200 | |
|
201 | 0 | ch_cfg = ctx->ch_cfg; |
202 | 0 | p = gf_filter_pid_get_property(pid, GF_PROP_PID_CHANNEL_LAYOUT); |
203 | 0 | if (p) ch_cfg = p->value.longuint; |
204 | 0 | if (!ch_cfg) ch_cfg = (nb_ch==1) ? GF_AUDIO_CH_FRONT_CENTER : (GF_AUDIO_CH_FRONT_LEFT|GF_AUDIO_CH_FRONT_RIGHT); |
205 | |
|
206 | 0 | ctx->timescale = sr; |
207 | 0 | p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE); |
208 | 0 | if (p) ctx->timescale = p->value.uint; |
209 | | |
210 | | //initial config |
211 | 0 | if (!ctx->freq || !ctx->nb_ch || !ctx->afmt) { |
212 | 0 | GF_Err e; |
213 | 0 | ctx->afmt = ctx->osfmt ? ctx->osfmt : afmt; |
214 | 0 | ctx->freq = ctx->osr ? ctx->osr : sr; |
215 | 0 | ctx->nb_ch = ctx->och ? ctx->och : nb_ch; |
216 | |
|
217 | 0 | if (ctx->olayout) { |
218 | 0 | ch_cfg = gf_audio_fmt_get_layout_from_name(ctx->olayout); |
219 | 0 | if (!ch_cfg) { |
220 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[Resampler] Unrecognized CICP layout %s, will infer layout from channel numbers (%d)", ctx->olayout, ctx->nb_ch)); |
221 | 0 | } else { |
222 | 0 | ctx->nb_ch = nb_ch = gf_audio_fmt_get_num_channels_from_layout(ch_cfg); |
223 | 0 | } |
224 | 0 | } |
225 | 0 | ctx->ch_cfg = ch_cfg; |
226 | |
|
227 | 0 | if (ctx->nb_ch != nb_ch) { |
228 | | //TODO, find LFE and surround |
229 | 0 | u32 cicp = gf_audio_fmt_get_cicp_layout(ctx->nb_ch, 0, 0); |
230 | 0 | ctx->ch_cfg = gf_audio_fmt_get_layout_from_cicp(cicp); |
231 | 0 | } |
232 | |
|
233 | 0 | e = gf_mixer_set_config(ctx->mixer, ctx->freq, ctx->nb_ch, ctx->afmt, ctx->ch_cfg); |
234 | 0 | if (e) return e; |
235 | 0 | } |
236 | | //input reconfig |
237 | 0 | if ((sr != ctx->input_ai.samplerate) || (nb_ch != ctx->input_ai.chan) |
238 | 0 | || (afmt != ctx->input_ai.afmt) || (ch_cfg != ctx->input_ai.ch_layout) |
239 | 0 | || (ctx->src_is_planar != gf_audio_fmt_is_planar(afmt)) |
240 | 0 | ) { |
241 | 0 | ctx->input_ai.samplerate = sr; |
242 | 0 | ctx->input_ai.afmt = afmt; |
243 | 0 | ctx->input_ai.chan = nb_ch; |
244 | 0 | ctx->input_ai.ch_layout = ch_cfg; |
245 | 0 | ctx->src_is_planar = gf_audio_fmt_is_planar(afmt); |
246 | 0 | ctx->cfg_changed = GF_TRUE; |
247 | 0 | } |
248 | |
|
249 | 0 | ctx->passthrough = GF_FALSE; |
250 | 0 | gf_filter_pid_copy_properties(ctx->opid, ctx->ipid); |
251 | |
|
252 | 0 | if ((ctx->input_ai.samplerate==ctx->freq) && (ctx->input_ai.chan==ctx->nb_ch) && (ctx->input_ai.afmt==ctx->afmt) && (ctx->speed==FIX_ONE)) |
253 | 0 | ctx->passthrough = GF_TRUE; |
254 | |
|
255 | 0 | gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(ctx->freq)); |
256 | 0 | gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->freq)); |
257 | 0 | gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(ctx->afmt)); |
258 | 0 | gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(ctx->nb_ch)); |
259 | 0 | gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CHANNEL_LAYOUT, &PROP_LONGUINT(ctx->ch_cfg)); |
260 | 0 | return GF_OK; |
261 | 0 | } |
262 | | |
263 | | |
264 | | static GF_Err resample_process(GF_Filter *filter) |
265 | 0 | { |
266 | 0 | u8 *output; |
267 | 0 | u32 osize, written; |
268 | 0 | GF_FilterPacket *dstpck; |
269 | 0 | GF_ResampleCtx *ctx = gf_filter_get_udta(filter); |
270 | 0 | u32 bps, bytes_per_samp; |
271 | 0 | if (!ctx->ipid) return GF_OK; |
272 | | |
273 | 0 | bps = gf_audio_fmt_bit_depth(ctx->afmt); |
274 | 0 | bytes_per_samp = ctx->nb_ch * bps / 8; |
275 | |
|
276 | 0 | while (1) { |
277 | 0 | if (!ctx->in_pck) { |
278 | 0 | ctx->in_pck = gf_filter_pid_get_packet(ctx->ipid); |
279 | |
|
280 | 0 | if (!ctx->in_pck) { |
281 | 0 | if (gf_filter_pid_is_eos(ctx->ipid)) { |
282 | 0 | if (ctx->passthrough || ctx->input_ai.is_eos) { |
283 | 0 | if (ctx->opid) |
284 | 0 | gf_filter_pid_set_eos(ctx->opid); |
285 | 0 | return GF_EOS; |
286 | 0 | } |
287 | 0 | ctx->input_ai.is_eos = 1; |
288 | 0 | } else { |
289 | 0 | ctx->input_ai.is_eos = 0; |
290 | 0 | return GF_OK; |
291 | 0 | } |
292 | 0 | } else { |
293 | 0 | ctx->data = gf_filter_pck_get_data(ctx->in_pck, &ctx->size); |
294 | 0 | u64 cts = gf_timestamp_rescale(gf_filter_pck_get_cts(ctx->in_pck), FIX2INT(ctx->speed * ctx->timescale), ctx->freq); |
295 | 0 | if (!ctx->out_cts_plus_one) { |
296 | 0 | ctx->out_cts_plus_one = cts + 1; |
297 | 0 | } |
298 | | //if we drift by more than 200ms, resync to input cts |
299 | 0 | else { |
300 | 0 | s64 diff = cts; |
301 | 0 | diff -= ctx->out_cts_plus_one-1; |
302 | | //200ms max |
303 | 0 | if (ABS(diff) * 1000 > ctx->freq * 200) { |
304 | 0 | ctx->out_cts_plus_one = cts + 1; |
305 | 0 | } |
306 | 0 | } |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | 0 | if (ctx->passthrough) { |
311 | 0 | gf_filter_pck_forward(ctx->in_pck, ctx->opid); |
312 | 0 | gf_filter_pid_drop_packet(ctx->ipid); |
313 | 0 | ctx->in_pck = NULL; |
314 | 0 | continue; |
315 | 0 | } |
316 | | |
317 | 0 | if (ctx->in_pck) { |
318 | 0 | osize = ctx->size * ctx->nb_ch * bps; |
319 | 0 | osize /= ctx->input_ai.chan * gf_audio_fmt_bit_depth(ctx->input_ai.afmt); |
320 | | //output in higher samplerate, need more space for same samples |
321 | 0 | if (ctx->freq > ctx->input_ai.samplerate) { |
322 | 0 | osize *= ctx->freq; |
323 | 0 | osize /= ctx->input_ai.samplerate; |
324 | 0 | while (osize % bytes_per_samp) |
325 | 0 | osize++; |
326 | 0 | } |
327 | 0 | } else { |
328 | | //flush remaining samples from mixer, use 20 sample buffer |
329 | 0 | osize = 20*ctx->nb_ch * bps / 8; |
330 | 0 | } |
331 | |
|
332 | 0 | dstpck = gf_filter_pck_new_alloc(ctx->opid, osize, &output); |
333 | 0 | if (!dstpck) return GF_OUT_OF_MEM; |
334 | | |
335 | 0 | if (ctx->in_pck) |
336 | 0 | gf_filter_pck_merge_properties(ctx->in_pck, dstpck); |
337 | |
|
338 | 0 | written = gf_mixer_get_output(ctx->mixer, output, osize, 0); |
339 | 0 | if (!written) { |
340 | 0 | gf_filter_pck_discard(dstpck); |
341 | 0 | } else { |
342 | 0 | u32 dur = written / bytes_per_samp; |
343 | 0 | if (written != osize) { |
344 | 0 | gf_filter_pck_truncate(dstpck, written); |
345 | 0 | } |
346 | 0 | gf_filter_pck_set_dts(dstpck, ctx->out_cts_plus_one - 1); |
347 | 0 | gf_filter_pck_set_cts(dstpck, ctx->out_cts_plus_one - 1); |
348 | 0 | gf_filter_pck_set_duration(dstpck, dur); |
349 | 0 | gf_filter_pck_send(dstpck); |
350 | | |
351 | | //out_cts is in output time scale ( = freq), increase by the amount of bytes/bps |
352 | 0 | ctx->out_cts_plus_one += dur; |
353 | 0 | } |
354 | | |
355 | | //still some bytes to use from packet, do not discard |
356 | 0 | if (ctx->bytes_consumed<ctx->size) { |
357 | 0 | continue; |
358 | 0 | } |
359 | | //done with this packet |
360 | 0 | if (ctx->in_pck) { |
361 | 0 | ctx->in_pck = NULL; |
362 | 0 | gf_filter_pid_drop_packet(ctx->ipid); |
363 | 0 | } |
364 | 0 | } |
365 | 0 | return GF_OK; |
366 | 0 | } |
367 | | |
368 | | static GF_Err resample_reconfigure_output(GF_Filter *filter, GF_FilterPid *pid) |
369 | 0 | { |
370 | 0 | u32 sr, nb_ch, afmt; |
371 | 0 | u64 ch_cfg; |
372 | 0 | GF_Err e; |
373 | 0 | const GF_PropertyValue *p; |
374 | 0 | GF_ResampleCtx *ctx = gf_filter_get_udta(filter); |
375 | 0 | if (ctx->opid != pid) return GF_BAD_PARAM; |
376 | | |
377 | 0 | sr = ctx->freq; |
378 | 0 | p = gf_filter_pid_caps_query(pid, GF_PROP_PID_SAMPLE_RATE); |
379 | 0 | if (p) sr = p->value.uint; |
380 | |
|
381 | 0 | nb_ch = ctx->nb_ch; |
382 | 0 | p = gf_filter_pid_caps_query(pid, GF_PROP_PID_NUM_CHANNELS); |
383 | 0 | if (p) nb_ch = p->value.uint; |
384 | |
|
385 | 0 | afmt = ctx->afmt; |
386 | 0 | p = gf_filter_pid_caps_query(pid, GF_PROP_PID_AUDIO_FORMAT); |
387 | 0 | if (p) afmt = p->value.uint; |
388 | |
|
389 | 0 | ch_cfg = ctx->ch_cfg; |
390 | 0 | p = gf_filter_pid_caps_query(pid, GF_PROP_PID_CHANNEL_LAYOUT); |
391 | 0 | if (p) ch_cfg = p->value.longuint; |
392 | |
|
393 | 0 | p = gf_filter_pid_caps_query(pid, GF_PROP_PID_AUDIO_SPEED); |
394 | 0 | if (p) { |
395 | 0 | ctx->speed = FLT2FIX((Float) p->value.number); |
396 | 0 | if (ctx->speed<0) ctx->speed = -ctx->speed; |
397 | 0 | } else { |
398 | 0 | ctx->speed = FIX_ONE; |
399 | 0 | } |
400 | |
|
401 | 0 | if ((sr==ctx->freq) && (nb_ch==ctx->nb_ch) && (afmt==ctx->afmt) && (ch_cfg==ctx->ch_cfg) && (ctx->speed == FIX_ONE) ) return GF_OK; |
402 | | |
403 | 0 | ctx->afmt = afmt; |
404 | 0 | ctx->freq = sr; |
405 | 0 | ctx->nb_ch = nb_ch; |
406 | 0 | ctx->ch_cfg = ch_cfg; |
407 | |
|
408 | 0 | e = gf_mixer_set_config(ctx->mixer, ctx->freq, ctx->nb_ch, ctx->afmt, ctx->ch_cfg); |
409 | 0 | if (e) return e; |
410 | 0 | ctx->passthrough = GF_FALSE; |
411 | |
|
412 | 0 | if ((ctx->input_ai.samplerate==ctx->freq) && (ctx->input_ai.chan==ctx->nb_ch) && (ctx->input_ai.afmt==afmt) && (ctx->speed == FIX_ONE)) |
413 | 0 | ctx->passthrough = GF_TRUE; |
414 | |
|
415 | 0 | gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(sr)); |
416 | 0 | gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(sr)); |
417 | 0 | gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(afmt)); |
418 | 0 | gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(nb_ch)); |
419 | 0 | gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CHANNEL_LAYOUT, &PROP_LONGUINT(ch_cfg)); |
420 | |
|
421 | 0 | if (ctx->speed > FIX_ONE) { |
422 | 0 | GF_FilterEvent evt; |
423 | 0 | GF_FEVT_INIT(evt, GF_FEVT_BUFFER_REQ, ctx->ipid); |
424 | 0 | evt.buffer_req.max_buffer_us = FIX2INT( ctx->speed * 100000 ); |
425 | 0 | gf_filter_pid_send_event(ctx->ipid, &evt); |
426 | 0 | } |
427 | |
|
428 | 0 | return GF_OK; |
429 | 0 | } |
430 | | |
431 | | static Bool resample_process_event(GF_Filter *filter, const GF_FilterEvent *evt) |
432 | 0 | { |
433 | 0 | if (((evt->base.type==GF_FEVT_PLAY) || (evt->base.type==GF_FEVT_SET_SPEED) ) |
434 | 0 | && evt->play.speed |
435 | 0 | ) { |
436 | 0 | GF_ResampleCtx *ctx = gf_filter_get_udta(filter); |
437 | 0 | ctx->speed = FLT2FIX(evt->play.speed); |
438 | 0 | if (ctx->speed<0) ctx->speed = -ctx->speed; |
439 | |
|
440 | 0 | ctx->passthrough = GF_FALSE; |
441 | 0 | if (ctx->speed > FIX_ONE) { |
442 | 0 | GF_FilterEvent anevt; |
443 | 0 | GF_FEVT_INIT(anevt, GF_FEVT_BUFFER_REQ, ctx->ipid); |
444 | 0 | anevt.buffer_req.max_buffer_us = FIX2INT( ctx->speed * 100000 ); |
445 | 0 | gf_filter_pid_send_event(ctx->ipid, &anevt); |
446 | 0 | } |
447 | | //reset output ts |
448 | 0 | ctx->out_cts_plus_one = 0; |
449 | 0 | } |
450 | 0 | return GF_FALSE; |
451 | 0 | } |
452 | | |
453 | | static const GF_FilterCapability ResamplerCaps[] = |
454 | | { |
455 | | CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO), |
456 | | CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW), |
457 | | CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO), |
458 | | CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW), |
459 | | }; |
460 | | |
461 | | #define OFFS(_n) #_n, offsetof(GF_ResampleCtx, _n) |
462 | | static GF_FilterArgs ResamplerArgs[] = |
463 | | { |
464 | | { OFFS(och), "desired number of output audio channels (0 for auto)", GF_PROP_UINT, "0", NULL, 0}, |
465 | | { OFFS(osr), "desired sample rate of output audio (0 for auto)", GF_PROP_UINT, "0", NULL, 0}, |
466 | | { OFFS(osfmt), "desired sample format of output audio (`none` for auto)", GF_PROP_PCMFMT, "none", NULL, 0}, |
467 | | { OFFS(olayout), "desired CICP layout of output audio (null for auto)", GF_PROP_STRING, NULL, NULL, 0}, |
468 | | {0} |
469 | | }; |
470 | | |
471 | | GF_FilterRegister ResamplerRegister = { |
472 | | .name = "resample", |
473 | | GF_FS_SET_DESCRIPTION("Audio resampler") |
474 | | GF_FS_SET_HELP("This filter resamples raw audio to a target sample rate, number of channels or audio format.") |
475 | | .private_size = sizeof(GF_ResampleCtx), |
476 | | .initialize = resample_initialize, |
477 | | .finalize = resample_finalize, |
478 | | .args = ResamplerArgs, |
479 | | .flags = GF_FS_REG_ALLOW_CYCLIC, |
480 | | SETCAPS(ResamplerCaps), |
481 | | .configure_pid = resample_configure_pid, |
482 | | .process = resample_process, |
483 | | .reconfigure_output = resample_reconfigure_output, |
484 | | .process_event = resample_process_event, |
485 | | }; |
486 | | |
487 | | const char *gf_audio_fmt_cicp_all_names(); |
488 | | |
489 | | const GF_FilterRegister *resample_register(GF_FilterSession *session) |
490 | 0 | { |
491 | 0 | ResamplerArgs[3].min_max_enum = gf_audio_fmt_cicp_all_names(); |
492 | 0 | return &ResamplerRegister; |
493 | 0 | } |
494 | | #else |
495 | | const GF_FilterRegister *resample_register(GF_FilterSession *session) |
496 | | { |
497 | | return NULL; |
498 | | } |
499 | | #endif //#ifndef GPAC_DISABLE_RESAMPLE |
500 | | |