From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 8EF60401DE for ; Sun, 15 Feb 2026 18:31:32 +0000 (UTC) Authentication-Results: ffbox; dkim=fail (body hash mismatch (got b'a/ALLoms5c8niWdJNtCpwelRGOKmWeRTN8cbihvWE+w=', expected b'Rh1w4MnQO6v9zLcsqsaz6d0psUhBenEd/RQHYdC2zOc=')) header.d=ffmpeg.org header.i=@ffmpeg.org header.a=rsa-sha256 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1771180267; h=mime-version : to : date : message-id : reply-to : subject : list-id : list-archive : list-archive : list-help : list-owner : list-post : list-subscribe : list-unsubscribe : from : cc : content-type : content-transfer-encoding : from; bh=a/ALLoms5c8niWdJNtCpwelRGOKmWeRTN8cbihvWE+w=; b=fiRAk7h0wDCO87X3UozMOtbNSh3KbCDTdzX7oCu+z4VgoVOoXxGUN0XzIyXsYaQEW2cnx 33BxF8SRye+4ru0k1zb3I1651UQZpuhNYkudnq097y11oXejp3kHV1jHzEYY4EpxH/kamvA d3iLs03yAWOZaMQhhEs6Ro9SkS532uoA/XyzVGyi3fmSPHxgbTg6SmsDPhN12YutbB49AG3 cZ6uWMzV/udNnlRJgIVME+dwaM/StaSwR9h0Ll+ByOVKqOJomWJ31CuB8gAMAAP2sV8hDR+ quR22Aa839P/IgKXJNQbNlrJ3NuuFzmHuR6t7IvBgPBm2OQHFcKhSKEqpYpQ== Received: from [172.20.0.3] (unknown [172.20.0.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id A761D691353; Sun, 15 Feb 2026 20:31:07 +0200 (EET) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=ffmpeg.org; s=arc; t=1771180257; b=DM9qEFwVoJWejIB4xNCxfC1WqztZKwD6HT0Z8sg7cOU2tF2PV0cGqKjXbWNbTxR1DYeCF 676Mg7mnAOG9hOBX4HpH3t9nGFGXPFuIbICfvjiIccZxzrskYdJMskvrkvCyLcalKDlSmYe OFvnUt+C4GTB7j1Scqf5EP+txZ8xziA9sA7zOaBeugsT5DE5DLXEJZo4Fc83Oc7ta+AfSPb 5RuX4RE/a01/hx8q25ZF4vJoEfkjEECVux9gLddTyxuA47d5iVDMR2qnZKQz2U07R8TdOlw +Z4aZo1GUimxpZMHRx15XrV49FKnk3UNplZnho0fdxvTKWFR2w7HH8MHsovQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=ffmpeg.org; s=arc; t=1771180257; h=from : sender : reply-to : subject : date : message-id : to : cc : mime-version : content-type : content-transfer-encoding : content-id : content-description : resent-date : resent-from : resent-sender : resent-to : resent-cc : resent-message-id : in-reply-to : references : list-id : list-help : list-unsubscribe : list-subscribe : list-post : list-owner : list-archive; bh=8w+cmgLnI5i/jxNSJNcYUnUEf7r04t+pupiZhWp8DLs=; b=OVGheA3JjzctOYK67HRT+bpeIbphOxaa6fp8otQNxDCwPrAXMTIisQf+jtfNfrJTd5Ne0 GGHrONSEjT2AdSjKAJvQalwJ6oYIpI8m5K52KOWf6xbx2XeUdCAjR6bHsgnhzJkQM9HgwTF US/rXcJoPROaFUSdCEfv7JIxD1BEkQfeGDH+rPq+URhEjZwuDDP/Cne1hiG54iSDc6eQAYH BHh1lN/8t+SofRdg37NERGU75bUnLN4NGMWRfs5kKlMHXwbHUXiS63jopnfg6YAiR9tsKde rLkxtHTDuZaCuO+KIGdV2lhq/LapqPpomzba56mugPfUKPpIXON4g6sQLK6w== ARC-Authentication-Results: i=1; ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none; dmarc=pass header.from=ffmpeg.org policy.dmarc=quarantine Authentication-Results: ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none (Message is not ARC signed); dmarc=pass (Used From Domain Record) header.from=ffmpeg.org policy.dmarc=quarantine DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1771180250; h=content-type : mime-version : content-transfer-encoding : from : to : reply-to : subject : date : from; bh=Rh1w4MnQO6v9zLcsqsaz6d0psUhBenEd/RQHYdC2zOc=; b=iZq+j1AAVa02V2oJgVuHsGNyZzkOcuRQf3M6AtDpoK4G6NHrbD3Zv5a1wJIjiTUZ9akiK qGBuWMz+eK+kSjNLtWaW4fs8Dndx6Oa0sPn7wNzMdtBR4707DBHOZ5G1muln1PTMJAhgJUC lzREyxzhIS7Y2dLY0lAVnq/LMlichOiByDoR3BVyiCOx20UnCXjk4RkjPj3xlMFtDyLS2k2 2MTMKLr8yjl3L00cDkNCnZKzEJHW4n+HTYi9fiTC9jTt4iBIRojE3e6orpezBOP7lmtbaKV 9KdMbAOY1WXxKTYyiDf6IcwU2WpmRx6/V51TiqEzLd4r6ynNAPiZ9mjrLFWw== Received: from c8d966988b92 (code.ffmpeg.org [188.245.149.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id BCB646912F5 for ; Sun, 15 Feb 2026 20:30:50 +0200 (EET) MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Date: Sun, 15 Feb 2026 18:30:50 -0000 Message-ID: <177118025086.25.11196673470250710651@009cbcb3d8cd> Message-ID-Hash: J2AMCHXK23KZNXLQ3PC5Y6ISNLXCGHV5 X-Message-ID-Hash: J2AMCHXK23KZNXLQ3PC5Y6ISNLXCGHV5 X-MailFrom: code@ffmpeg.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-ffmpeg-devel.ffmpeg.org-0; header-match-ffmpeg-devel.ffmpeg.org-1; header-match-ffmpeg-devel.ffmpeg.org-2; header-match-ffmpeg-devel.ffmpeg.org-3; emergency; member-moderation X-Mailman-Version: 3.3.10 Precedence: list Reply-To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PR] avfilter/af_loudnorm: add stats_file option (PR #21766) List-Id: FFmpeg development discussions and patches Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Adam Jensen via ffmpeg-devel Cc: Adam Jensen Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Archived-At: List-Archive: List-Post: PR #21766 opened by Adam Jensen (acj) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21766 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21766.patch loudnorm provides stats output that's meant to be used for two-pass normalization. These stats are often interleaved with ffmpeg's stream descriptions and other output, making them difficult to parse and pass to the second pass. The new stats_file option enables writing the stats to a separate file, or to standard output, for simple parsing and other programmatic usage. Signed-off-by: Adam Jensen >>From 9437d3e688fd48b9163e3d649eec500734ee6294 Mon Sep 17 00:00:00 2001 From: Adam Jensen Date: Sun, 15 Feb 2026 11:28:25 -0500 Subject: [PATCH] avfilter/af_loudnorm: add stats_file option loudnorm provides stats output that's meant to be used for two-pass normalization. These stats are often interleaved with ffmpeg's stream descriptions and other output, making them difficult to parse and pass to the second pass. The new stats_file option enables writing the stats to a separate file, or to standard output, for simple parsing and other programmatic usage. Signed-off-by: Adam Jensen --- doc/filters.texi | 6 ++++++ libavfilter/af_loudnorm.c | 41 +++++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 80a1bda322..9f0218f33a 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -5914,6 +5914,12 @@ Options are true or false. Default is false. @item print_format Set print format for stats. Options are summary, json, or none. Default value is none. + +@item stats_file +If specified, the filter will use the named file to save the stats. The +format of the file is controlled by @option{print_format}, defaulting to +json. When @code{-} is given as the filename, stats are written to standard +output. @end table @section lowpass diff --git a/libavfilter/af_loudnorm.c b/libavfilter/af_loudnorm.c index 2636f2245e..23006edb56 100644 --- a/libavfilter/af_loudnorm.c +++ b/libavfilter/af_loudnorm.c @@ -20,6 +20,8 @@ /* http://k.ylo.ph/2016/04/04/loudnorm.html */ +#include "libavutil/avstring.h" +#include "libavutil/file_open.h" #include "libavutil/mem.h" #include "libavutil/opt.h" #include "avfilter.h" @@ -64,6 +66,7 @@ typedef struct LoudNormContext { int linear; int dual_mono; enum PrintFormat print_format; + char *stats_file_str; double *buf; int buf_size; @@ -121,6 +124,7 @@ static const AVOption loudnorm_options[] = { { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NONE}, 0, 0, FLAGS, .unit = "print_format" }, { "json", 0, 0, AV_OPT_TYPE_CONST, {.i64 = JSON}, 0, 0, FLAGS, .unit = "print_format" }, { "summary", 0, 0, AV_OPT_TYPE_CONST, {.i64 = SUMMARY}, 0, 0, FLAGS, .unit = "print_format" }, + { "stats_file", "set stats output file", OFFSET(stats_file_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, { NULL } }; @@ -824,6 +828,8 @@ static av_cold void uninit(AVFilterContext *ctx) LoudNormContext *s = ctx->priv; double i_in, i_out, lra_in, lra_out, thresh_in, thresh_out, tp_in, tp_out; int c; + FILE *stats_file = NULL; + char *stats = NULL; if (!s->r128_in || !s->r128_out) goto end; @@ -848,13 +854,31 @@ static av_cold void uninit(AVFilterContext *ctx) tp_out = tmp; } + + if (s->stats_file_str) { + if (s->print_format == NONE) + s->print_format = JSON; + + if (!strcmp(s->stats_file_str, "-")) { + stats_file = stdout; + } else { + stats_file = avpriv_fopen_utf8(s->stats_file_str, "w"); + if (!stats_file) { + int err = AVERROR(errno); + av_log(ctx, AV_LOG_ERROR, "Could not open stats file %s: %s\n", + s->stats_file_str, av_err2str(err)); + goto end; + } + } + } + switch(s->print_format) { case NONE: break; case JSON: - av_log(ctx, AV_LOG_INFO, - "\n{\n" + stats = av_asprintf( + "{\n" "\t\"input_i\" : \"%.2f\",\n" "\t\"input_tp\" : \"%.2f\",\n" "\t\"input_lra\" : \"%.2f\",\n" @@ -877,11 +901,13 @@ static av_cold void uninit(AVFilterContext *ctx) s->frame_type == LINEAR_MODE ? "linear" : "dynamic", s->target_i - i_out ); + av_log(ctx, AV_LOG_INFO, "\n%s", stats); + if (stats_file) + fprintf(stats_file, "%s", stats); break; case SUMMARY: - av_log(ctx, AV_LOG_INFO, - "\n" + stats = av_asprintf( "Input Integrated: %+6.1f LUFS\n" "Input True Peak: %+6.1f dBTP\n" "Input LRA: %6.1f LU\n" @@ -905,10 +931,17 @@ static av_cold void uninit(AVFilterContext *ctx) s->frame_type == LINEAR_MODE ? "Linear" : "Dynamic", s->target_i - i_out ); + av_log(ctx, AV_LOG_INFO, "\n%s", stats); + if (stats_file) + fprintf(stats_file, "%s", stats); break; } end: + if (stats) + av_free(stats); + if (stats_file && stats_file != stdout) + fclose(stats_file); if (s->r128_in) ff_ebur128_destroy(&s->r128_in); if (s->r128_out) -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org