[FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
Andreas Rheinhardt
andreas.rheinhardt at outlook.com
Fri Apr 25 22:00:41 EEST 2025
一只大 肥猫:
> This patch introduces PCM dumping support between AVFilter links, intended for audio debugging.
>
> It adds a configure-time option `--dumpdir=PATH` to specify the output directory for raw PCM data (default: /tmp/).
>
> Two commands are exposed to control dumping:
> - dump_raw_start <dst_filter_name>
> - dump_raw_stop <dst_filter_name>
>
> Dump files are written as: srcname-dstname-channel.pcm
>
> Supports packed and planar formats. File descriptors are managed automatically and work only on audio links.
>
> Example usage:
> avfilter_process_command(filter, "dump_raw_start", "volume", NULL, 0, 0);
>
> This feature helps developers debug filter behavior by inspecting intermediate audio data.
> ---
> From 6a31c85afd2800c09076c1e2b7c734c7b719f73d Mon Sep 17 00:00:00 2001
> From: Yibo Fang <blueybf777 at outlook.com>
> Date: Wed, 23 Apr 2025 09:17:51 +0800
> Subject: [PATCH 1/1] avfilter: add PCM dumping between filters for audio
> debugging
>
> This patch adds the ability to dump raw PCM audio data between AVFilter links.
> It introduces a configure-time option `--dumpdir=PATH` to control the output
> directory of dump files (default: /tmp/). This feature is helpful for debugging
> filter behavior and verifying audio processing.
>
> Two filter commands are added:
> - dump_raw_start <dst_filter_name>
> - dump_raw_stop <dst_filter_name>
>
> The PCM files are written in the format: srcname-dstname-<channel>.pcm.
>
> Supports both packed and planar formats. File descriptors are automatically
> managed. Works only on audio links.
>
> Example usage:
> avfilter_process_command(filter, "dump_raw_start", "volume", NULL, 0, 0);
>
> Signed-off-by: Yibo Fang <blueybf777 at outlook.com>
> ---
> configure | 7 +++
> libavfilter/avfilter.c | 110 +++++++++++++++++++++++++++++++++++++++++
> libavfilter/avfilter.h | 4 ++
> 3 files changed, 121 insertions(+)
>
> diff --git a/configure b/configure
> index c94b8eac43..381633749d 100755
> --- a/configure
> +++ b/configure
> @@ -524,6 +524,7 @@ Developer options (useful when working on FFmpeg itself):
> --disable-large-tests disable tests that use a large amount of memory
> --disable-ptx-compression don't compress CUDA PTX code even when possible
> --disable-version-tracking don't include the git/release version in the build
> + --dumpdir=PATH location of pcm dump files to save.
>
> NOTE: Object files are built at the place where configure is launched.
> EOF
> @@ -2690,6 +2691,7 @@ PATHS_LIST="
> prefix
> shlibdir
> install_name_dir
> + dumpdir
> "
>
> CMDLINE_SET="
> @@ -4123,6 +4125,9 @@ incdir_default='${prefix}/include'
> libdir_default='${prefix}/lib'
> mandir_default='${prefix}/share/man'
>
> +# runtime path
> +dumpdir_default='/tmp/'
> +
> # toolchain
> ar_default="ar"
> cc_default="gcc"
> @@ -8118,6 +8123,7 @@ DOCDIR=\$(DESTDIR)$docdir
> MANDIR=\$(DESTDIR)$mandir
> PKGCONFIGDIR=\$(DESTDIR)$pkgconfigdir
> INSTALL_NAME_DIR=$install_name_dir
> +DUMPDIR=$dumpdir
> SRC_PATH=$source_path
> SRC_LINK=$source_link
> ifndef MAIN_MAKEFILE
> @@ -8267,6 +8273,7 @@ cat > $TMPH <<EOF
> #define CONFIG_THIS_YEAR 2025
> #define FFMPEG_DATADIR "$(eval c_escape $datadir)"
> #define AVCONV_DATADIR "$(eval c_escape $datadir)"
> +#define FFMPEG_DUMPDIR "$(eval c_escape $dumpdir)"
> #define CC_IDENT "$(c_escape ${cc_ident:-Unknown compiler})"
> #define OS_NAME $target_os
> #define EXTERN_PREFIX "${extern_prefix}"
> diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
> index 64c1075c40..9fc6308544 100644
> --- a/libavfilter/avfilter.c
> +++ b/libavfilter/avfilter.c
> @@ -19,6 +19,9 @@
> * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> */
>
> +#include <fcntl.h>
> +#include <unistd.h>
> +
> #include "libavutil/avassert.h"
> #include "libavutil/avstring.h"
> #include "libavutil/bprint.h"
> @@ -195,6 +198,66 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad,
> return 0;
> }
>
> +static av_cold void link_uninit_dump_pcm(AVFilterLink *link, int stop)
> +{
> + if (link->dump_pcm_fds) {
> + int i;
> + for (i = 0; i < link->nb_dump_pcm_fds; i++) {
> + if (link->dump_pcm_fds[i])
> + close(link->dump_pcm_fds[i]);
> + }
> + av_free(link->dump_pcm_fds);
> + link->dump_pcm_fds = NULL;
> + link->nb_dump_pcm_fds = 0;
> + }
> +
> + if (stop)
> + link->dump_pcm = 0;
> +}
> +
> +static av_cold int link_init_dump_pcm(AVFilterLink *link)
> +{
> + char path[4096];
> + int fd, i;
> +
> + link->nb_dump_pcm_fds = av_sample_fmt_is_planar(link->format)? link->ch_layout.nb_channels : 1;
> + link->dump_pcm_fds = av_malloc_array(link->nb_dump_pcm_fds, sizeof(int));
> + if (!link->dump_pcm_fds)
> + return AVERROR(ENOMEM);
> +
> + for (i = 0; i < link->nb_dump_pcm_fds; i++) {
> + snprintf(path, sizeof(path), FFMPEG_DUMPDIR"%.16s-%.8s-%d.pcm", link->src->name, link->dst->name, i);
> + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
> + if (fd < 0) {
> + link_uninit_dump_pcm(link, 1);
> + return AVERROR(errno);
> + }
> + link->dump_pcm_fds[i] = fd;
> + }
> +
> + return 0;
> +}
> +
> +static int filter_set_dump_pcm(AVFilterContext *filter, const char *target, int set)
> +{
> + int i;
> +
> + for (i = 0; i < filter->nb_outputs; i++) {
> + AVFilterLink *link = filter->outputs[i];
> + if (!target || !strcmp(link->dst->name, target)) {
> + if (set) {
> + link->dump_pcm = 1;
> + } else
> + link_uninit_dump_pcm(link, 1);
> +
> + if (target)
> + return 0;
> + }
> + }
> +
> + return target ? AVERROR(EINVAL) : 0;
> +}
> +
> static void link_free(AVFilterLink **link)
> {
> FilterLinkInternal *li;
> @@ -208,6 +271,8 @@ static void link_free(AVFilterLink **link)
> av_channel_layout_uninit(&(*link)->ch_layout);
> av_frame_side_data_free(&(*link)->side_data, &(*link)->nb_side_data);
>
> + link_uninit_dump_pcm(*link, 1);
> +
> av_buffer_unref(&li->l.hw_frames_ctx);
>
> av_freep(link);
> @@ -617,6 +682,10 @@ int avfilter_process_command(AVFilterContext *filter, const char *cmd, const cha
> if (res == local_res)
> av_log(filter, AV_LOG_INFO, "%s", res);
> return 0;
> + }else if(!strcmp(cmd, "dump_raw_start")) {
> + return filter_set_dump_pcm(filter, arg, 1);
> + }else if(!strcmp(cmd, "dump_raw_stop")) {
> + return filter_set_dump_pcm(filter, arg, 0);
> }else if(!strcmp(cmd, "enable")) {
> return set_enable_expr(fffilterctx(filter), arg);
> }else if (fffilter(filter->filter)->process_command) {
> @@ -1050,6 +1119,41 @@ fail:
> return ret;
> }
>
> +static int link_dump_frame(AVFilterLink *link, AVFrame *frame)
> +{
> + int samples_size, ret;
> +
> + if (!link->dump_pcm_fds) {
> + ret = link_init_dump_pcm(link);
> + if (ret < 0)
> + return ret;
> + }
> +
> + samples_size = av_get_bytes_per_sample(frame->format) * frame->nb_samples;
> + if (av_sample_fmt_is_planar(frame->format)) {
> + int i;
> + for (i = 0; i < link->nb_dump_pcm_fds && i < frame->ch_layout.nb_channels; i++) {
> + if (i < AV_NUM_DATA_POINTERS) {
> + ret = write(link->dump_pcm_fds[i], frame->data[i], samples_size);
> + } else
> + ret = write(link->dump_pcm_fds[i], frame->extended_data[i - AV_NUM_DATA_POINTERS], samples_size);
> +
> + if (ret < 0)
> + goto err;
> + }
> + } else {
> + ret = write(link->dump_pcm_fds[0], frame->data[0], samples_size * frame->ch_layout.nb_channels);
> + if (ret < 0)
> + goto err;
> +
> + }
> +
> + return 0;
> +err:
> + link_uninit_dump_pcm(link, 1);
> + return AVERROR(errno);
> +}
> +
> int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
> {
> FilterLinkInternal * const li = ff_link_internal(link);
> @@ -1087,6 +1191,12 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
> link->time_base);
> }
>
> + if (link->dump_pcm && link->type == AVMEDIA_TYPE_AUDIO) {
> + ret = link_dump_frame(link, frame);
> + if (ret < 0)
> + av_log(link->dst, AV_LOG_ERROR, "Dump pcm files failed with %d\n", ret);
> + }
> +
> li->frame_blocked_in = li->frame_wanted_out = 0;
> li->l.frame_count_in++;
> li->l.sample_count_in += frame->nb_samples;
> diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
> index a89d3cf658..6d04b9da77 100644
> --- a/libavfilter/avfilter.h
> +++ b/libavfilter/avfilter.h
> @@ -404,6 +404,10 @@ struct AVFilterLink {
> int sample_rate; ///< samples per second
> AVChannelLayout ch_layout; ///< channel layout of current buffer (see libavutil/channel_layout.h)
>
> + int dump_pcm; ///< flag to dump pcm
> + int *dump_pcm_fds; ///< dump files
> + unsigned nb_dump_pcm_fds; ///< number of dump file
> +
> /**
> * Define the time base used by the PTS of the frames/samples
> * which will pass through this link.
> --
> 2.34.1
>
Can't you use the asplit filter to duplicate the audio?
- Andreas
More information about the ffmpeg-devel
mailing list