diff --git a/Makefile.in b/Makefile.in index efd0705d4..e3fd6a990 100644 --- a/Makefile.in +++ b/Makefile.in @@ -123,6 +123,7 @@ COMMON_OBJS = \ src/audio/filter/delay.o \ src/audio/filter/discard.o \ src/audio/filter/controlport_stats.o \ + src/audio/filter/silence.o \ src/capture_filter.o \ src/capture_filter/change_pixfmt.o \ src/capture_filter/color.o \ diff --git a/src/audio/audio_filter.h b/src/audio/audio_filter.h index 1027a3229..2f373f01d 100644 --- a/src/audio/audio_filter.h +++ b/src/audio/audio_filter.h @@ -64,7 +64,7 @@ struct audio_filter_info{ /// @retval 0 if initialized successfully /// @retval <0 if error /// @retval >0 no error but state was not returned, eg. showing help - af_result_code (*init)(struct module *mod, const char *cfg, void **state); + enum af_result_code (*init)(struct module *mod, const char *cfg, void **state); /// @brief Frees filter void (*done)(void *state); @@ -76,7 +76,7 @@ struct audio_filter_info{ /// @param sample_rate sample rate /// @retval 0 if initialized successfully /// @retval <0 if error - af_result_code (*configure)(void *state, + enum af_result_code (*configure)(void *state, int bps, int ch_count, int sample_rate); /// @brief Get configured input audio format @@ -91,7 +91,7 @@ struct audio_filter_info{ /// @param state filter state /// @param f frame to filter, can take ownership of passed // frame and return a different one - af_result_code (*filter)(void *state, struct audio_frame **f); + enum af_result_code (*filter)(void *state, struct audio_frame **f); }; struct audio_filter{ @@ -99,12 +99,12 @@ struct audio_filter{ void *state; }; -af_result_code audio_filter_init(struct module *parent, const char *name, const char *cfg, +enum af_result_code audio_filter_init(struct module *parent, const char *name, const char *cfg, struct audio_filter *filter); void audio_filter_destroy(struct audio_filter *state); -af_result_code audio_filter_configure(struct audio_filter *state, +enum af_result_code audio_filter_configure(struct audio_filter *state, int bps, int ch_count, int sample_rate); void audio_filter_get_configured_out(struct audio_filter *state, @@ -113,7 +113,7 @@ void audio_filter_get_configured_out(struct audio_filter *state, void audio_filter_get_configured_in(struct audio_filter *state, int *bps, int *ch_count, int *sample_rate); -af_result_code audio_filter(struct audio_filter *state, struct audio_frame **frame); +enum af_result_code audio_filter(struct audio_filter *state, struct audio_frame **frame); //void register_audio_filter(struct audio_filter_info *filter); diff --git a/src/audio/filter/silence.c b/src/audio/filter/silence.c new file mode 100644 index 000000000..691bf4c08 --- /dev/null +++ b/src/audio/filter/silence.c @@ -0,0 +1,159 @@ +/** + * @file audio/filter/silence.c + * @author Martin Pulec + */ +/* + * Copyright (c) 2023 CESNET, z. s. p. o. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "audio/audio_filter.h" +#include "audio/types.h" +#include "lib_common.h" +#include "utils/color_out.h" + +enum { + MAX_CHANNELS = 16, +}; + +struct state_silence { + struct audio_desc desc; + size_t silence_channels[MAX_CHANNELS]; + int silence_channels_count; +}; + +static void +usage() +{ + color_printf("Audio capture " TBOLD( + "silence") " allows muting individual channels.\n\n"); + color_printf("Usage:\n"); + color_printf("\t" TBOLD("--audio-filter silence[:,]\n\n")); + color_printf("(indexed from zero)\n"); + color_printf("If no index is given, all channels will be muted.\n"); +} + +static enum af_result_code +init(struct module *parent, const char *cfg, void **state) +{ + (void) parent; + if (strlen(cfg) > 0) { + cfg++; + } + if (strcmp(cfg, "help") == 0) { + usage(); + return AF_HELP_SHOWN; + } + struct state_silence *s = calloc(1, sizeof *s); + + char *fmt = strdupa(cfg); + char *item = NULL; + char *end_ptr = NULL; + while ((item = strtok_r(fmt, ",", &end_ptr)) != NULL) { + assert(s->silence_channels_count < MAX_CHANNELS - 1); + s->silence_channels[s->silence_channels_count++] = + strtol(item, NULL, 10); + fmt = NULL; + } + *state = s; + return AF_OK; +} + +static enum af_result_code +configure(void *state, int in_bps, int in_ch_count, int in_sample_rate) +{ + struct state_silence *s = state; + + s->desc.bps = in_bps; + s->desc.ch_count = in_ch_count; + s->desc.sample_rate = in_sample_rate; + return AF_OK; +} + +static void +done(void *state) +{ + free(state); +} + +static void +get_configured_desc(void *state, int *bps, int *ch_count, int *sample_rate) +{ + struct state_silence *s = state; + + *bps = s->desc.bps; + *ch_count = s->desc.ch_count; + *sample_rate = s->desc.sample_rate; +} + +static enum af_result_code +filter(void *state, struct audio_frame **frame) +{ + struct state_silence *s = state; + + if (s->silence_channels_count == 0) { + memset((*frame)->data, 0, (*frame)->data_len); + return AF_OK; + } + + const int frame_size = s->desc.bps * s->desc.ch_count; + for (int i = 0; i < s->silence_channels_count; ++i) { + if (s->silence_channels[i] >= (size_t) (*frame)->ch_count) { + continue; + } + char *ptr = + (*frame)->data + s->silence_channels[i] * (*frame)->bps; + for (int j = 0; j < (*frame)->data_len; j += frame_size) { + memset(ptr, 0, (*frame)->bps); + ptr += frame_size; + } + } + + return AF_OK; +} + +static const struct audio_filter_info audio_filter_silence = { + .name = "silence", + .init = init, + .done = done, + .configure = configure, + .get_configured_in = get_configured_desc, + .get_configured_out = get_configured_desc, + .filter = filter, +}; + +REGISTER_MODULE(silence, &audio_filter_silence, LIBRARY_CLASS_AUDIO_FILTER, + AUDIO_FILTER_ABI_VERSION);