LCOV - code coverage report
Current view: top level - filters - resample_audio.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 202 224 90.2 %
Date: 2021-04-29 23:48:07 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2018-2021
       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_PLAYER
      32             : 
      33             : typedef struct
      34             : {
      35             :         //opts
      36             :         u32 och, osr, ofmt;
      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             :         //source is planar
      47             :         Bool src_is_planar;
      48             :         GF_AudioInterface input_ai;
      49             :         Bool passthrough;
      50             :         u32 timescale;
      51             :         const char *data;
      52             :         u32 size, bytes_consumed;
      53             :         Fixed speed;
      54             :         GF_FilterPacket *in_pck;
      55             :         Bool cfg_changed;
      56             : } GF_ResampleCtx;
      57             : 
      58             : 
      59        2800 : static u8 *resample_fetch_frame(void *callback, u32 *size, u32 *planar_stride, u32 audio_delay_ms)
      60             : {
      61             :         u32 sample_offset;
      62             :         GF_ResampleCtx *ctx = (GF_ResampleCtx *) callback;
      63        2800 :         if (!ctx->data) {
      64             :                 //fetch data if none present (we may have drop the previous frame while mixing)
      65             :                 assert(!ctx->in_pck);
      66          77 :                 ctx->in_pck = gf_filter_pid_get_packet(ctx->ipid);
      67          77 :                 if (!ctx->in_pck) {
      68          45 :                         *size = 0;
      69          45 :                         return NULL;
      70             :                 }
      71          32 :                 ctx->data = gf_filter_pck_get_data(ctx->in_pck, &ctx->size);
      72             :                 //note we only update CTS when no packet is present at the start of process()
      73             : 
      74          32 :                 if (!ctx->data) {
      75           0 :                         *size = 0;
      76           0 :                         return NULL;
      77             :                 }
      78             :         }
      79             : 
      80             :         assert(ctx->data);
      81        2755 :         *size = ctx->size - ctx->bytes_consumed;
      82        2755 :         sample_offset = ctx->bytes_consumed;
      83             :         //planar mode, bytes consumed correspond to all channels, so move frame pointer
      84             :         //to first sample non consumed = bytes_consumed/nb_channels
      85        2755 :         if (ctx->src_is_planar) {
      86         735 :                 *planar_stride = ctx->size / ctx->input_ai.chan;
      87         735 :                 sample_offset /= ctx->input_ai.chan;
      88             :         }
      89        2755 :         return (char*)ctx->data + sample_offset;
      90             : }
      91             : 
      92        2755 : static void resample_release_frame(void *callback, u32 nb_bytes)
      93             : {
      94             :         GF_ResampleCtx *ctx = (GF_ResampleCtx *) callback;
      95        2755 :         ctx->bytes_consumed += nb_bytes;
      96             :         assert(ctx->bytes_consumed<=ctx->size);
      97        2755 :         if (ctx->bytes_consumed==ctx->size) {
      98             :                 //trash packet and get a new one
      99        2753 :                 gf_filter_pid_drop_packet(ctx->ipid);
     100        2753 :                 ctx->data = NULL;
     101        2753 :                 ctx->in_pck = NULL;
     102        2753 :                 ctx->size = ctx->bytes_consumed = 0;
     103             :                 //do NOT fetch data until needed
     104             :         }
     105        2755 : }
     106             : 
     107        5318 : static Bool resample_get_config(struct _audiointerface *ai, Bool for_reconf)
     108             : {
     109        5318 :         GF_ResampleCtx *ctx = (GF_ResampleCtx *) ai->callback;
     110        5318 :         if (ctx->cfg_changed) {
     111          42 :                 ctx->cfg_changed = GF_FALSE;
     112          42 :                 return GF_FALSE;
     113             :         }
     114             :         return GF_TRUE;
     115             : }
     116        7788 : static Bool resample_is_muted(void *callback)
     117             : {
     118        7788 :         return GF_FALSE;
     119             : }
     120        5276 : static Fixed resample_get_speed(void *callback)
     121             : {
     122             :         GF_ResampleCtx *ctx = (GF_ResampleCtx *) callback;
     123        5276 :         return ctx->speed;
     124             : }
     125        5268 : static Bool resample_get_channel_volume(void *callback, Fixed *vol)
     126             : {
     127             :         u32 i;
     128        5268 :         for (i=0; i<GF_AUDIO_MIXER_MAX_CHANNELS; i++) vol[i] = FIX_ONE;
     129        5268 :         return GF_FALSE;
     130             : }
     131             : 
     132          43 : static GF_Err resample_initialize(GF_Filter *filter)
     133             : {
     134          43 :         GF_ResampleCtx *ctx = gf_filter_get_udta(filter);
     135          43 :         ctx->mixer = gf_mixer_new(NULL);
     136          43 :         if (!ctx->mixer) return GF_OUT_OF_MEM;
     137             : 
     138          43 :         ctx->input_ai.callback = ctx;
     139          43 :         ctx->input_ai.FetchFrame = resample_fetch_frame;
     140          43 :         ctx->input_ai.ReleaseFrame = resample_release_frame;
     141          43 :         ctx->input_ai.GetConfig = resample_get_config;
     142          43 :         ctx->input_ai.IsMuted = resample_is_muted;
     143          43 :         ctx->input_ai.GetSpeed = resample_get_speed;
     144          43 :         ctx->input_ai.GetChannelVolume = resample_get_channel_volume;
     145          43 :         ctx->speed = FIX_ONE;
     146          43 :         return GF_OK;
     147             : }
     148             : 
     149          43 : static void resample_finalize(GF_Filter *filter)
     150             : {
     151          43 :         GF_ResampleCtx *ctx = gf_filter_get_udta(filter);
     152          43 :         if (ctx->mixer) gf_mixer_del(ctx->mixer);
     153          43 :         if (ctx->in_pck && ctx->ipid) gf_filter_pid_drop_packet(ctx->ipid);
     154          43 : }
     155             : 
     156             : 
     157          46 : static GF_Err resample_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     158             : {
     159             :         const GF_PropertyValue *p;
     160             :         u32 sr, nb_ch, afmt;
     161             :         u64 ch_cfg;
     162          46 :         GF_ResampleCtx *ctx = gf_filter_get_udta(filter);
     163          46 :         if (is_remove) {
     164           0 :                 if (ctx->opid) {
     165           0 :                         gf_mixer_remove_input(ctx->mixer, &ctx->input_ai);
     166           0 :                         gf_filter_pid_remove(ctx->opid);
     167           0 :                         ctx->opid = NULL;
     168             :                 }
     169           0 :                 if (ctx->in_pck) gf_filter_pid_drop_packet(ctx->ipid);
     170           0 :                 ctx->in_pck = NULL;
     171           0 :                 return GF_OK;
     172             :         }
     173          46 :         if (! gf_filter_pid_check_caps(pid))
     174             :                 return GF_NOT_SUPPORTED;
     175             : 
     176          46 :         if (!ctx->opid) {
     177          43 :                 ctx->opid = gf_filter_pid_new(filter);
     178          43 :                 gf_filter_pid_set_max_buffer(ctx->opid, gf_filter_pid_get_max_buffer(pid) );
     179             :         }
     180          46 :         if (!ctx->ipid) {
     181          43 :                 ctx->ipid = pid;
     182          43 :                 gf_mixer_add_input(ctx->mixer, &ctx->input_ai);
     183             :         }
     184             : 
     185          46 :         sr = ctx->freq;
     186          46 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
     187          46 :         if (p) sr = p->value.uint;
     188          46 :         if (!sr) sr = 44100;
     189             : 
     190          46 :         nb_ch = ctx->nb_ch;
     191          46 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
     192          46 :         if (p) nb_ch = p->value.uint;
     193          46 :         if (!nb_ch) nb_ch = 1;
     194             : 
     195          46 :         afmt = ctx->afmt;
     196          46 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
     197          46 :         if (p) afmt = p->value.uint;
     198             : 
     199          46 :         ch_cfg = ctx->ch_cfg;
     200          46 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_CHANNEL_LAYOUT);
     201          46 :         if (p) ch_cfg = p->value.longuint;
     202          46 :         if (!ch_cfg) ch_cfg = (nb_ch==1) ? GF_AUDIO_CH_FRONT_CENTER : (GF_AUDIO_CH_FRONT_LEFT|GF_AUDIO_CH_FRONT_RIGHT);
     203             : 
     204          46 :         ctx->timescale = sr;
     205          46 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
     206          46 :         if (p) ctx->timescale = p->value.uint;
     207             : 
     208             :         //initial config
     209          46 :         if (!ctx->freq || !ctx->nb_ch || !ctx->afmt) {
     210             :                 GF_Err e;
     211          43 :                 ctx->afmt = ctx->ofmt ? ctx->ofmt : afmt;
     212          43 :                 ctx->freq = ctx->osr ? ctx->osr : sr;
     213          43 :                 ctx->nb_ch = ctx->och ? ctx->och : nb_ch;
     214          43 :                 ctx->ch_cfg = ch_cfg;
     215             : 
     216          43 :                 e = gf_mixer_set_config(ctx->mixer, ctx->freq, ctx->nb_ch, afmt, ctx->ch_cfg);
     217          43 :                 if (e) return e;
     218             :         }
     219             :         //input reconfig
     220          46 :         if ((sr != ctx->input_ai.samplerate) || (nb_ch != ctx->input_ai.chan)
     221           3 :                 || (afmt != ctx->input_ai.afmt) || (ch_cfg != ctx->input_ai.ch_layout)
     222           2 :                 || (ctx->src_is_planar != gf_audio_fmt_is_planar(afmt))
     223             :         ) {
     224          44 :                 ctx->input_ai.samplerate = sr;
     225          44 :                 ctx->input_ai.afmt = afmt;
     226          44 :                 ctx->input_ai.chan = nb_ch;
     227          44 :                 ctx->input_ai.ch_layout = ch_cfg;
     228          44 :                 ctx->src_is_planar = gf_audio_fmt_is_planar(afmt);
     229          44 :                 ctx->cfg_changed = GF_TRUE;
     230             :         }
     231             : 
     232          46 :         ctx->passthrough = GF_FALSE;
     233          46 :         gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
     234             : 
     235          46 :         if ((ctx->input_ai.samplerate==ctx->freq) && (ctx->input_ai.chan==ctx->nb_ch) && (ctx->input_ai.afmt==ctx->afmt) && (ctx->speed==FIX_ONE))
     236          44 :                 ctx->passthrough = GF_TRUE;
     237             : 
     238          46 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(ctx->freq));
     239          46 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->freq));
     240          46 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(ctx->afmt));
     241          46 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(ctx->nb_ch));
     242          46 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CHANNEL_LAYOUT, &PROP_LONGUINT(ctx->ch_cfg));
     243          46 :         return GF_OK;
     244             : }
     245             : 
     246             : 
     247        2215 : static GF_Err resample_process(GF_Filter *filter)
     248             : {
     249             :         u8 *output;
     250             :         u32 osize, written;
     251             :         GF_FilterPacket *dstpck;
     252        2215 :         GF_ResampleCtx *ctx = gf_filter_get_udta(filter);
     253             :         u32 bps, bytes_per_samp;
     254        2215 :         if (!ctx->ipid) return GF_OK;
     255             : 
     256        2215 :         bps = gf_audio_fmt_bit_depth(ctx->afmt);
     257        2215 :         bytes_per_samp = ctx->nb_ch * bps / 8;
     258             : 
     259             :         while (1) {
     260        5273 :                 if (!ctx->in_pck) {
     261        5229 :                         ctx->in_pck = gf_filter_pid_get_packet(ctx->ipid);
     262             : 
     263        5229 :                         if (!ctx->in_pck) {
     264        2256 :                                 if (gf_filter_pid_is_eos(ctx->ipid)) {
     265          82 :                                         if (ctx->passthrough || ctx->input_ai.is_eos) {
     266          41 :                                                 if (ctx->opid)
     267          41 :                                                         gf_filter_pid_set_eos(ctx->opid);
     268             :                                                 return GF_EOS;
     269             :                                         }
     270          41 :                                         ctx->input_ai.is_eos = 1;
     271             :                                 } else {
     272        2174 :                                         ctx->input_ai.is_eos = 0;
     273        2174 :                                         return GF_OK;
     274             :                                 }
     275             :                         } else {
     276        2973 :                                 ctx->data = gf_filter_pck_get_data(ctx->in_pck, &ctx->size);
     277        2973 :                                 u64 cts = gf_filter_pck_get_cts(ctx->in_pck);
     278        2973 :                                 cts *= ctx->freq;
     279        2973 :                                 cts /= FIX2INT(ctx->speed * ctx->timescale);
     280        2973 :                                 if (!ctx->out_cts_plus_one) {
     281          43 :                                         ctx->out_cts_plus_one = cts + 1;
     282        2930 :                                 } else if (ctx->freq != ctx->input_ai.samplerate) {
     283             :                                         s64 diff = cts;
     284           0 :                                         diff -= ctx->out_cts_plus_one-1;
     285             :                                         //200ms max
     286           0 :                                         if (ABS(diff) * 1000 > ctx->freq * 200) {
     287           0 :                                                 ctx->out_cts_plus_one = cts + 1;
     288             :                                         }
     289             :                                 }
     290             :                         }
     291             :                 }
     292             : 
     293        3058 :                 if (ctx->passthrough) {
     294         252 :                         gf_filter_pck_forward(ctx->in_pck, ctx->opid);
     295         252 :                         gf_filter_pid_drop_packet(ctx->ipid);
     296         252 :                         ctx->in_pck = NULL;
     297         252 :                         continue;
     298             :                 }
     299             : 
     300        2806 :                 if (ctx->in_pck) {
     301        2765 :                         osize = ctx->size * ctx->nb_ch * bps;
     302        2765 :                         osize /= ctx->input_ai.chan * gf_audio_fmt_bit_depth(ctx->input_ai.afmt);
     303             :                         //output in higher samplerate, need more space for same samples
     304        2765 :                         if (ctx->freq > ctx->input_ai.samplerate) {
     305           0 :                                 osize *= ctx->freq;
     306           0 :                                 osize /= ctx->input_ai.samplerate;
     307           0 :                                 while (osize % bytes_per_samp)
     308           0 :                                         osize++;
     309             :                         }
     310             :                 } else {
     311             :                         //flush remaining samples from mixer, use 20 sample buffer
     312          41 :                         osize = 20*ctx->nb_ch * bps / 8;
     313             :                 }
     314             : 
     315        2806 :                 dstpck = gf_filter_pck_new_alloc(ctx->opid, osize, &output);
     316        2806 :                 if (!dstpck) return GF_OUT_OF_MEM;
     317             : 
     318        2806 :                 if (ctx->in_pck)
     319        2765 :                         gf_filter_pck_merge_properties(ctx->in_pck, dstpck);
     320             : 
     321        2806 :                 written = gf_mixer_get_output(ctx->mixer, output, osize, 0);
     322        2806 :                 if (!written) {
     323          83 :                         gf_filter_pck_discard(dstpck);
     324             :                 } else {
     325        2723 :                         if (written != osize) {
     326           4 :                                 gf_filter_pck_truncate(dstpck, written);
     327             :                         }
     328        2723 :                         gf_filter_pck_set_dts(dstpck, ctx->out_cts_plus_one - 1);
     329        2723 :                         gf_filter_pck_set_cts(dstpck, ctx->out_cts_plus_one - 1);
     330        2723 :                         gf_filter_pck_send(dstpck);
     331             : 
     332             :                         //out_cts is in output time scale ( = freq), increase by the amount of bytes/bps
     333        2723 :                         ctx->out_cts_plus_one += written / bytes_per_samp;
     334             :                 }
     335             : 
     336             :                 //still some bytes to use from packet, do not discard
     337        2806 :                 if (ctx->bytes_consumed<ctx->size) {
     338          44 :                         continue;
     339             :                 }
     340             :                 //done with this packet
     341        2762 :                 if (ctx->in_pck) {
     342           0 :                         ctx->in_pck = NULL;
     343           0 :                         gf_filter_pid_drop_packet(ctx->ipid);
     344             :                 }
     345             :         }
     346             :         return GF_OK;
     347             : }
     348             : 
     349          41 : static GF_Err resample_reconfigure_output(GF_Filter *filter, GF_FilterPid *pid)
     350             : {
     351             :         u32 sr, nb_ch, afmt;
     352             :         u64 ch_cfg;
     353             :         GF_Err e;
     354             :         const GF_PropertyValue *p;
     355          41 :         GF_ResampleCtx *ctx = gf_filter_get_udta(filter);
     356          41 :         if (ctx->opid != pid) return GF_BAD_PARAM;
     357             :                 
     358          41 :         sr = ctx->freq;
     359          41 :         p = gf_filter_pid_caps_query(pid, GF_PROP_PID_SAMPLE_RATE);
     360          41 :         if (p) sr = p->value.uint;
     361             : 
     362          41 :         nb_ch = ctx->nb_ch;
     363          41 :         p = gf_filter_pid_caps_query(pid, GF_PROP_PID_NUM_CHANNELS);
     364          41 :         if (p) nb_ch = p->value.uint;
     365             : 
     366          41 :         afmt = ctx->afmt;
     367          41 :         p = gf_filter_pid_caps_query(pid, GF_PROP_PID_AUDIO_FORMAT);
     368          41 :         if (p) afmt = p->value.uint;
     369             : 
     370          41 :         ch_cfg = ctx->ch_cfg;
     371          41 :         p = gf_filter_pid_caps_query(pid, GF_PROP_PID_CHANNEL_LAYOUT);
     372          41 :         if (p) ch_cfg = p->value.longuint;
     373             : 
     374          41 :         p = gf_filter_pid_caps_query(pid, GF_PROP_PID_AUDIO_SPEED);
     375          41 :         if (p) {
     376           3 :                 ctx->speed = FLT2FIX((Float) p->value.number);
     377           3 :                 if (ctx->speed<0) ctx->speed = -ctx->speed;
     378             :         } else {
     379          38 :                 ctx->speed = FIX_ONE;
     380             :         }
     381             : 
     382          41 :         if ((sr==ctx->freq) && (nb_ch==ctx->nb_ch) && (afmt==ctx->afmt) && (ch_cfg==ctx->ch_cfg) && (ctx->speed == FIX_ONE) ) return GF_OK;
     383             : 
     384          41 :         ctx->afmt = afmt;
     385          41 :         ctx->freq = sr;
     386          41 :         ctx->nb_ch = nb_ch;
     387          41 :         ctx->ch_cfg = ch_cfg;
     388             : 
     389          41 :         e = gf_mixer_set_config(ctx->mixer, ctx->freq, ctx->nb_ch, ctx->afmt, ctx->ch_cfg);
     390          41 :         if (e) return e;
     391          41 :         ctx->passthrough = GF_FALSE;
     392             : 
     393          41 :         if ((ctx->input_ai.samplerate==ctx->freq) && (ctx->input_ai.chan==ctx->nb_ch) && (ctx->input_ai.afmt==afmt) && (ctx->speed == FIX_ONE))
     394           0 :                 ctx->passthrough = GF_TRUE;
     395             : 
     396          41 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(sr));
     397          41 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(afmt));
     398          41 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(nb_ch));
     399          41 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CHANNEL_LAYOUT, &PROP_LONGUINT(ch_cfg));
     400             : 
     401          41 :         if (ctx->speed > FIX_ONE) {
     402             :                 GF_FilterEvent evt;
     403           2 :                 GF_FEVT_INIT(evt, GF_FEVT_BUFFER_REQ, ctx->ipid);
     404           2 :                 evt.buffer_req.max_buffer_us = FIX2INT( ctx->speed * 100000 );
     405           2 :                 gf_filter_pid_send_event(ctx->ipid, &evt);
     406             :         }
     407             : 
     408             :         return GF_OK;
     409             : }
     410             : 
     411         324 : static Bool resample_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     412             : {
     413         324 :         if (((evt->base.type==GF_FEVT_PLAY) || (evt->base.type==GF_FEVT_SET_SPEED) )
     414          10 :                 && evt->play.speed
     415             :         ) {
     416          10 :                 GF_ResampleCtx *ctx = gf_filter_get_udta(filter);
     417          10 :                 ctx->speed = FLT2FIX(evt->play.speed);
     418          10 :                 if (ctx->speed<0) ctx->speed = -ctx->speed;
     419             : 
     420          10 :                 ctx->passthrough = GF_FALSE;
     421          10 :                 if (ctx->speed > FIX_ONE) {
     422             :                         GF_FilterEvent anevt;
     423           0 :                         GF_FEVT_INIT(anevt, GF_FEVT_BUFFER_REQ, ctx->ipid);
     424           0 :                         anevt.buffer_req.max_buffer_us = FIX2INT( ctx->speed * 100000 );
     425           0 :                         gf_filter_pid_send_event(ctx->ipid, &anevt);
     426             :                 }
     427             :         }
     428         324 :         return GF_FALSE;
     429             : }
     430             : 
     431             : static const GF_FilterCapability ResamplerCaps[] =
     432             : {
     433             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     434             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW),
     435             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
     436             :         CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
     437             : };
     438             : 
     439             : #define OFFS(_n)        #_n, offsetof(GF_ResampleCtx, _n)
     440             : static const GF_FilterArgs ResamplerArgs[] =
     441             : {
     442             :         { OFFS(och), "desired number of output audio channels - 0 for auto", GF_PROP_UINT, "0", NULL, 0},
     443             :         { OFFS(osr), "desired sample rate of output audio - 0 for auto", GF_PROP_UINT, "0", NULL, 0},
     444             :         { OFFS(ofmt), "desired format of output audio - none for auto", GF_PROP_PCMFMT, "none", NULL, 0},
     445             :         {0}
     446             : };
     447             : 
     448             : GF_FilterRegister ResamplerRegister = {
     449             :         .name = "resample",
     450             :         GF_FS_SET_DESCRIPTION("Audio resampler")
     451             :         GF_FS_SET_HELP("This filter resamples raw audio to a target sample rate, number of channels or audio format.")
     452             :         .private_size = sizeof(GF_ResampleCtx),
     453             :         .initialize = resample_initialize,
     454             :         .finalize = resample_finalize,
     455             :         .args = ResamplerArgs,
     456             :         SETCAPS(ResamplerCaps),
     457             :         .configure_pid = resample_configure_pid,
     458             :         .process = resample_process,
     459             :         .reconfigure_output = resample_reconfigure_output,
     460             :         .process_event = resample_process_event,
     461             : };
     462             : 
     463             : 
     464        2877 : const GF_FilterRegister *resample_register(GF_FilterSession *session)
     465             : {
     466        2877 :         return &ResamplerRegister;
     467             : }
     468             : #else
     469             : 
     470             : const GF_FilterRegister *resample_register(GF_FilterSession *session)
     471             : {
     472             :         return NULL;
     473             : }
     474             : 
     475             : #endif // GPAC_DISABLE_PLAYER

Generated by: LCOV version 1.13