Coverage Report

Created: 2023-11-19 06:24

/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