* [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
@ 2025-04-23 6:02 一只大 肥猫
2025-04-25 12:26 ` Nicolas George
2025-04-25 19:00 ` Andreas Rheinhardt
0 siblings, 2 replies; 7+ messages in thread
From: 一只大 肥猫 @ 2025-04-23 6:02 UTC (permalink / raw)
To: ffmpeg-devel
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@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@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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
2025-04-23 6:02 [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging 一只大 肥猫
@ 2025-04-25 12:26 ` Nicolas George
2025-04-25 19:00 ` Andreas Rheinhardt
1 sibling, 0 replies; 7+ messages in thread
From: Nicolas George @ 2025-04-25 12:26 UTC (permalink / raw)
To: FFmpeg development discussions and patches
一只大 肥猫 (HE12025-04-23):
> This patch introduces PCM dumping support between AVFilter links, intended for audio debugging.
Thanks for the patch. See a few remarks below.
> 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
I do not like the fact that it relies on a hard-coded output path. I
think it would be better to take the output file name from the command
argument, even if it means splitting it.
> 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);
You need to add that into the documentation. And please add an example
for the command-line tool.
> 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@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@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)
You never call it this function with stop != 1.
> +{
> + 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]);
0 is a valid fd, -1 is usually used for invalid.
> + }
> + av_free(link->dump_pcm_fds);
> + link->dump_pcm_fds = NULL;
av_freep()
> + link->nb_dump_pcm_fds = 0;
> + }
> +
> + if (stop)
> + link->dump_pcm = 0;
Seems redundant with dump_pcm_fds being NULL or not. The fact that stop
is always 1 makes it confusing.
> +}
> +
> +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));
I think turning planar into packet when dumping would be much more
convenient and simpler to implement.
> + 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);
I would be more comfortable with O_EXCL there too.
> + if (fd < 0) {
> + link_uninit_dump_pcm(link, 1);
> + return AVERROR(errno);
> + }
> + link->dump_pcm_fds[i] = fd;
Logging the file name would be a good idea.
> + }
> +
> + 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;
Why not init it immediately?
> + } else
> + link_uninit_dump_pcm(link, 1);
I do not remember what we decided, but mixing braces and no braces is
not it. Same below.
> +
> + 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);
This is not how extended_data works.
You need to loop in case of short writes. Or maybe, simpler: use stdio.
Same below.
> +
> + 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.
Regards,
--
Nicolas George
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
2025-04-23 6:02 [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging 一只大 肥猫
2025-04-25 12:26 ` Nicolas George
@ 2025-04-25 19:00 ` Andreas Rheinhardt
2025-05-26 7:43 ` [FFmpeg-devel] 回复: " Fang Yibo
1 sibling, 1 reply; 7+ messages in thread
From: Andreas Rheinhardt @ 2025-04-25 19:00 UTC (permalink / raw)
To: ffmpeg-devel
一只大 肥猫:
> 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@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@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
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 7+ messages in thread
* [FFmpeg-devel] 回复: [PATCH] avfilter: add PCM dumping between filters for audio debugging
2025-04-25 19:00 ` Andreas Rheinhardt
@ 2025-05-26 7:43 ` Fang Yibo
2025-05-26 7:49 ` [FFmpeg-devel] " Yibo Fang
0 siblings, 1 reply; 7+ messages in thread
From: Fang Yibo @ 2025-05-26 7:43 UTC (permalink / raw)
To: ffmpeg-devel
Re: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
From: Yibo Fang <blueybf777@outlook.com>
Subject: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
Date: Wed, 23 Apr 2025 10:57:00 +0800
Message-Id: <cover.0.2@ffmpeg>
This series adds a debugging feature that lets developers dump raw
PCM audio between filters. The data are written to files in a user-
selected directory via two new commands:
dump_raw_start file=/path/to/dir/
dump_raw_stop
Planar audio is automatically interleaved; only audio links are
affected. Patch 1/2 implements the feature, patch 2/2 documents it.
Regards,
Yibo Fang
---
From 2df56e74273d477ae27003e0981220749a6b58cf Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 10:58:55 +0800
Subject: [PATCH 1/2] avfilter: add PCM dumping between filters for audio
debugging
This patch introduces PCM dumping support between AVFilter links,
intended for audio debugging and filter-graph inspection.
Two filter commands are added:
dump_raw_start file=/path/to/output/dir/
dump_raw_stop
When dumping is enabled, audio leaving the selected filter output is
written to an automatically named file:
<src_filter>-<dst_filter>.pcm
Planar formats are converted to packed. File descriptors are opened
on demand and closed automatically. Works only on audio links.
Example (C API):
avfilter_process_command(filter, "dump_raw_start",
"file=/tmp/", NULL, 0, 0);
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
libavfilter/avfilter.c | 144 +++++++++++++++++++++++++++++++++++++++++
libavfilter/avfilter.h | 5 ++
2 files changed, 149 insertions(+)
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 64c1075c40..b7034569db 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"
@@
return 0;
}
+/* ---------- PCM dump helpers ---------- */
+
+static av_cold void link_uninit_dump_pcm(AVFilterLink *link)
+{
+ if (link->dump_pcm_fds) {
+ for (unsigned i = 0; i < link->nb_dump_pcm_fds; i++)
+ if (link->dump_pcm_fds[i] >= 0)
+ close(link->dump_pcm_fds[i]);
+ av_freep(&link->dump_pcm_fds);
+ link->nb_dump_pcm_fds = 0;
+ }
+ av_freep(&link->dump_pcm_path);
+ link->dump_pcm = 0;
+}
+
+static av_cold int link_init_dump_pcm(AVFilterLink *link)
+{
+ /* build full path "<dir>/<src>-<dst>.pcm" */
+ const char *dir = link->dump_pcm_path;
+ const char *sep = dir[strlen(dir) - 1] == '/' ? "" : "/";
+ char path[4096];
+ snprintf(path, sizeof(path), "%s%s%.16s-%.8s.pcm",
+ dir, sep, link->src->name, link->dst->name);
+
+ link->nb_dump_pcm_fds = 1;
+ link->dump_pcm_fds = av_malloc(sizeof(int));
+ if (!link->dump_pcm_fds)
+ return AVERROR(ENOMEM);
+
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+ if (fd < 0) {
+ link_uninit_dump_pcm(link);
+ return AVERROR(errno);
+ }
+ link->dump_pcm_fds[0] = fd;
+ av_log(link->dst, AV_LOG_INFO, "PCM dump file: %s\n", path);
+ return 0;
+}
+
+static int filter_set_dump_pcm(AVFilterContext *f, const char *arg, int set)
+{
+ const char *prefix = "file=";
+ if (!arg || av_strncasecmp(arg, prefix, strlen(prefix))) {
+ av_log(f, AV_LOG_ERROR,
+ "dump_raw_* expects argument file=/dir/\n");
+ return AVERROR(EINVAL);
+ }
+ const char *dir = arg + strlen(prefix);
+
+ for (int i = 0; i < f->nb_outputs; i++) {
+ AVFilterLink *l = f->outputs[i];
+ if (l->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (set) {
+ av_freep(&l->dump_pcm_path);
+ l->dump_pcm_path = av_strdup(dir);
+ if (!l->dump_pcm_path)
+ return AVERROR(ENOMEM);
+ l->dump_pcm = 1;
+ } else
+ link_uninit_dump_pcm(l);
+ }
+ return 0;
+}
+
+/* write one frame */
+static int link_dump_frame(AVFilterLink *link, AVFrame *frame)
+{
+ int ret;
+ if (!link->dump_pcm_fds &&
+ (ret = link_init_dump_pcm(link)) < 0)
+ return ret;
+
+ const int ch = frame->ch_layout.nb_channels;
+ const int bps = av_get_bytes_per_sample(frame->format);
+ const int ns = frame->nb_samples;
+ const int totlen = ch * ns * bps;
+
+ /* interleave planar to packed if necessary */
+ if (av_sample_fmt_is_planar(frame->format)) {
+ uint8_t *buf = av_malloc(totlen);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ for (int s = 0; s < ns; s++)
+ for (int c = 0; c < ch; c++)
+ memcpy(buf + (s*ch + c)*bps,
+ (uint8_t*)frame->extended_data[c] + s*bps,
+ bps);
+ ret = write(link->dump_pcm_fds[0], buf, totlen);
+ av_free(buf);
+ } else
+ ret = write(link->dump_pcm_fds[0], frame->data[0], totlen);
+
+ if (ret < 0) {
+ av_log(link->dst, AV_LOG_ERROR, "PCM dump write failed: %s\n",
+ av_err2str(ret));
+ link_uninit_dump_pcm(link);
+ return ret;
+ }
+ return 0;
+}
+
static void link_free(AVFilterLink **link)
{
@@
int avfilter_process_command(AVFilterContext *filter, const char *cmd,
const char *arg, char *res, int res_len, int flags)
{
@@
- if (res == local_res)
- av_log(filter, AV_LOG_INFO, "%s", res);
- return 0;
+ 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);
@@
/* ... existing code ... */
+ 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,
+ "PCM dump failed with %s\n", av_err2str(ret));
+ }
+
li->frame_blocked_in = li->frame_wanted_out = 0;
@@
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index a89d3cf658..0d4c91bb6b 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@
AVChannelLayout ch_layout; ///< channel layout of current buffer
+ int dump_pcm; ///< enable dumping on this link
+ int *dump_pcm_fds; ///< one FD per link (currently 1)
+ unsigned nb_dump_pcm_fds; ///< number of FDs
+ char *dump_pcm_path; ///< user-supplied directory
+
/**
* Define the time base used by the PTS of the frames/samples
* which will pass through this link.
--
2.34.1
---
From 8cf7bfa52a10be87e0f17b9783159b0e83f960f7 Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 12:38:00 +0800
Subject: [PATCH 2/2] doc/filters: document dump_raw_start and dump_raw_stop
commands
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
doc/filters.texi | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/doc/filters.texi b/doc/filters.texi
index 45b3e03e5e..6a2d23df3f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@
@section adumppcm
Dump raw PCM audio data for debugging between filters.
This filter allows writing raw audio data to disk from any audio link
in the filtergraph for inspection. It supports planar and packed formats,
automatically interleaving planar data for writing.
@subsection Syntax
@example
dump_raw_start=file=/your/dump/directory/
@end example
@subsection Usage
To enable dumping, use the @code{dump_raw_start} command:
@example
ffmpeg -i input.wav -af "volume,asendcmd='0.0 dump_raw_start file=/tmp/pcm/'" -f null -
@end example
To stop dumping:
@example
asendcmd='5.0 dump_raw_stop'
@end example
@subsection Options
@table @option
@item file
Specify a directory path (not a filename) where PCM data will be dumped.
The dumped file is named @code{<src>-<dst>.pcm} and will be overwritten if it already exists.
@end table
--
2.34.1
________________________________
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Sent: 26 April 2025 3:00
To: ffmpeg-devel@ffmpeg.org <ffmpeg-devel@ffmpeg.org>
Subject: Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
一只大 肥猫:
> 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@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@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
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
2025-05-26 7:43 ` [FFmpeg-devel] 回复: " Fang Yibo
@ 2025-05-26 7:49 ` Yibo Fang
2025-05-26 7:52 ` Yibo Fang
2025-05-26 7:57 ` [FFmpeg-devel] [FFmpeg-devel, v3, 0/2] " Yibo Fang
0 siblings, 2 replies; 7+ messages in thread
From: Yibo Fang @ 2025-05-26 7:49 UTC (permalink / raw)
To: ffmpeg-devel
[FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
From: Yibo Fang <blueybf777@outlook.com>
Subject: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
Date: Wed, 23 Apr 2025 10:57:00 +0800
Message-Id: <cover.0.2@ffmpeg>
This series adds a debugging feature that lets developers dump raw
PCM audio between filters. The data are written to files in a user-
selected directory via two new commands:
dump_raw_start file=/path/to/dir/
dump_raw_stop
Planar audio is automatically interleaved; only audio links are
affected. Patch 1/2 implements the feature, patch 2/2 documents it.
Regards,
Yibo Fang
---
From 2df56e74273d477ae27003e0981220749a6b58cf Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 10:58:55 +0800
Subject: [PATCH 1/2] avfilter: add PCM dumping between filters for audio
debugging
This patch introduces PCM dumping support between AVFilter links,
intended for audio debugging and filter-graph inspection.
Two filter commands are added:
dump_raw_start file=/path/to/output/dir/
dump_raw_stop
When dumping is enabled, audio leaving the selected filter output is
written to an automatically named file:
<src_filter>-<dst_filter>.pcm
Planar formats are converted to packed. File descriptors are opened
on demand and closed automatically. Works only on audio links.
Example (C API):
avfilter_process_command(filter, "dump_raw_start",
"file=/tmp/", NULL, 0, 0);
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
libavfilter/avfilter.c | 144 +++++++++++++++++++++++++++++++++++++++++
libavfilter/avfilter.h | 5 ++
2 files changed, 149 insertions(+)
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 64c1075c40..b7034569db 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"
@@
return 0;
}
+/* ---------- PCM dump helpers ---------- */
+
+static av_cold void link_uninit_dump_pcm(AVFilterLink *link)
+{
+ if (link->dump_pcm_fds) {
+ for (unsigned i = 0; i < link->nb_dump_pcm_fds; i++)
+ if (link->dump_pcm_fds[i] >= 0)
+ close(link->dump_pcm_fds[i]);
+ av_freep(&link->dump_pcm_fds);
+ link->nb_dump_pcm_fds = 0;
+ }
+ av_freep(&link->dump_pcm_path);
+ link->dump_pcm = 0;
+}
+
+static av_cold int link_init_dump_pcm(AVFilterLink *link)
+{
+ /* build full path "<dir>/<src>-<dst>.pcm" */
+ const char *dir = link->dump_pcm_path;
+ const char *sep = dir[strlen(dir) - 1] == '/' ? "" : "/";
+ char path[4096];
+ snprintf(path, sizeof(path), "%s%s%.16s-%.8s.pcm",
+ dir, sep, link->src->name, link->dst->name);
+
+ link->nb_dump_pcm_fds = 1;
+ link->dump_pcm_fds = av_malloc(sizeof(int));
+ if (!link->dump_pcm_fds)
+ return AVERROR(ENOMEM);
+
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+ if (fd < 0) {
+ link_uninit_dump_pcm(link);
+ return AVERROR(errno);
+ }
+ link->dump_pcm_fds[0] = fd;
+ av_log(link->dst, AV_LOG_INFO, "PCM dump file: %s\n", path);
+ return 0;
+}
+
+static int filter_set_dump_pcm(AVFilterContext *f, const char *arg, int set)
+{
+ const char *prefix = "file=";
+ if (!arg || av_strncasecmp(arg, prefix, strlen(prefix))) {
+ av_log(f, AV_LOG_ERROR,
+ "dump_raw_* expects argument file=/dir/\n");
+ return AVERROR(EINVAL);
+ }
+ const char *dir = arg + strlen(prefix);
+
+ for (int i = 0; i < f->nb_outputs; i++) {
+ AVFilterLink *l = f->outputs[i];
+ if (l->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (set) {
+ av_freep(&l->dump_pcm_path);
+ l->dump_pcm_path = av_strdup(dir);
+ if (!l->dump_pcm_path)
+ return AVERROR(ENOMEM);
+ l->dump_pcm = 1;
+ } else
+ link_uninit_dump_pcm(l);
+ }
+ return 0;
+}
+
+/* write one frame */
+static int link_dump_frame(AVFilterLink *link, AVFrame *frame)
+{
+ int ret;
+ if (!link->dump_pcm_fds &&
+ (ret = link_init_dump_pcm(link)) < 0)
+ return ret;
+
+ const int ch = frame->ch_layout.nb_channels;
+ const int bps = av_get_bytes_per_sample(frame->format);
+ const int ns = frame->nb_samples;
+ const int totlen = ch * ns * bps;
+
+ /* interleave planar to packed if necessary */
+ if (av_sample_fmt_is_planar(frame->format)) {
+ uint8_t *buf = av_malloc(totlen);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ for (int s = 0; s < ns; s++)
+ for (int c = 0; c < ch; c++)
+ memcpy(buf + (s*ch + c)*bps,
+ (uint8_t*)frame->extended_data[c] + s*bps,
+ bps);
+ ret = write(link->dump_pcm_fds[0], buf, totlen);
+ av_free(buf);
+ } else
+ ret = write(link->dump_pcm_fds[0], frame->data[0], totlen);
+
+ if (ret < 0) {
+ av_log(link->dst, AV_LOG_ERROR, "PCM dump write failed: %s\n",
+ av_err2str(ret));
+ link_uninit_dump_pcm(link);
+ return ret;
+ }
+ return 0;
+}
+
static void link_free(AVFilterLink **link)
{
@@
int avfilter_process_command(AVFilterContext *filter, const char *cmd,
const char *arg, char *res, int res_len, int flags)
{
@@
- if (res == local_res)
- av_log(filter, AV_LOG_INFO, "%s", res);
- return 0;
+ 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);
@@
/* ... existing code ... */
+ 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,
+ "PCM dump failed with %s\n", av_err2str(ret));
+ }
+
li->frame_blocked_in = li->frame_wanted_out = 0;
@@
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index a89d3cf658..0d4c91bb6b 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@
AVChannelLayout ch_layout; ///< channel layout of current buffer
+ int dump_pcm; ///< enable dumping on this link
+ int *dump_pcm_fds; ///< one FD per link (currently 1)
+ unsigned nb_dump_pcm_fds; ///< number of FDs
+ char *dump_pcm_path; ///< user-supplied directory
+
/**
* Define the time base used by the PTS of the frames/samples
* which will pass through this link.
--
2.34.1
---
From 8cf7bfa52a10be87e0f17b9783159b0e83f960f7 Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 12:38:00 +0800
Subject: [PATCH 2/2] doc/filters: document dump_raw_start and dump_raw_stop
commands
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
doc/filters.texi | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/doc/filters.texi b/doc/filters.texi
index 45b3e03e5e..6a2d23df3f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@
@section adumppcm
Dump raw PCM audio data for debugging between filters.
This filter allows writing raw audio data to disk from any audio link
in the filtergraph for inspection. It supports planar and packed formats,
automatically interleaving planar data for writing.
@subsection Syntax
@example
dump_raw_start=file=/your/dump/directory/
@end example
@subsection Usage
To enable dumping, use the @code{dump_raw_start} command:
@example
ffmpeg -i input.wav -af "volume,asendcmd='0.0 dump_raw_start file=/tmp/pcm/'" -f null -
@end example
To stop dumping:
@example
asendcmd='5.0 dump_raw_stop'
@end example
@subsection Options
@table @option
@item file
Specify a directory path (not a filename) where PCM data will be dumped.
The dumped file is named @code{<src>-<dst>.pcm} and will be overwritten if it already exists.
@end table
--
2.34.1
________________________________
From: Fang Yibo <blueybf777@outlook.com>
Sent: 26 May 2025 15:43
To: ffmpeg-devel@ffmpeg.org <ffmpeg-devel@ffmpeg.org>
Subject: 回复: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
Re: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
From: Yibo Fang <blueybf777@outlook.com>
Subject: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
Date: Wed, 23 Apr 2025 10:57:00 +0800
Message-Id: <cover.0.2@ffmpeg>
This series adds a debugging feature that lets developers dump raw
PCM audio between filters. The data are written to files in a user-
selected directory via two new commands:
dump_raw_start file=/path/to/dir/
dump_raw_stop
Planar audio is automatically interleaved; only audio links are
affected. Patch 1/2 implements the feature, patch 2/2 documents it.
Regards,
Yibo Fang
---
From 2df56e74273d477ae27003e0981220749a6b58cf Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 10:58:55 +0800
Subject: [PATCH 1/2] avfilter: add PCM dumping between filters for audio
debugging
This patch introduces PCM dumping support between AVFilter links,
intended for audio debugging and filter-graph inspection.
Two filter commands are added:
dump_raw_start file=/path/to/output/dir/
dump_raw_stop
When dumping is enabled, audio leaving the selected filter output is
written to an automatically named file:
<src_filter>-<dst_filter>.pcm
Planar formats are converted to packed. File descriptors are opened
on demand and closed automatically. Works only on audio links.
Example (C API):
avfilter_process_command(filter, "dump_raw_start",
"file=/tmp/", NULL, 0, 0);
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
libavfilter/avfilter.c | 144 +++++++++++++++++++++++++++++++++++++++++
libavfilter/avfilter.h | 5 ++
2 files changed, 149 insertions(+)
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 64c1075c40..b7034569db 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"
@@
return 0;
}
+/* ---------- PCM dump helpers ---------- */
+
+static av_cold void link_uninit_dump_pcm(AVFilterLink *link)
+{
+ if (link->dump_pcm_fds) {
+ for (unsigned i = 0; i < link->nb_dump_pcm_fds; i++)
+ if (link->dump_pcm_fds[i] >= 0)
+ close(link->dump_pcm_fds[i]);
+ av_freep(&link->dump_pcm_fds);
+ link->nb_dump_pcm_fds = 0;
+ }
+ av_freep(&link->dump_pcm_path);
+ link->dump_pcm = 0;
+}
+
+static av_cold int link_init_dump_pcm(AVFilterLink *link)
+{
+ /* build full path "<dir>/<src>-<dst>.pcm" */
+ const char *dir = link->dump_pcm_path;
+ const char *sep = dir[strlen(dir) - 1] == '/' ? "" : "/";
+ char path[4096];
+ snprintf(path, sizeof(path), "%s%s%.16s-%.8s.pcm",
+ dir, sep, link->src->name, link->dst->name);
+
+ link->nb_dump_pcm_fds = 1;
+ link->dump_pcm_fds = av_malloc(sizeof(int));
+ if (!link->dump_pcm_fds)
+ return AVERROR(ENOMEM);
+
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+ if (fd < 0) {
+ link_uninit_dump_pcm(link);
+ return AVERROR(errno);
+ }
+ link->dump_pcm_fds[0] = fd;
+ av_log(link->dst, AV_LOG_INFO, "PCM dump file: %s\n", path);
+ return 0;
+}
+
+static int filter_set_dump_pcm(AVFilterContext *f, const char *arg, int set)
+{
+ const char *prefix = "file=";
+ if (!arg || av_strncasecmp(arg, prefix, strlen(prefix))) {
+ av_log(f, AV_LOG_ERROR,
+ "dump_raw_* expects argument file=/dir/\n");
+ return AVERROR(EINVAL);
+ }
+ const char *dir = arg + strlen(prefix);
+
+ for (int i = 0; i < f->nb_outputs; i++) {
+ AVFilterLink *l = f->outputs[i];
+ if (l->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (set) {
+ av_freep(&l->dump_pcm_path);
+ l->dump_pcm_path = av_strdup(dir);
+ if (!l->dump_pcm_path)
+ return AVERROR(ENOMEM);
+ l->dump_pcm = 1;
+ } else
+ link_uninit_dump_pcm(l);
+ }
+ return 0;
+}
+
+/* write one frame */
+static int link_dump_frame(AVFilterLink *link, AVFrame *frame)
+{
+ int ret;
+ if (!link->dump_pcm_fds &&
+ (ret = link_init_dump_pcm(link)) < 0)
+ return ret;
+
+ const int ch = frame->ch_layout.nb_channels;
+ const int bps = av_get_bytes_per_sample(frame->format);
+ const int ns = frame->nb_samples;
+ const int totlen = ch * ns * bps;
+
+ /* interleave planar to packed if necessary */
+ if (av_sample_fmt_is_planar(frame->format)) {
+ uint8_t *buf = av_malloc(totlen);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ for (int s = 0; s < ns; s++)
+ for (int c = 0; c < ch; c++)
+ memcpy(buf + (s*ch + c)*bps,
+ (uint8_t*)frame->extended_data[c] + s*bps,
+ bps);
+ ret = write(link->dump_pcm_fds[0], buf, totlen);
+ av_free(buf);
+ } else
+ ret = write(link->dump_pcm_fds[0], frame->data[0], totlen);
+
+ if (ret < 0) {
+ av_log(link->dst, AV_LOG_ERROR, "PCM dump write failed: %s\n",
+ av_err2str(ret));
+ link_uninit_dump_pcm(link);
+ return ret;
+ }
+ return 0;
+}
+
static void link_free(AVFilterLink **link)
{
@@
int avfilter_process_command(AVFilterContext *filter, const char *cmd,
const char *arg, char *res, int res_len, int flags)
{
@@
- if (res == local_res)
- av_log(filter, AV_LOG_INFO, "%s", res);
- return 0;
+ 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);
@@
/* ... existing code ... */
+ 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,
+ "PCM dump failed with %s\n", av_err2str(ret));
+ }
+
li->frame_blocked_in = li->frame_wanted_out = 0;
@@
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index a89d3cf658..0d4c91bb6b 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@
AVChannelLayout ch_layout; ///< channel layout of current buffer
+ int dump_pcm; ///< enable dumping on this link
+ int *dump_pcm_fds; ///< one FD per link (currently 1)
+ unsigned nb_dump_pcm_fds; ///< number of FDs
+ char *dump_pcm_path; ///< user-supplied directory
+
/**
* Define the time base used by the PTS of the frames/samples
* which will pass through this link.
--
2.34.1
---
From 8cf7bfa52a10be87e0f17b9783159b0e83f960f7 Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 12:38:00 +0800
Subject: [PATCH 2/2] doc/filters: document dump_raw_start and dump_raw_stop
commands
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
doc/filters.texi | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/doc/filters.texi b/doc/filters.texi
index 45b3e03e5e..6a2d23df3f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@
@section adumppcm
Dump raw PCM audio data for debugging between filters.
This filter allows writing raw audio data to disk from any audio link
in the filtergraph for inspection. It supports planar and packed formats,
automatically interleaving planar data for writing.
@subsection Syntax
@example
dump_raw_start=file=/your/dump/directory/
@end example
@subsection Usage
To enable dumping, use the @code{dump_raw_start} command:
@example
ffmpeg -i input.wav -af "volume,asendcmd='0.0 dump_raw_start file=/tmp/pcm/'" -f null -
@end example
To stop dumping:
@example
asendcmd='5.0 dump_raw_stop'
@end example
@subsection Options
@table @option
@item file
Specify a directory path (not a filename) where PCM data will be dumped.
The dumped file is named @code{<src>-<dst>.pcm} and will be overwritten if it already exists.
@end table
--
2.34.1
________________________________
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Sent: 26 April 2025 3:00
To: ffmpeg-devel@ffmpeg.org <ffmpeg-devel@ffmpeg.org>
Subject: Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
一只大 肥猫:
> 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@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@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
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
2025-05-26 7:49 ` [FFmpeg-devel] " Yibo Fang
@ 2025-05-26 7:52 ` Yibo Fang
2025-05-26 7:57 ` [FFmpeg-devel] [FFmpeg-devel, v3, 0/2] " Yibo Fang
1 sibling, 0 replies; 7+ messages in thread
From: Yibo Fang @ 2025-05-26 7:52 UTC (permalink / raw)
To: FFmpeg development discussions and patches
Re: [FFmpeg-devel,v3,0/2] avfilter: add PCM dumping between filters for audio debugging
From: Yibo Fang <blueybf777@outlook.com>
Subject: [FFmpeg-devel,v3,0/2] avfilter: add PCM dumping between filters for audio debugging
Date: Wed, 23 Apr 2025 10:57:00 +0800
Message-Id: <cover.0.2@ffmpeg>
This series adds a debugging feature that lets developers dump raw
PCM audio between filters. The data are written to files in a user-
selected directory via two new commands:
dump_raw_start file=/path/to/dir/
dump_raw_stop
Planar audio is automatically interleaved; only audio links are
affected. Patch 1/2 implements the feature, patch 2/2 documents it.
Regards,
Yibo Fang
---
From 2df56e74273d477ae27003e0981220749a6b58cf Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 10:58:55 +0800
Subject: [PATCH 1/2] avfilter: add PCM dumping between filters for audio
debugging
This patch introduces PCM dumping support between AVFilter links,
intended for audio debugging and filter-graph inspection.
Two filter commands are added:
dump_raw_start file=/path/to/output/dir/
dump_raw_stop
When dumping is enabled, audio leaving the selected filter output is
written to an automatically named file:
<src_filter>-<dst_filter>.pcm
Planar formats are converted to packed. File descriptors are opened
on demand and closed automatically. Works only on audio links.
Example (C API):
avfilter_process_command(filter, "dump_raw_start",
"file=/tmp/", NULL, 0, 0);
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
libavfilter/avfilter.c | 144 +++++++++++++++++++++++++++++++++++++++++
libavfilter/avfilter.h | 5 ++
2 files changed, 149 insertions(+)
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 64c1075c40..b7034569db 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"
@@
return 0;
}
+/* ---------- PCM dump helpers ---------- */
+
+static av_cold void link_uninit_dump_pcm(AVFilterLink *link)
+{
+ if (link->dump_pcm_fds) {
+ for (unsigned i = 0; i < link->nb_dump_pcm_fds; i++)
+ if (link->dump_pcm_fds[i] >= 0)
+ close(link->dump_pcm_fds[i]);
+ av_freep(&link->dump_pcm_fds);
+ link->nb_dump_pcm_fds = 0;
+ }
+ av_freep(&link->dump_pcm_path);
+ link->dump_pcm = 0;
+}
+
+static av_cold int link_init_dump_pcm(AVFilterLink *link)
+{
+ /* build full path "<dir>/<src>-<dst>.pcm" */
+ const char *dir = link->dump_pcm_path;
+ const char *sep = dir[strlen(dir) - 1] == '/' ? "" : "/";
+ char path[4096];
+ snprintf(path, sizeof(path), "%s%s%.16s-%.8s.pcm",
+ dir, sep, link->src->name, link->dst->name);
+
+ link->nb_dump_pcm_fds = 1;
+ link->dump_pcm_fds = av_malloc(sizeof(int));
+ if (!link->dump_pcm_fds)
+ return AVERROR(ENOMEM);
+
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+ if (fd < 0) {
+ link_uninit_dump_pcm(link);
+ return AVERROR(errno);
+ }
+ link->dump_pcm_fds[0] = fd;
+ av_log(link->dst, AV_LOG_INFO, "PCM dump file: %s\n", path);
+ return 0;
+}
+
+static int filter_set_dump_pcm(AVFilterContext *f, const char *arg, int set)
+{
+ const char *prefix = "file=";
+ if (!arg || av_strncasecmp(arg, prefix, strlen(prefix))) {
+ av_log(f, AV_LOG_ERROR,
+ "dump_raw_* expects argument file=/dir/\n");
+ return AVERROR(EINVAL);
+ }
+ const char *dir = arg + strlen(prefix);
+
+ for (int i = 0; i < f->nb_outputs; i++) {
+ AVFilterLink *l = f->outputs[i];
+ if (l->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (set) {
+ av_freep(&l->dump_pcm_path);
+ l->dump_pcm_path = av_strdup(dir);
+ if (!l->dump_pcm_path)
+ return AVERROR(ENOMEM);
+ l->dump_pcm = 1;
+ } else
+ link_uninit_dump_pcm(l);
+ }
+ return 0;
+}
+
+/* write one frame */
+static int link_dump_frame(AVFilterLink *link, AVFrame *frame)
+{
+ int ret;
+ if (!link->dump_pcm_fds &&
+ (ret = link_init_dump_pcm(link)) < 0)
+ return ret;
+
+ const int ch = frame->ch_layout.nb_channels;
+ const int bps = av_get_bytes_per_sample(frame->format);
+ const int ns = frame->nb_samples;
+ const int totlen = ch * ns * bps;
+
+ /* interleave planar to packed if necessary */
+ if (av_sample_fmt_is_planar(frame->format)) {
+ uint8_t *buf = av_malloc(totlen);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ for (int s = 0; s < ns; s++)
+ for (int c = 0; c < ch; c++)
+ memcpy(buf + (s*ch + c)*bps,
+ (uint8_t*)frame->extended_data[c] + s*bps,
+ bps);
+ ret = write(link->dump_pcm_fds[0], buf, totlen);
+ av_free(buf);
+ } else
+ ret = write(link->dump_pcm_fds[0], frame->data[0], totlen);
+
+ if (ret < 0) {
+ av_log(link->dst, AV_LOG_ERROR, "PCM dump write failed: %s\n",
+ av_err2str(ret));
+ link_uninit_dump_pcm(link);
+ return ret;
+ }
+ return 0;
+}
+
static void link_free(AVFilterLink **link)
{
@@
int avfilter_process_command(AVFilterContext *filter, const char *cmd,
const char *arg, char *res, int res_len, int flags)
{
@@
- if (res == local_res)
- av_log(filter, AV_LOG_INFO, "%s", res);
- return 0;
+ 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);
@@
/* ... existing code ... */
+ 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,
+ "PCM dump failed with %s\n", av_err2str(ret));
+ }
+
li->frame_blocked_in = li->frame_wanted_out = 0;
@@
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index a89d3cf658..0d4c91bb6b 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@
AVChannelLayout ch_layout; ///< channel layout of current buffer
+ int dump_pcm; ///< enable dumping on this link
+ int *dump_pcm_fds; ///< one FD per link (currently 1)
+ unsigned nb_dump_pcm_fds; ///< number of FDs
+ char *dump_pcm_path; ///< user-supplied directory
+
/**
* Define the time base used by the PTS of the frames/samples
* which will pass through this link.
--
2.34.1
---
From 8cf7bfa52a10be87e0f17b9783159b0e83f960f7 Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 12:38:00 +0800
Subject: [PATCH 2/2] doc/filters: document dump_raw_start and dump_raw_stop
commands
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
doc/filters.texi | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/doc/filters.texi b/doc/filters.texi
index 45b3e03e5e..6a2d23df3f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@
@section adumppcm
Dump raw PCM audio data for debugging between filters.
This filter allows writing raw audio data to disk from any audio link
in the filtergraph for inspection. It supports planar and packed formats,
automatically interleaving planar data for writing.
@subsection Syntax
@example
dump_raw_start=file=/your/dump/directory/
@end example
@subsection Usage
To enable dumping, use the @code{dump_raw_start} command:
@example
ffmpeg -i input.wav -af "volume,asendcmd='0.0 dump_raw_start file=/tmp/pcm/'" -f null -
@end example
To stop dumping:
@example
asendcmd='5.0 dump_raw_stop'
@end example
@subsection Options
@table @option
@item file
Specify a directory path (not a filename) where PCM data will be dumped.
The dumped file is named @code{<src>-<dst>.pcm} and will be overwritten if it already exists.
@end table
--
2.34.1
________________________________
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of Yibo Fang <blueybf777@outlook.com>
Sent: 26 May 2025 15:49
To: ffmpeg-devel@ffmpeg.org <ffmpeg-devel@ffmpeg.org>
Subject: Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
[FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
From: Yibo Fang <blueybf777@outlook.com>
Subject: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
Date: Wed, 23 Apr 2025 10:57:00 +0800
Message-Id: <cover.0.2@ffmpeg>
This series adds a debugging feature that lets developers dump raw
PCM audio between filters. The data are written to files in a user-
selected directory via two new commands:
dump_raw_start file=/path/to/dir/
dump_raw_stop
Planar audio is automatically interleaved; only audio links are
affected. Patch 1/2 implements the feature, patch 2/2 documents it.
Regards,
Yibo Fang
---
From 2df56e74273d477ae27003e0981220749a6b58cf Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 10:58:55 +0800
Subject: [PATCH 1/2] avfilter: add PCM dumping between filters for audio
debugging
This patch introduces PCM dumping support between AVFilter links,
intended for audio debugging and filter-graph inspection.
Two filter commands are added:
dump_raw_start file=/path/to/output/dir/
dump_raw_stop
When dumping is enabled, audio leaving the selected filter output is
written to an automatically named file:
<src_filter>-<dst_filter>.pcm
Planar formats are converted to packed. File descriptors are opened
on demand and closed automatically. Works only on audio links.
Example (C API):
avfilter_process_command(filter, "dump_raw_start",
"file=/tmp/", NULL, 0, 0);
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
libavfilter/avfilter.c | 144 +++++++++++++++++++++++++++++++++++++++++
libavfilter/avfilter.h | 5 ++
2 files changed, 149 insertions(+)
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 64c1075c40..b7034569db 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"
@@
return 0;
}
+/* ---------- PCM dump helpers ---------- */
+
+static av_cold void link_uninit_dump_pcm(AVFilterLink *link)
+{
+ if (link->dump_pcm_fds) {
+ for (unsigned i = 0; i < link->nb_dump_pcm_fds; i++)
+ if (link->dump_pcm_fds[i] >= 0)
+ close(link->dump_pcm_fds[i]);
+ av_freep(&link->dump_pcm_fds);
+ link->nb_dump_pcm_fds = 0;
+ }
+ av_freep(&link->dump_pcm_path);
+ link->dump_pcm = 0;
+}
+
+static av_cold int link_init_dump_pcm(AVFilterLink *link)
+{
+ /* build full path "<dir>/<src>-<dst>.pcm" */
+ const char *dir = link->dump_pcm_path;
+ const char *sep = dir[strlen(dir) - 1] == '/' ? "" : "/";
+ char path[4096];
+ snprintf(path, sizeof(path), "%s%s%.16s-%.8s.pcm",
+ dir, sep, link->src->name, link->dst->name);
+
+ link->nb_dump_pcm_fds = 1;
+ link->dump_pcm_fds = av_malloc(sizeof(int));
+ if (!link->dump_pcm_fds)
+ return AVERROR(ENOMEM);
+
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+ if (fd < 0) {
+ link_uninit_dump_pcm(link);
+ return AVERROR(errno);
+ }
+ link->dump_pcm_fds[0] = fd;
+ av_log(link->dst, AV_LOG_INFO, "PCM dump file: %s\n", path);
+ return 0;
+}
+
+static int filter_set_dump_pcm(AVFilterContext *f, const char *arg, int set)
+{
+ const char *prefix = "file=";
+ if (!arg || av_strncasecmp(arg, prefix, strlen(prefix))) {
+ av_log(f, AV_LOG_ERROR,
+ "dump_raw_* expects argument file=/dir/\n");
+ return AVERROR(EINVAL);
+ }
+ const char *dir = arg + strlen(prefix);
+
+ for (int i = 0; i < f->nb_outputs; i++) {
+ AVFilterLink *l = f->outputs[i];
+ if (l->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (set) {
+ av_freep(&l->dump_pcm_path);
+ l->dump_pcm_path = av_strdup(dir);
+ if (!l->dump_pcm_path)
+ return AVERROR(ENOMEM);
+ l->dump_pcm = 1;
+ } else
+ link_uninit_dump_pcm(l);
+ }
+ return 0;
+}
+
+/* write one frame */
+static int link_dump_frame(AVFilterLink *link, AVFrame *frame)
+{
+ int ret;
+ if (!link->dump_pcm_fds &&
+ (ret = link_init_dump_pcm(link)) < 0)
+ return ret;
+
+ const int ch = frame->ch_layout.nb_channels;
+ const int bps = av_get_bytes_per_sample(frame->format);
+ const int ns = frame->nb_samples;
+ const int totlen = ch * ns * bps;
+
+ /* interleave planar to packed if necessary */
+ if (av_sample_fmt_is_planar(frame->format)) {
+ uint8_t *buf = av_malloc(totlen);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ for (int s = 0; s < ns; s++)
+ for (int c = 0; c < ch; c++)
+ memcpy(buf + (s*ch + c)*bps,
+ (uint8_t*)frame->extended_data[c] + s*bps,
+ bps);
+ ret = write(link->dump_pcm_fds[0], buf, totlen);
+ av_free(buf);
+ } else
+ ret = write(link->dump_pcm_fds[0], frame->data[0], totlen);
+
+ if (ret < 0) {
+ av_log(link->dst, AV_LOG_ERROR, "PCM dump write failed: %s\n",
+ av_err2str(ret));
+ link_uninit_dump_pcm(link);
+ return ret;
+ }
+ return 0;
+}
+
static void link_free(AVFilterLink **link)
{
@@
int avfilter_process_command(AVFilterContext *filter, const char *cmd,
const char *arg, char *res, int res_len, int flags)
{
@@
- if (res == local_res)
- av_log(filter, AV_LOG_INFO, "%s", res);
- return 0;
+ 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);
@@
/* ... existing code ... */
+ 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,
+ "PCM dump failed with %s\n", av_err2str(ret));
+ }
+
li->frame_blocked_in = li->frame_wanted_out = 0;
@@
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index a89d3cf658..0d4c91bb6b 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@
AVChannelLayout ch_layout; ///< channel layout of current buffer
+ int dump_pcm; ///< enable dumping on this link
+ int *dump_pcm_fds; ///< one FD per link (currently 1)
+ unsigned nb_dump_pcm_fds; ///< number of FDs
+ char *dump_pcm_path; ///< user-supplied directory
+
/**
* Define the time base used by the PTS of the frames/samples
* which will pass through this link.
--
2.34.1
---
From 8cf7bfa52a10be87e0f17b9783159b0e83f960f7 Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 12:38:00 +0800
Subject: [PATCH 2/2] doc/filters: document dump_raw_start and dump_raw_stop
commands
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
doc/filters.texi | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/doc/filters.texi b/doc/filters.texi
index 45b3e03e5e..6a2d23df3f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@
@section adumppcm
Dump raw PCM audio data for debugging between filters.
This filter allows writing raw audio data to disk from any audio link
in the filtergraph for inspection. It supports planar and packed formats,
automatically interleaving planar data for writing.
@subsection Syntax
@example
dump_raw_start=file=/your/dump/directory/
@end example
@subsection Usage
To enable dumping, use the @code{dump_raw_start} command:
@example
ffmpeg -i input.wav -af "volume,asendcmd='0.0 dump_raw_start file=/tmp/pcm/'" -f null -
@end example
To stop dumping:
@example
asendcmd='5.0 dump_raw_stop'
@end example
@subsection Options
@table @option
@item file
Specify a directory path (not a filename) where PCM data will be dumped.
The dumped file is named @code{<src>-<dst>.pcm} and will be overwritten if it already exists.
@end table
--
2.34.1
________________________________
From: Fang Yibo <blueybf777@outlook.com>
Sent: 26 May 2025 15:43
To: ffmpeg-devel@ffmpeg.org <ffmpeg-devel@ffmpeg.org>
Subject: Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
Re: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
From: Yibo Fang <blueybf777@outlook.com>
Subject: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
Date: Wed, 23 Apr 2025 10:57:00 +0800
Message-Id: <cover.0.2@ffmpeg>
This series adds a debugging feature that lets developers dump raw
PCM audio between filters. The data are written to files in a user-
selected directory via two new commands:
dump_raw_start file=/path/to/dir/
dump_raw_stop
Planar audio is automatically interleaved; only audio links are
affected. Patch 1/2 implements the feature, patch 2/2 documents it.
Regards,
Yibo Fang
---
From 2df56e74273d477ae27003e0981220749a6b58cf Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 10:58:55 +0800
Subject: [PATCH 1/2] avfilter: add PCM dumping between filters for audio
debugging
This patch introduces PCM dumping support between AVFilter links,
intended for audio debugging and filter-graph inspection.
Two filter commands are added:
dump_raw_start file=/path/to/output/dir/
dump_raw_stop
When dumping is enabled, audio leaving the selected filter output is
written to an automatically named file:
<src_filter>-<dst_filter>.pcm
Planar formats are converted to packed. File descriptors are opened
on demand and closed automatically. Works only on audio links.
Example (C API):
avfilter_process_command(filter, "dump_raw_start",
"file=/tmp/", NULL, 0, 0);
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
libavfilter/avfilter.c | 144 +++++++++++++++++++++++++++++++++++++++++
libavfilter/avfilter.h | 5 ++
2 files changed, 149 insertions(+)
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 64c1075c40..b7034569db 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"
@@
return 0;
}
+/* ---------- PCM dump helpers ---------- */
+
+static av_cold void link_uninit_dump_pcm(AVFilterLink *link)
+{
+ if (link->dump_pcm_fds) {
+ for (unsigned i = 0; i < link->nb_dump_pcm_fds; i++)
+ if (link->dump_pcm_fds[i] >= 0)
+ close(link->dump_pcm_fds[i]);
+ av_freep(&link->dump_pcm_fds);
+ link->nb_dump_pcm_fds = 0;
+ }
+ av_freep(&link->dump_pcm_path);
+ link->dump_pcm = 0;
+}
+
+static av_cold int link_init_dump_pcm(AVFilterLink *link)
+{
+ /* build full path "<dir>/<src>-<dst>.pcm" */
+ const char *dir = link->dump_pcm_path;
+ const char *sep = dir[strlen(dir) - 1] == '/' ? "" : "/";
+ char path[4096];
+ snprintf(path, sizeof(path), "%s%s%.16s-%.8s.pcm",
+ dir, sep, link->src->name, link->dst->name);
+
+ link->nb_dump_pcm_fds = 1;
+ link->dump_pcm_fds = av_malloc(sizeof(int));
+ if (!link->dump_pcm_fds)
+ return AVERROR(ENOMEM);
+
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+ if (fd < 0) {
+ link_uninit_dump_pcm(link);
+ return AVERROR(errno);
+ }
+ link->dump_pcm_fds[0] = fd;
+ av_log(link->dst, AV_LOG_INFO, "PCM dump file: %s\n", path);
+ return 0;
+}
+
+static int filter_set_dump_pcm(AVFilterContext *f, const char *arg, int set)
+{
+ const char *prefix = "file=";
+ if (!arg || av_strncasecmp(arg, prefix, strlen(prefix))) {
+ av_log(f, AV_LOG_ERROR,
+ "dump_raw_* expects argument file=/dir/\n");
+ return AVERROR(EINVAL);
+ }
+ const char *dir = arg + strlen(prefix);
+
+ for (int i = 0; i < f->nb_outputs; i++) {
+ AVFilterLink *l = f->outputs[i];
+ if (l->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (set) {
+ av_freep(&l->dump_pcm_path);
+ l->dump_pcm_path = av_strdup(dir);
+ if (!l->dump_pcm_path)
+ return AVERROR(ENOMEM);
+ l->dump_pcm = 1;
+ } else
+ link_uninit_dump_pcm(l);
+ }
+ return 0;
+}
+
+/* write one frame */
+static int link_dump_frame(AVFilterLink *link, AVFrame *frame)
+{
+ int ret;
+ if (!link->dump_pcm_fds &&
+ (ret = link_init_dump_pcm(link)) < 0)
+ return ret;
+
+ const int ch = frame->ch_layout.nb_channels;
+ const int bps = av_get_bytes_per_sample(frame->format);
+ const int ns = frame->nb_samples;
+ const int totlen = ch * ns * bps;
+
+ /* interleave planar to packed if necessary */
+ if (av_sample_fmt_is_planar(frame->format)) {
+ uint8_t *buf = av_malloc(totlen);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ for (int s = 0; s < ns; s++)
+ for (int c = 0; c < ch; c++)
+ memcpy(buf + (s*ch + c)*bps,
+ (uint8_t*)frame->extended_data[c] + s*bps,
+ bps);
+ ret = write(link->dump_pcm_fds[0], buf, totlen);
+ av_free(buf);
+ } else
+ ret = write(link->dump_pcm_fds[0], frame->data[0], totlen);
+
+ if (ret < 0) {
+ av_log(link->dst, AV_LOG_ERROR, "PCM dump write failed: %s\n",
+ av_err2str(ret));
+ link_uninit_dump_pcm(link);
+ return ret;
+ }
+ return 0;
+}
+
static void link_free(AVFilterLink **link)
{
@@
int avfilter_process_command(AVFilterContext *filter, const char *cmd,
const char *arg, char *res, int res_len, int flags)
{
@@
- if (res == local_res)
- av_log(filter, AV_LOG_INFO, "%s", res);
- return 0;
+ 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);
@@
/* ... existing code ... */
+ 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,
+ "PCM dump failed with %s\n", av_err2str(ret));
+ }
+
li->frame_blocked_in = li->frame_wanted_out = 0;
@@
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index a89d3cf658..0d4c91bb6b 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@
AVChannelLayout ch_layout; ///< channel layout of current buffer
+ int dump_pcm; ///< enable dumping on this link
+ int *dump_pcm_fds; ///< one FD per link (currently 1)
+ unsigned nb_dump_pcm_fds; ///< number of FDs
+ char *dump_pcm_path; ///< user-supplied directory
+
/**
* Define the time base used by the PTS of the frames/samples
* which will pass through this link.
--
2.34.1
---
From 8cf7bfa52a10be87e0f17b9783159b0e83f960f7 Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 12:38:00 +0800
Subject: [PATCH 2/2] doc/filters: document dump_raw_start and dump_raw_stop
commands
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
doc/filters.texi | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/doc/filters.texi b/doc/filters.texi
index 45b3e03e5e..6a2d23df3f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@
@section adumppcm
Dump raw PCM audio data for debugging between filters.
This filter allows writing raw audio data to disk from any audio link
in the filtergraph for inspection. It supports planar and packed formats,
automatically interleaving planar data for writing.
@subsection Syntax
@example
dump_raw_start=file=/your/dump/directory/
@end example
@subsection Usage
To enable dumping, use the @code{dump_raw_start} command:
@example
ffmpeg -i input.wav -af "volume,asendcmd='0.0 dump_raw_start file=/tmp/pcm/'" -f null -
@end example
To stop dumping:
@example
asendcmd='5.0 dump_raw_stop'
@end example
@subsection Options
@table @option
@item file
Specify a directory path (not a filename) where PCM data will be dumped.
The dumped file is named @code{<src>-<dst>.pcm} and will be overwritten if it already exists.
@end table
--
2.34.1
________________________________
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Sent: 26 April 2025 3:00
To: ffmpeg-devel@ffmpeg.org <ffmpeg-devel@ffmpeg.org>
Subject: Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
??? ??:
> 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@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@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
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 7+ messages in thread
* [FFmpeg-devel] [FFmpeg-devel, v3, 0/2] avfilter: add PCM dumping between filters for audio debugging
2025-05-26 7:49 ` [FFmpeg-devel] " Yibo Fang
2025-05-26 7:52 ` Yibo Fang
@ 2025-05-26 7:57 ` Yibo Fang
1 sibling, 0 replies; 7+ messages in thread
From: Yibo Fang @ 2025-05-26 7:57 UTC (permalink / raw)
To: ffmpeg-devel
From: Yibo Fang <blueybf777@outlook.com>
Subject: [FFmpeg-devel,v3,0/2] avfilter: add PCM dumping between filters for audio debugging
Date: Wed, 23 Apr 2025 10:57:00 +0800
Message-Id: <cover.0.2@ffmpeg>
This series adds a debugging feature that lets developers dump raw
PCM audio between filters. The data are written to files in a user-
selected directory via two new commands:
dump_raw_start file=/path/to/dir/
dump_raw_stop
Planar audio is automatically interleaved; only audio links are
affected. Patch 1/2 implements the feature, patch 2/2 documents it.
Regards,
Yibo Fang
---
From 2df56e74273d477ae27003e0981220749a6b58cf Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 10:58:55 +0800
Subject: [PATCH 1/2] avfilter: add PCM dumping between filters for audio
debugging
This patch introduces PCM dumping support between AVFilter links,
intended for audio debugging and filter-graph inspection.
Two filter commands are added:
dump_raw_start file=/path/to/output/dir/
dump_raw_stop
When dumping is enabled, audio leaving the selected filter output is
written to an automatically named file:
<src_filter>-<dst_filter>.pcm
Planar formats are converted to packed. File descriptors are opened
on demand and closed automatically. Works only on audio links.
Example (C API):
avfilter_process_command(filter, "dump_raw_start",
"file=/tmp/", NULL, 0, 0);
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
libavfilter/avfilter.c | 144 +++++++++++++++++++++++++++++++++++++++++
libavfilter/avfilter.h | 5 ++
2 files changed, 149 insertions(+)
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 64c1075c40..b7034569db 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"
@@
return 0;
}
+/* ---------- PCM dump helpers ---------- */
+
+static av_cold void link_uninit_dump_pcm(AVFilterLink *link)
+{
+ if (link->dump_pcm_fds) {
+ for (unsigned i = 0; i < link->nb_dump_pcm_fds; i++)
+ if (link->dump_pcm_fds[i] >= 0)
+ close(link->dump_pcm_fds[i]);
+ av_freep(&link->dump_pcm_fds);
+ link->nb_dump_pcm_fds = 0;
+ }
+ av_freep(&link->dump_pcm_path);
+ link->dump_pcm = 0;
+}
+
+static av_cold int link_init_dump_pcm(AVFilterLink *link)
+{
+ /* build full path "<dir>/<src>-<dst>.pcm" */
+ const char *dir = link->dump_pcm_path;
+ const char *sep = dir[strlen(dir) - 1] == '/' ? "" : "/";
+ char path[4096];
+ snprintf(path, sizeof(path), "%s%s%.16s-%.8s.pcm",
+ dir, sep, link->src->name, link->dst->name);
+
+ link->nb_dump_pcm_fds = 1;
+ link->dump_pcm_fds = av_malloc(sizeof(int));
+ if (!link->dump_pcm_fds)
+ return AVERROR(ENOMEM);
+
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+ if (fd < 0) {
+ link_uninit_dump_pcm(link);
+ return AVERROR(errno);
+ }
+ link->dump_pcm_fds[0] = fd;
+ av_log(link->dst, AV_LOG_INFO, "PCM dump file: %s\n", path);
+ return 0;
+}
+
+static int filter_set_dump_pcm(AVFilterContext *f, const char *arg, int set)
+{
+ const char *prefix = "file=";
+ if (!arg || av_strncasecmp(arg, prefix, strlen(prefix))) {
+ av_log(f, AV_LOG_ERROR,
+ "dump_raw_* expects argument file=/dir/\n");
+ return AVERROR(EINVAL);
+ }
+ const char *dir = arg + strlen(prefix);
+
+ for (int i = 0; i < f->nb_outputs; i++) {
+ AVFilterLink *l = f->outputs[i];
+ if (l->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (set) {
+ av_freep(&l->dump_pcm_path);
+ l->dump_pcm_path = av_strdup(dir);
+ if (!l->dump_pcm_path)
+ return AVERROR(ENOMEM);
+ l->dump_pcm = 1;
+ } else
+ link_uninit_dump_pcm(l);
+ }
+ return 0;
+}
+
+/* write one frame */
+static int link_dump_frame(AVFilterLink *link, AVFrame *frame)
+{
+ int ret;
+ if (!link->dump_pcm_fds &&
+ (ret = link_init_dump_pcm(link)) < 0)
+ return ret;
+
+ const int ch = frame->ch_layout.nb_channels;
+ const int bps = av_get_bytes_per_sample(frame->format);
+ const int ns = frame->nb_samples;
+ const int totlen = ch * ns * bps;
+
+ /* interleave planar to packed if necessary */
+ if (av_sample_fmt_is_planar(frame->format)) {
+ uint8_t *buf = av_malloc(totlen);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ for (int s = 0; s < ns; s++)
+ for (int c = 0; c < ch; c++)
+ memcpy(buf + (s*ch + c)*bps,
+ (uint8_t*)frame->extended_data[c] + s*bps,
+ bps);
+ ret = write(link->dump_pcm_fds[0], buf, totlen);
+ av_free(buf);
+ } else
+ ret = write(link->dump_pcm_fds[0], frame->data[0], totlen);
+
+ if (ret < 0) {
+ av_log(link->dst, AV_LOG_ERROR, "PCM dump write failed: %s\n",
+ av_err2str(ret));
+ link_uninit_dump_pcm(link);
+ return ret;
+ }
+ return 0;
+}
+
static void link_free(AVFilterLink **link)
{
@@
int avfilter_process_command(AVFilterContext *filter, const char *cmd,
const char *arg, char *res, int res_len, int flags)
{
@@
- if (res == local_res)
- av_log(filter, AV_LOG_INFO, "%s", res);
- return 0;
+ 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);
@@
/* ... existing code ... */
+ 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,
+ "PCM dump failed with %s\n", av_err2str(ret));
+ }
+
li->frame_blocked_in = li->frame_wanted_out = 0;
@@
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index a89d3cf658..0d4c91bb6b 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@
AVChannelLayout ch_layout; ///< channel layout of current buffer
+ int dump_pcm; ///< enable dumping on this link
+ int *dump_pcm_fds; ///< one FD per link (currently 1)
+ unsigned nb_dump_pcm_fds; ///< number of FDs
+ char *dump_pcm_path; ///< user-supplied directory
+
/**
* Define the time base used by the PTS of the frames/samples
* which will pass through this link.
--
2.34.1
---
From 8cf7bfa52a10be87e0f17b9783159b0e83f960f7 Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 12:38:00 +0800
Subject: [PATCH 2/2] doc/filters: document dump_raw_start and dump_raw_stop
commands
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
doc/filters.texi | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/doc/filters.texi b/doc/filters.texi
index 45b3e03e5e..6a2d23df3f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@
@section adumppcm
Dump raw PCM audio data for debugging between filters.
This filter allows writing raw audio data to disk from any audio link
in the filtergraph for inspection. It supports planar and packed formats,
automatically interleaving planar data for writing.
@subsection Syntax
@example
dump_raw_start=file=/your/dump/directory/
@end example
@subsection Usage
To enable dumping, use the @code{dump_raw_start} command:
@example
ffmpeg -i input.wav -af "volume,asendcmd='0.0 dump_raw_start file=/tmp/pcm/'" -f null -
@end example
To stop dumping:
@example
asendcmd='5.0 dump_raw_stop'
@end example
@subsection Options
@table @option
@item file
Specify a directory path (not a filename) where PCM data will be dumped.
The dumped file is named @code{<src>-<dst>.pcm} and will be overwritten if it already exists.
@end table
--
2.34.1
________________________________
From: Yibo Fang <blueybf777@outlook.com>
Sent: 26 May 2025 15:49
To: ffmpeg-devel@ffmpeg.org <ffmpeg-devel@ffmpeg.org>
Subject: Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
[FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
From: Yibo Fang <blueybf777@outlook.com>
Subject: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
Date: Wed, 23 Apr 2025 10:57:00 +0800
Message-Id: <cover.0.2@ffmpeg>
This series adds a debugging feature that lets developers dump raw
PCM audio between filters. The data are written to files in a user-
selected directory via two new commands:
dump_raw_start file=/path/to/dir/
dump_raw_stop
Planar audio is automatically interleaved; only audio links are
affected. Patch 1/2 implements the feature, patch 2/2 documents it.
Regards,
Yibo Fang
---
From 2df56e74273d477ae27003e0981220749a6b58cf Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 10:58:55 +0800
Subject: [PATCH 1/2] avfilter: add PCM dumping between filters for audio
debugging
This patch introduces PCM dumping support between AVFilter links,
intended for audio debugging and filter-graph inspection.
Two filter commands are added:
dump_raw_start file=/path/to/output/dir/
dump_raw_stop
When dumping is enabled, audio leaving the selected filter output is
written to an automatically named file:
<src_filter>-<dst_filter>.pcm
Planar formats are converted to packed. File descriptors are opened
on demand and closed automatically. Works only on audio links.
Example (C API):
avfilter_process_command(filter, "dump_raw_start",
"file=/tmp/", NULL, 0, 0);
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
libavfilter/avfilter.c | 144 +++++++++++++++++++++++++++++++++++++++++
libavfilter/avfilter.h | 5 ++
2 files changed, 149 insertions(+)
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 64c1075c40..b7034569db 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"
@@
return 0;
}
+/* ---------- PCM dump helpers ---------- */
+
+static av_cold void link_uninit_dump_pcm(AVFilterLink *link)
+{
+ if (link->dump_pcm_fds) {
+ for (unsigned i = 0; i < link->nb_dump_pcm_fds; i++)
+ if (link->dump_pcm_fds[i] >= 0)
+ close(link->dump_pcm_fds[i]);
+ av_freep(&link->dump_pcm_fds);
+ link->nb_dump_pcm_fds = 0;
+ }
+ av_freep(&link->dump_pcm_path);
+ link->dump_pcm = 0;
+}
+
+static av_cold int link_init_dump_pcm(AVFilterLink *link)
+{
+ /* build full path "<dir>/<src>-<dst>.pcm" */
+ const char *dir = link->dump_pcm_path;
+ const char *sep = dir[strlen(dir) - 1] == '/' ? "" : "/";
+ char path[4096];
+ snprintf(path, sizeof(path), "%s%s%.16s-%.8s.pcm",
+ dir, sep, link->src->name, link->dst->name);
+
+ link->nb_dump_pcm_fds = 1;
+ link->dump_pcm_fds = av_malloc(sizeof(int));
+ if (!link->dump_pcm_fds)
+ return AVERROR(ENOMEM);
+
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+ if (fd < 0) {
+ link_uninit_dump_pcm(link);
+ return AVERROR(errno);
+ }
+ link->dump_pcm_fds[0] = fd;
+ av_log(link->dst, AV_LOG_INFO, "PCM dump file: %s\n", path);
+ return 0;
+}
+
+static int filter_set_dump_pcm(AVFilterContext *f, const char *arg, int set)
+{
+ const char *prefix = "file=";
+ if (!arg || av_strncasecmp(arg, prefix, strlen(prefix))) {
+ av_log(f, AV_LOG_ERROR,
+ "dump_raw_* expects argument file=/dir/\n");
+ return AVERROR(EINVAL);
+ }
+ const char *dir = arg + strlen(prefix);
+
+ for (int i = 0; i < f->nb_outputs; i++) {
+ AVFilterLink *l = f->outputs[i];
+ if (l->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (set) {
+ av_freep(&l->dump_pcm_path);
+ l->dump_pcm_path = av_strdup(dir);
+ if (!l->dump_pcm_path)
+ return AVERROR(ENOMEM);
+ l->dump_pcm = 1;
+ } else
+ link_uninit_dump_pcm(l);
+ }
+ return 0;
+}
+
+/* write one frame */
+static int link_dump_frame(AVFilterLink *link, AVFrame *frame)
+{
+ int ret;
+ if (!link->dump_pcm_fds &&
+ (ret = link_init_dump_pcm(link)) < 0)
+ return ret;
+
+ const int ch = frame->ch_layout.nb_channels;
+ const int bps = av_get_bytes_per_sample(frame->format);
+ const int ns = frame->nb_samples;
+ const int totlen = ch * ns * bps;
+
+ /* interleave planar to packed if necessary */
+ if (av_sample_fmt_is_planar(frame->format)) {
+ uint8_t *buf = av_malloc(totlen);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ for (int s = 0; s < ns; s++)
+ for (int c = 0; c < ch; c++)
+ memcpy(buf + (s*ch + c)*bps,
+ (uint8_t*)frame->extended_data[c] + s*bps,
+ bps);
+ ret = write(link->dump_pcm_fds[0], buf, totlen);
+ av_free(buf);
+ } else
+ ret = write(link->dump_pcm_fds[0], frame->data[0], totlen);
+
+ if (ret < 0) {
+ av_log(link->dst, AV_LOG_ERROR, "PCM dump write failed: %s\n",
+ av_err2str(ret));
+ link_uninit_dump_pcm(link);
+ return ret;
+ }
+ return 0;
+}
+
static void link_free(AVFilterLink **link)
{
@@
int avfilter_process_command(AVFilterContext *filter, const char *cmd,
const char *arg, char *res, int res_len, int flags)
{
@@
- if (res == local_res)
- av_log(filter, AV_LOG_INFO, "%s", res);
- return 0;
+ 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);
@@
/* ... existing code ... */
+ 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,
+ "PCM dump failed with %s\n", av_err2str(ret));
+ }
+
li->frame_blocked_in = li->frame_wanted_out = 0;
@@
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index a89d3cf658..0d4c91bb6b 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@
AVChannelLayout ch_layout; ///< channel layout of current buffer
+ int dump_pcm; ///< enable dumping on this link
+ int *dump_pcm_fds; ///< one FD per link (currently 1)
+ unsigned nb_dump_pcm_fds; ///< number of FDs
+ char *dump_pcm_path; ///< user-supplied directory
+
/**
* Define the time base used by the PTS of the frames/samples
* which will pass through this link.
--
2.34.1
---
From 8cf7bfa52a10be87e0f17b9783159b0e83f960f7 Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 12:38:00 +0800
Subject: [PATCH 2/2] doc/filters: document dump_raw_start and dump_raw_stop
commands
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
doc/filters.texi | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/doc/filters.texi b/doc/filters.texi
index 45b3e03e5e..6a2d23df3f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@
@section adumppcm
Dump raw PCM audio data for debugging between filters.
This filter allows writing raw audio data to disk from any audio link
in the filtergraph for inspection. It supports planar and packed formats,
automatically interleaving planar data for writing.
@subsection Syntax
@example
dump_raw_start=file=/your/dump/directory/
@end example
@subsection Usage
To enable dumping, use the @code{dump_raw_start} command:
@example
ffmpeg -i input.wav -af "volume,asendcmd='0.0 dump_raw_start file=/tmp/pcm/'" -f null -
@end example
To stop dumping:
@example
asendcmd='5.0 dump_raw_stop'
@end example
@subsection Options
@table @option
@item file
Specify a directory path (not a filename) where PCM data will be dumped.
The dumped file is named @code{<src>-<dst>.pcm} and will be overwritten if it already exists.
@end table
--
2.34.1
________________________________
From: Fang Yibo <blueybf777@outlook.com>
Sent: 26 May 2025 15:43
To: ffmpeg-devel@ffmpeg.org <ffmpeg-devel@ffmpeg.org>
Subject: 回复: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
Re: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
From: Yibo Fang <blueybf777@outlook.com>
Subject: [FFmpeg-devel,v2,0/2] avfilter: add PCM dumping between filters for audio debugging
Date: Wed, 23 Apr 2025 10:57:00 +0800
Message-Id: <cover.0.2@ffmpeg>
This series adds a debugging feature that lets developers dump raw
PCM audio between filters. The data are written to files in a user-
selected directory via two new commands:
dump_raw_start file=/path/to/dir/
dump_raw_stop
Planar audio is automatically interleaved; only audio links are
affected. Patch 1/2 implements the feature, patch 2/2 documents it.
Regards,
Yibo Fang
---
From 2df56e74273d477ae27003e0981220749a6b58cf Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 10:58:55 +0800
Subject: [PATCH 1/2] avfilter: add PCM dumping between filters for audio
debugging
This patch introduces PCM dumping support between AVFilter links,
intended for audio debugging and filter-graph inspection.
Two filter commands are added:
dump_raw_start file=/path/to/output/dir/
dump_raw_stop
When dumping is enabled, audio leaving the selected filter output is
written to an automatically named file:
<src_filter>-<dst_filter>.pcm
Planar formats are converted to packed. File descriptors are opened
on demand and closed automatically. Works only on audio links.
Example (C API):
avfilter_process_command(filter, "dump_raw_start",
"file=/tmp/", NULL, 0, 0);
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
libavfilter/avfilter.c | 144 +++++++++++++++++++++++++++++++++++++++++
libavfilter/avfilter.h | 5 ++
2 files changed, 149 insertions(+)
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 64c1075c40..b7034569db 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"
@@
return 0;
}
+/* ---------- PCM dump helpers ---------- */
+
+static av_cold void link_uninit_dump_pcm(AVFilterLink *link)
+{
+ if (link->dump_pcm_fds) {
+ for (unsigned i = 0; i < link->nb_dump_pcm_fds; i++)
+ if (link->dump_pcm_fds[i] >= 0)
+ close(link->dump_pcm_fds[i]);
+ av_freep(&link->dump_pcm_fds);
+ link->nb_dump_pcm_fds = 0;
+ }
+ av_freep(&link->dump_pcm_path);
+ link->dump_pcm = 0;
+}
+
+static av_cold int link_init_dump_pcm(AVFilterLink *link)
+{
+ /* build full path "<dir>/<src>-<dst>.pcm" */
+ const char *dir = link->dump_pcm_path;
+ const char *sep = dir[strlen(dir) - 1] == '/' ? "" : "/";
+ char path[4096];
+ snprintf(path, sizeof(path), "%s%s%.16s-%.8s.pcm",
+ dir, sep, link->src->name, link->dst->name);
+
+ link->nb_dump_pcm_fds = 1;
+ link->dump_pcm_fds = av_malloc(sizeof(int));
+ if (!link->dump_pcm_fds)
+ return AVERROR(ENOMEM);
+
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+ if (fd < 0) {
+ link_uninit_dump_pcm(link);
+ return AVERROR(errno);
+ }
+ link->dump_pcm_fds[0] = fd;
+ av_log(link->dst, AV_LOG_INFO, "PCM dump file: %s\n", path);
+ return 0;
+}
+
+static int filter_set_dump_pcm(AVFilterContext *f, const char *arg, int set)
+{
+ const char *prefix = "file=";
+ if (!arg || av_strncasecmp(arg, prefix, strlen(prefix))) {
+ av_log(f, AV_LOG_ERROR,
+ "dump_raw_* expects argument file=/dir/\n");
+ return AVERROR(EINVAL);
+ }
+ const char *dir = arg + strlen(prefix);
+
+ for (int i = 0; i < f->nb_outputs; i++) {
+ AVFilterLink *l = f->outputs[i];
+ if (l->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (set) {
+ av_freep(&l->dump_pcm_path);
+ l->dump_pcm_path = av_strdup(dir);
+ if (!l->dump_pcm_path)
+ return AVERROR(ENOMEM);
+ l->dump_pcm = 1;
+ } else
+ link_uninit_dump_pcm(l);
+ }
+ return 0;
+}
+
+/* write one frame */
+static int link_dump_frame(AVFilterLink *link, AVFrame *frame)
+{
+ int ret;
+ if (!link->dump_pcm_fds &&
+ (ret = link_init_dump_pcm(link)) < 0)
+ return ret;
+
+ const int ch = frame->ch_layout.nb_channels;
+ const int bps = av_get_bytes_per_sample(frame->format);
+ const int ns = frame->nb_samples;
+ const int totlen = ch * ns * bps;
+
+ /* interleave planar to packed if necessary */
+ if (av_sample_fmt_is_planar(frame->format)) {
+ uint8_t *buf = av_malloc(totlen);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ for (int s = 0; s < ns; s++)
+ for (int c = 0; c < ch; c++)
+ memcpy(buf + (s*ch + c)*bps,
+ (uint8_t*)frame->extended_data[c] + s*bps,
+ bps);
+ ret = write(link->dump_pcm_fds[0], buf, totlen);
+ av_free(buf);
+ } else
+ ret = write(link->dump_pcm_fds[0], frame->data[0], totlen);
+
+ if (ret < 0) {
+ av_log(link->dst, AV_LOG_ERROR, "PCM dump write failed: %s\n",
+ av_err2str(ret));
+ link_uninit_dump_pcm(link);
+ return ret;
+ }
+ return 0;
+}
+
static void link_free(AVFilterLink **link)
{
@@
int avfilter_process_command(AVFilterContext *filter, const char *cmd,
const char *arg, char *res, int res_len, int flags)
{
@@
- if (res == local_res)
- av_log(filter, AV_LOG_INFO, "%s", res);
- return 0;
+ 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);
@@
/* ... existing code ... */
+ 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,
+ "PCM dump failed with %s\n", av_err2str(ret));
+ }
+
li->frame_blocked_in = li->frame_wanted_out = 0;
@@
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index a89d3cf658..0d4c91bb6b 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@
AVChannelLayout ch_layout; ///< channel layout of current buffer
+ int dump_pcm; ///< enable dumping on this link
+ int *dump_pcm_fds; ///< one FD per link (currently 1)
+ unsigned nb_dump_pcm_fds; ///< number of FDs
+ char *dump_pcm_path; ///< user-supplied directory
+
/**
* Define the time base used by the PTS of the frames/samples
* which will pass through this link.
--
2.34.1
---
From 8cf7bfa52a10be87e0f17b9783159b0e83f960f7 Mon Sep 17 00:00:00 2001
From: Yibo Fang <blueybf777@outlook.com>
Date: Wed, 23 Apr 2025 12:38:00 +0800
Subject: [PATCH 2/2] doc/filters: document dump_raw_start and dump_raw_stop
commands
Signed-off-by: Yibo Fang <blueybf777@outlook.com>
---
doc/filters.texi | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/doc/filters.texi b/doc/filters.texi
index 45b3e03e5e..6a2d23df3f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@
@section adumppcm
Dump raw PCM audio data for debugging between filters.
This filter allows writing raw audio data to disk from any audio link
in the filtergraph for inspection. It supports planar and packed formats,
automatically interleaving planar data for writing.
@subsection Syntax
@example
dump_raw_start=file=/your/dump/directory/
@end example
@subsection Usage
To enable dumping, use the @code{dump_raw_start} command:
@example
ffmpeg -i input.wav -af "volume,asendcmd='0.0 dump_raw_start file=/tmp/pcm/'" -f null -
@end example
To stop dumping:
@example
asendcmd='5.0 dump_raw_stop'
@end example
@subsection Options
@table @option
@item file
Specify a directory path (not a filename) where PCM data will be dumped.
The dumped file is named @code{<src>-<dst>.pcm} and will be overwritten if it already exists.
@end table
--
2.34.1
________________________________
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> on behalf of Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Sent: 26 April 2025 3:00
To: ffmpeg-devel@ffmpeg.org <ffmpeg-devel@ffmpeg.org>
Subject: Re: [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging
一只大 肥?:
> 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@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@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
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-05-26 7:57 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-04-23 6:02 [FFmpeg-devel] [PATCH] avfilter: add PCM dumping between filters for audio debugging 一只大 肥猫
2025-04-25 12:26 ` Nicolas George
2025-04-25 19:00 ` Andreas Rheinhardt
2025-05-26 7:43 ` [FFmpeg-devel] 回复: " Fang Yibo
2025-05-26 7:49 ` [FFmpeg-devel] " Yibo Fang
2025-05-26 7:52 ` Yibo Fang
2025-05-26 7:57 ` [FFmpeg-devel] [FFmpeg-devel, v3, 0/2] " Yibo Fang
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git