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 <blueybf...@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 <blueybf...@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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".