PR #24042 opened by anders-mjoll
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24042
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24042.patch

# Summary of changes

Adds a `file` option to `showinfo` and `ashowinfo` so their per-frame output can
be written to an avio destination instead of the log, mirroring the `file`
option the `metadata`/`ametadata` filters already have. In the log the output is
interleaved with unrelated messages and gated by the global log level, which
makes it impractical to consume programmatically. With `file` set, the output
goes to a plain file, `-` for standard output, or any writable URL, and is
flushed after every frame so a second process can follow it while filtering is
still running. Behaviour is unchanged when the option is unset.

* `libavfilter/vf_showinfo.c`, `libavfilter/af_ashowinfo.c`: the per-frame
  `av_log(ctx, AV_LOG_INFO, ...)` calls now go through a `write_info()` helper,
  which writes to the AVIOContext when `file` is set and falls back to
  `av_vlog()` otherwise.
* Side-data dumps that used to abort a half-written line with an
  `AV_LOG_ERROR`/`AV_LOG_WARNING` message now write that text into the same
  stream. Splitting one logical line across two log levels never produced
  coherent output — under a raised log level you got the fragment without its
  context — and with `file` set the reason would land in a different sink than
  the line it belongs to.
* Write failures are reported but deliberately not fatal: the information stream
  is diagnostic, so losing its consumer should not abort filtering.
* `configure`: declare the `avformat` dependency for both filters, as they now
  use avio.
* `doc/filters.texi`: document the option for both filters.

Testing: exercised with a plain file, `-`/`pipe:1` and `tcp://` destinations;
killing the reader mid-stream logs the write error and lets the transcode finis
built with `--disable-static --enable-shared` and checked `libavfilter.so` for
undefined symbols to validate the configure change; man and HTML documentation
build and render correctly.

Two notes for reviewers:

* As the option value is part of a filtergraph description, `:` in a URL has to
  be escaped (`file=tcp\\://127.0.0.1\\:42000`). This is mentioned in the
  documentation, next to the same caveat the `zmq` and `lv2` filters carry.
* `fg_create()` in `fftools/ffmpeg_filter.c` builds a throwaway graph to
  determine the kinds of inputs and outputs, so `init()` runs twice and the
  destination is opened, closed and reopened. This is invisible for a file, but
  listener that accepts a single connection will see two. The `metadata` filters
  behave the same way today, so no attempt is made to change it here.

<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).

Attached filenames must match the sample's filename:

```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->



From 09f619ed1dbb69319d3ceadb31c094e5fd37a040 Mon Sep 17 00:00:00 2001
From: Anders Rein <[email protected]>
Date: Fri, 7 Aug 2026 11:27:29 +0200
Subject: [PATCH] avfilter/{vf,af}_showinfo: add support for file output

showinfo and ashowinfo could so far only write their per-frame
information to the log, where it is interleaved with other logging and
subject to the global log level, which makes it inconvenient to consume
programmatically.

Add a file option to both filters that redirects the output to any
destination avio can write to: a plain file, "-" for standard output or
a URL such as tcp://. The destination is flushed after every frame, so
that another process can follow the output while filtering is still in
progress. When the option is unset, the information is written to the
log as before.

The option mirrors the file option of the metadata filters.
---
 configure                  |   4 +
 doc/filters.texi           |  22 ++
 libavfilter/af_ashowinfo.c | 202 +++++++++-----
 libavfilter/vf_showinfo.c  | 528 +++++++++++++++++++++----------------
 4 files changed, 455 insertions(+), 301 deletions(-)

diff --git a/configure b/configure
index f2896b81c7..e1b54ca874 100755
--- a/configure
+++ b/configure
@@ -4138,6 +4138,7 @@ libzmq_protocol_select="network"
 ametadata_filter_deps="avformat"
 amovie_filter_deps="avcodec avformat"
 aresample_filter_deps="swresample"
+ashowinfo_filter_deps="avformat"
 asr_filter_deps="pocketsphinx"
 ass_filter_deps="libass"
 avgblur_opencl_filter_deps="opencl"
@@ -4261,6 +4262,7 @@ select_filter_select="scene_sad"
 sharpness_vaapi_filter_deps="vaapi"
 showcqt_filter_deps="avformat swscale"
 showcqt_filter_suggest="libfontconfig libfreetype"
+showinfo_filter_deps="avformat"
 signature_filter_deps="gpl avcodec avformat"
 smartblur_filter_deps="gpl swscale"
 sobel_opencl_filter_deps="opencl"
@@ -8342,6 +8344,7 @@ enabled zlib && add_cppflags -DZLIB_CONST
 # conditional library dependencies, in any order
 enabled amovie_filter       && prepend avfilter_deps "avformat avcodec"
 enabled aresample_filter    && prepend avfilter_deps "swresample"
+enabled ashowinfo_filter    && prepend avfilter_deps "avformat"
 enabled cover_rect_filter   && prepend avfilter_deps "avformat avcodec"
 enabled ebur128_filter && enabled swresample && prepend avfilter_deps 
"swresample"
 enabled elbg_filter         && prepend avfilter_deps "avcodec"
@@ -8357,6 +8360,7 @@ enabled sab_filter          && prepend avfilter_deps 
"swscale"
 enabled scale_filter        && prepend avfilter_deps "swscale"
 enabled scale2ref_filter    && prepend avfilter_deps "swscale"
 enabled showcqt_filter      && prepend avfilter_deps "avformat swscale"
+enabled showinfo_filter     && prepend avfilter_deps "avformat"
 enabled signature_filter    && prepend avfilter_deps "avcodec avformat"
 enabled smartblur_filter    && prepend avfilter_deps "swscale"
 enabled spp_filter          && prepend avfilter_deps "avcodec"
diff --git a/doc/filters.texi b/doc/filters.texi
index 985a1615a9..505e030ed8 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -3222,6 +3222,19 @@ Set the output sample rate. Default is 44100 Hz.
 Show a line containing various information for each input audio frame.
 The input audio is not modified.
 
+This filter supports the following options:
+
+@table @option
+@item file
+Write the information to the given destination instead of the log. Besides a
+plain file name, any URL that can be written to is accepted, and ``-'' is a
+shorthand for standard output. The destination is flushed after every audio
+frame, so another process can read the information while the filter is still
+running. If this option is not set, the information is written to the log.
+Note that a URL usually contains ':' signs, which have to be escaped (see
+@ref{filtergraph escaping}).
+@end table
+
 The shown line contains a sequence of key/value pairs of the form
 @var{key}:@var{value}.
 
@@ -22453,6 +22466,15 @@ Calculate checksums of each plane. By default enabled.
 @item udu_sei_as_ascii
 Try to print user data unregistered SEI as ascii character when possible,
 in hex format otherwise.
+
+@item file
+Write the information to the given destination instead of the log. Besides a
+plain file name, any URL that can be written to is accepted, and ``-'' is a
+shorthand for standard output. The destination is flushed after every frame, so
+another process can read the information while the filter is still running. If
+this option is not set, the information is written to the log.
+Note that a URL usually contains ':' signs, which have to be escaped (see
+@ref{filtergraph escaping}).
 @end table
 
 The shown line contains a sequence of key/value pairs of the form
diff --git a/libavfilter/af_ashowinfo.c b/libavfilter/af_ashowinfo.c
index 2653d0d089..328f12607e 100644
--- a/libavfilter/af_ashowinfo.c
+++ b/libavfilter/af_ashowinfo.c
@@ -31,9 +31,11 @@
 #include "libavutil/downmix_info.h"
 #include "libavutil/iamf.h"
 #include "libavutil/mem.h"
+#include "libavutil/opt.h"
 #include "libavutil/replaygain.h"
 #include "libavutil/timestamp.h"
 #include "libavutil/samplefmt.h"
+#include "libavformat/avio.h"
 
 #include "libavcodec/defs.h"
 
@@ -42,39 +44,95 @@
 #include "filters.h"
 
 typedef struct AShowInfoContext {
+    const AVClass *class;
     /**
      * Scratch space for individual plane checksums for planar audio
      */
     uint32_t *plane_checksums;
+    char* file_str;
+    AVIOContext* avio_context;
 } AShowInfoContext;
 
+#define OFFSET(x) offsetof(AShowInfoContext, x)
+#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+
+static const AVOption ashowinfo_options[] = {
+    { "file", "Dump information to file instead of writing to log",
+        OFFSET(file_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AF },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(ashowinfo);
+
+static av_cold int init(AVFilterContext *ctx) {
+    int ret;
+    AShowInfoContext *s = ctx->priv;
+
+    if (s->file_str != NULL) {
+        if (!strcmp("-", s->file_str)) {
+            ret = avio_open(&s->avio_context, "pipe:1", AVIO_FLAG_WRITE);
+        } else {
+            ret = avio_open(&s->avio_context, s->file_str, AVIO_FLAG_WRITE);
+        }
+
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Could not open %s: %s\n",
+                   s->file_str, av_err2str(ret));
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
 static av_cold void uninit(AVFilterContext *ctx)
 {
+    int ret;
     AShowInfoContext *s = ctx->priv;
     av_freep(&s->plane_checksums);
+
+    if (s->avio_context) {
+        ret = avio_close(s->avio_context);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to close file: %s\n", 
s->file_str);
+        }
+    }
 }
 
+static void write_info(AVFilterContext *ctx, const char *fmt, ...) {
+    AShowInfoContext *s = ctx->priv;
+    va_list vl;
+    va_start(vl, fmt);
+    if (s->avio_context != NULL) {
+        avio_vprintf(s->avio_context, fmt, vl);
+    } else {
+        av_vlog(ctx, AV_LOG_INFO, fmt, vl);
+    }
+    va_end(vl);
+}
+
+
 static void dump_matrixenc(AVFilterContext *ctx, AVFrameSideData *sd)
 {
     enum AVMatrixEncoding enc;
 
-    av_log(ctx, AV_LOG_INFO, "matrix encoding: ");
+    write_info(ctx, "matrix encoding: ");
 
     if (sd->size < sizeof(enum AVMatrixEncoding)) {
-        av_log(ctx, AV_LOG_INFO, "invalid data");
+        write_info(ctx, "invalid data");
         return;
     }
 
     enc = *(enum AVMatrixEncoding *)sd->data;
     switch (enc) {
-    case AV_MATRIX_ENCODING_NONE:           av_log(ctx, AV_LOG_INFO, "none");  
              break;
-    case AV_MATRIX_ENCODING_DOLBY:          av_log(ctx, AV_LOG_INFO, "Dolby 
Surround");      break;
-    case AV_MATRIX_ENCODING_DPLII:          av_log(ctx, AV_LOG_INFO, "Dolby 
Pro Logic II");  break;
-    case AV_MATRIX_ENCODING_DPLIIX:         av_log(ctx, AV_LOG_INFO, "Dolby 
Pro Logic IIx"); break;
-    case AV_MATRIX_ENCODING_DPLIIZ:         av_log(ctx, AV_LOG_INFO, "Dolby 
Pro Logic IIz"); break;
-    case AV_MATRIX_ENCODING_DOLBYEX:        av_log(ctx, AV_LOG_INFO, "Dolby 
EX");            break;
-    case AV_MATRIX_ENCODING_DOLBYHEADPHONE: av_log(ctx, AV_LOG_INFO, "Dolby 
Headphone");     break;
-    default:                                av_log(ctx, AV_LOG_WARNING, 
"unknown");          break;
+    case AV_MATRIX_ENCODING_NONE:           write_info(ctx, "none");           
     break;
+    case AV_MATRIX_ENCODING_DOLBY:          write_info(ctx, "Dolby Surround"); 
     break;
+    case AV_MATRIX_ENCODING_DPLII:          write_info(ctx, "Dolby Pro Logic 
II");  break;
+    case AV_MATRIX_ENCODING_DPLIIX:         write_info(ctx, "Dolby Pro Logic 
IIx"); break;
+    case AV_MATRIX_ENCODING_DPLIIZ:         write_info(ctx, "Dolby Pro Logic 
IIz"); break;
+    case AV_MATRIX_ENCODING_DOLBYEX:        write_info(ctx, "Dolby EX");       
     break;
+    case AV_MATRIX_ENCODING_DOLBYHEADPHONE: write_info(ctx, "Dolby 
Headphone");     break;
+    default:                                write_info(ctx, "unknown");        
  break;
     }
 }
 
@@ -82,23 +140,23 @@ static void dump_downmix(AVFilterContext *ctx, 
AVFrameSideData *sd)
 {
     AVDownmixInfo *di;
 
-    av_log(ctx, AV_LOG_INFO, "downmix: ");
+    write_info(ctx, "downmix: ");
     if (sd->size < sizeof(*di)) {
-        av_log(ctx, AV_LOG_INFO, "invalid data");
+        write_info(ctx, "invalid data");
         return;
     }
 
     di = (AVDownmixInfo *)sd->data;
 
-    av_log(ctx, AV_LOG_INFO, "preferred downmix type - ");
+    write_info(ctx, "preferred downmix type - ");
     switch (di->preferred_downmix_type) {
-    case AV_DOWNMIX_TYPE_LORO:    av_log(ctx, AV_LOG_INFO, "Lo/Ro");           
   break;
-    case AV_DOWNMIX_TYPE_LTRT:    av_log(ctx, AV_LOG_INFO, "Lt/Rt");           
   break;
-    case AV_DOWNMIX_TYPE_DPLII:   av_log(ctx, AV_LOG_INFO, "Dolby Pro Logic 
II"); break;
-    default:                      av_log(ctx, AV_LOG_WARNING, "unknown");      
   break;
+    case AV_DOWNMIX_TYPE_LORO:    write_info(ctx, "Lo/Ro");              break;
+    case AV_DOWNMIX_TYPE_LTRT:    write_info(ctx, "Lt/Rt");              break;
+    case AV_DOWNMIX_TYPE_DPLII:   write_info(ctx, "Dolby Pro Logic II"); break;
+    default:                      write_info(ctx, "unknown");         break;
     }
 
-    av_log(ctx, AV_LOG_INFO, " Mix levels: center %f (%f ltrt) - "
+    write_info(ctx, " Mix levels: center %f (%f ltrt) - "
            "surround %f (%f ltrt) - lfe %f",
            di->center_mix_level, di->center_mix_level_ltrt,
            di->surround_mix_level, di->surround_mix_level_ltrt,
@@ -117,57 +175,57 @@ static void dump_downmix_matrix(AVFilterContext *ctx, 
AVFrameSideData *sd, AVCha
     AVDownmixMatrix *dm = (AVDownmixMatrix *)sd->data;
     char buf[128];
 
-    av_log(ctx, AV_LOG_INFO, "downmix matrix: ");
+    write_info(ctx, "downmix matrix: ");
     if (dm->in_ch_count != ch_layout->nb_channels) {
-        av_log(ctx, AV_LOG_INFO, "invalid data");
+        write_info(ctx, "invalid data");
         return;
     }
 
-    av_log(ctx, AV_LOG_INFO, "downmix type - ");
+    write_info(ctx, "downmix type - ");
     switch (dm->downmix_type) {
-    case AV_DOWNMIX_TYPE_LORO:    av_log(ctx, AV_LOG_INFO, "Lo/Ro\n");         
     break;
-    case AV_DOWNMIX_TYPE_LTRT:    av_log(ctx, AV_LOG_INFO, "Lt/Rt\n");         
     break;
-    case AV_DOWNMIX_TYPE_DPLII:   av_log(ctx, AV_LOG_INFO, "Dolby Pro Logic 
II\n"); break;
-    default:                      av_log(ctx, AV_LOG_WARNING, "invalid data"); 
    return;
+    case AV_DOWNMIX_TYPE_LORO:    write_info(ctx, "Lo/Ro\n");              
break;
+    case AV_DOWNMIX_TYPE_LTRT:    write_info(ctx, "Lt/Rt\n");              
break;
+    case AV_DOWNMIX_TYPE_DPLII:   write_info(ctx, "Dolby Pro Logic II\n"); 
break;
+    default:                      write_info(ctx, "invalid data");     return;
     }
 
     for (int i = 0; i < downmix_type_map[dm->downmix_type]; i++) {
-        av_log(ctx, AV_LOG_INFO, "[%s] = { ", i ? "FR" : "FL");
+        write_info(ctx, "[%s] = { ", i ? "FR" : "FL");
         for (int j = 0; j < dm->in_ch_count; j++) {
             av_channel_name(buf, sizeof(buf), 
av_channel_layout_channel_from_index(ch_layout, j));
-            av_log(ctx, AV_LOG_INFO, ".%s = %f, ", buf, 
*av_downmix_matrix_coeff(dm, i, j));
+            write_info(ctx, ".%s = %f, ", buf, *av_downmix_matrix_coeff(dm, i, 
j));
         }
-        av_log(ctx, AV_LOG_INFO, "}%s", i == 
downmix_type_map[dm->downmix_type] - 1 ? "" : ",\n");
+        write_info(ctx, "}%s", i == downmix_type_map[dm->downmix_type] - 1 ? 
"" : ",\n");
     }
 }
 
 static void print_gain(AVFilterContext *ctx, const char *str, int32_t gain)
 {
-    av_log(ctx, AV_LOG_INFO, "%s - ", str);
+    write_info(ctx, "%s - ", str);
     if (gain == INT32_MIN)
-        av_log(ctx, AV_LOG_INFO, "unknown");
+        write_info(ctx, "unknown");
     else
-        av_log(ctx, AV_LOG_INFO, "%f", gain / 100000.0f);
-    av_log(ctx, AV_LOG_INFO, ", ");
+        write_info(ctx, "%f", gain / 100000.0f);
+    write_info(ctx, ", ");
 }
 
 static void print_peak(AVFilterContext *ctx, const char *str, uint32_t peak)
 {
-    av_log(ctx, AV_LOG_INFO, "%s - ", str);
+    write_info(ctx, "%s - ", str);
     if (!peak)
-        av_log(ctx, AV_LOG_INFO, "unknown");
+        write_info(ctx, "unknown");
     else
-        av_log(ctx, AV_LOG_INFO, "%f", peak / 100000.0f);
-    av_log(ctx, AV_LOG_INFO, ", ");
+        write_info(ctx, "%f", peak / 100000.0f);
+    write_info(ctx, ", ");
 }
 
 static void dump_replaygain(AVFilterContext *ctx, AVFrameSideData *sd)
 {
     AVReplayGain *rg;
 
-    av_log(ctx, AV_LOG_INFO, "replaygain: ");
+    write_info(ctx, "replaygain: ");
     if (sd->size < sizeof(*rg)) {
-        av_log(ctx, AV_LOG_INFO, "invalid data");
+        write_info(ctx, "invalid data");
         return;
     }
     rg = (AVReplayGain*)sd->data;
@@ -182,23 +240,23 @@ static void dump_audio_service_type(AVFilterContext *ctx, 
AVFrameSideData *sd)
 {
     enum AVAudioServiceType *ast;
 
-    av_log(ctx, AV_LOG_INFO, "audio service type: ");
+    write_info(ctx, "audio service type: ");
     if (sd->size < sizeof(*ast)) {
-        av_log(ctx, AV_LOG_INFO, "invalid data");
+        write_info(ctx, "invalid data");
         return;
     }
     ast = (enum AVAudioServiceType*)sd->data;
     switch (*ast) {
-    case AV_AUDIO_SERVICE_TYPE_MAIN:              av_log(ctx, AV_LOG_INFO, 
"Main Audio Service"); break;
-    case AV_AUDIO_SERVICE_TYPE_EFFECTS:           av_log(ctx, AV_LOG_INFO, 
"Effects");            break;
-    case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED: av_log(ctx, AV_LOG_INFO, 
"Visually Impaired");  break;
-    case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED:  av_log(ctx, AV_LOG_INFO, 
"Hearing Impaired");   break;
-    case AV_AUDIO_SERVICE_TYPE_DIALOGUE:          av_log(ctx, AV_LOG_INFO, 
"Dialogue");           break;
-    case AV_AUDIO_SERVICE_TYPE_COMMENTARY:        av_log(ctx, AV_LOG_INFO, 
"Commentary");         break;
-    case AV_AUDIO_SERVICE_TYPE_EMERGENCY:         av_log(ctx, AV_LOG_INFO, 
"Emergency");          break;
-    case AV_AUDIO_SERVICE_TYPE_VOICE_OVER:        av_log(ctx, AV_LOG_INFO, 
"Voice Over");         break;
-    case AV_AUDIO_SERVICE_TYPE_KARAOKE:           av_log(ctx, AV_LOG_INFO, 
"Karaoke");            break;
-    default:                                      av_log(ctx, AV_LOG_INFO, 
"unknown");            break;
+    case AV_AUDIO_SERVICE_TYPE_MAIN:              write_info(ctx, "Main Audio 
Service"); break;
+    case AV_AUDIO_SERVICE_TYPE_EFFECTS:           write_info(ctx, "Effects");  
          break;
+    case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED: write_info(ctx, "Visually 
Impaired");  break;
+    case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED:  write_info(ctx, "Hearing 
Impaired");   break;
+    case AV_AUDIO_SERVICE_TYPE_DIALOGUE:          write_info(ctx, "Dialogue"); 
          break;
+    case AV_AUDIO_SERVICE_TYPE_COMMENTARY:        write_info(ctx, 
"Commentary");         break;
+    case AV_AUDIO_SERVICE_TYPE_EMERGENCY:         write_info(ctx, 
"Emergency");          break;
+    case AV_AUDIO_SERVICE_TYPE_VOICE_OVER:        write_info(ctx, "Voice 
Over");         break;
+    case AV_AUDIO_SERVICE_TYPE_KARAOKE:           write_info(ctx, "Karaoke");  
          break;
+    default:                                      write_info(ctx, "unknown");  
          break;
     }
 }
 
@@ -208,21 +266,21 @@ static void 
dump_iamf_parameter_definition(AVFilterContext *ctx, const AVFrameSi
 
     switch (param->type) {
         case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN:
-            av_log(ctx, AV_LOG_INFO, "iamf mix gain parameters: ");
+            write_info(ctx, "iamf mix gain parameters: ");
             break;
         case AV_IAMF_PARAMETER_DEFINITION_DEMIXING:
-            av_log(ctx, AV_LOG_INFO, "iamf demixing parameters: ");
+            write_info(ctx, "iamf demixing parameters: ");
             break;
         case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN:
-            av_log(ctx, AV_LOG_INFO, "iamf recon gain parameters: ");
+            write_info(ctx, "iamf recon gain parameters: ");
             break;
         default:
-            av_log(ctx, AV_LOG_ERROR, "unknown iamf parameter definition type: 
%d",
+            write_info(ctx, "unknown iamf parameter definition type: %d",
                 param->type);
             return;
     }
 
-    av_log(ctx, AV_LOG_INFO,
+    write_info(ctx,
         "nb_subblocks=%d, "
         "parameter_id=%d, "
         "parameter_rate=%d, "
@@ -238,11 +296,11 @@ static void 
dump_iamf_parameter_definition(AVFilterContext *ctx, const AVFrameSi
     for (unsigned i = 0; i < param->nb_subblocks; i++) {
         const void *subblock = av_iamf_param_definition_get_subblock(param, i);
 
-        av_log(ctx, AV_LOG_INFO, " subblock[%d]={ ", i);
+        write_info(ctx, " subblock[%d]={ ", i);
         switch (param->type) {
             case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
                 const AVIAMFMixGain *mix = subblock;
-                av_log(ctx, AV_LOG_INFO,
+                write_info(ctx,
                     "subblock_duration=%d, "
                     "animation_type=%d, "
                     "start_point_value=%d/%d, "
@@ -260,7 +318,7 @@ static void dump_iamf_parameter_definition(AVFilterContext 
*ctx, const AVFrameSi
             }
             case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
                 const AVIAMFDemixingInfo *demix = subblock;
-                av_log(ctx, AV_LOG_INFO,
+                write_info(ctx,
                     "subblock_duration=%d, "
                     "dmixp_mode=%d",
                     demix->subblock_duration,
@@ -270,20 +328,20 @@ static void 
dump_iamf_parameter_definition(AVFilterContext *ctx, const AVFrameSi
             }
             case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
                 const AVIAMFReconGain *recon = subblock;
-                av_log(ctx, AV_LOG_INFO,
+                write_info(ctx,
                     "subblock_duration=%d", recon->subblock_duration
                 );
                 break;
             }
         }
-        av_log(ctx, AV_LOG_INFO, (i == param->nb_subblocks - 1) ? " }" : " 
},");
+        write_info(ctx, (i == param->nb_subblocks - 1) ? " }" : " },");
     }
 
 }
 
 static void dump_unknown(AVFilterContext *ctx, AVFrameSideData *sd)
 {
-    av_log(ctx, AV_LOG_INFO, "unknown side data type: %d, size "
+    write_info(ctx, "unknown side data type: %d, size "
            "%zu bytes", sd->type, sd->size);
 }
 
@@ -316,7 +374,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
 
     av_channel_layout_describe(&buf->ch_layout, chlayout_str, 
sizeof(chlayout_str));
 
-    av_log(ctx, AV_LOG_INFO,
+    write_info(ctx,
            "n:%"PRId64" pts:%s pts_time:%s "
            "fmt:%s channels:%d chlayout:%s rate:%d nb_samples:%d "
            "checksum:%08"PRIX32" ",
@@ -326,15 +384,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame 
*buf)
            buf->sample_rate, buf->nb_samples,
            checksum);
 
-    av_log(ctx, AV_LOG_INFO, "plane_checksums: [ ");
+    write_info(ctx, "plane_checksums: [ ");
     for (i = 0; i < planes; i++)
-        av_log(ctx, AV_LOG_INFO, "%08"PRIX32" ", s->plane_checksums[i]);
-    av_log(ctx, AV_LOG_INFO, "]\n");
+        write_info(ctx, "%08"PRIX32" ", s->plane_checksums[i]);
+    write_info(ctx, "]\n");
 
     for (i = 0; i < buf->nb_side_data; i++) {
         AVFrameSideData *sd = buf->side_data[i];
 
-        av_log(ctx, AV_LOG_INFO, "  side data - ");
+        write_info(ctx, "  side data - ");
         switch (sd->type) {
         case AV_FRAME_DATA_MATRIXENCODING: dump_matrixenc (ctx, sd); break;
         case AV_FRAME_DATA_DOWNMIX_INFO:   dump_downmix   (ctx, sd); break;
@@ -349,7 +407,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
         default:                           dump_unknown   (ctx, sd); break;
         }
 
-        av_log(ctx, AV_LOG_INFO, "\n");
+        write_info(ctx, "\n");
+    }
+
+    if (s->avio_context) {
+        avio_flush(s->avio_context);
+        if (s->avio_context->error < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to write to %s: %s\n",
+                   s->file_str, av_err2str(s->avio_context->error));
+        }
     }
 
     return ff_filter_frame(inlink->dst->outputs[0], buf);
@@ -366,8 +432,10 @@ static const AVFilterPad inputs[] = {
 const FFFilter ff_af_ashowinfo = {
     .p.name        = "ashowinfo",
     .p.description = NULL_IF_CONFIG_SMALL("Show textual information for each 
audio frame."),
+    .p.priv_class  = &ashowinfo_class,
     .p.flags       = AVFILTER_FLAG_METADATA_ONLY,
     .priv_size   = sizeof(AShowInfoContext),
+    .init        = init,
     .uninit      = uninit,
     FILTER_INPUTS(inputs),
     FILTER_OUTPUTS(ff_audio_default_filterpad),
diff --git a/libavfilter/vf_showinfo.c b/libavfilter/vf_showinfo.c
index 4ac3d45dc0..0c6d855873 100644
--- a/libavfilter/vf_showinfo.c
+++ b/libavfilter/vf_showinfo.c
@@ -46,6 +46,7 @@
 #include "libavutil/detection_bbox.h"
 #include "libavutil/ambient_viewing_environment.h"
 #include "libavutil/uuid.h"
+#include "libavformat/avio.h"
 
 #include "avfilter.h"
 #include "filters.h"
@@ -55,6 +56,8 @@ typedef struct ShowInfoContext {
     const AVClass *class;
     int calculate_checksums;
     int udu_sei_as_ascii;
+    char* file_str;
+    AVIOContext* avio_context;
 } ShowInfoContext;
 
 #define OFFSET(x) offsetof(ShowInfoContext, x)
@@ -64,33 +67,47 @@ static const AVOption showinfo_options[] = {
     { "checksum", "calculate checksums", OFFSET(calculate_checksums), 
AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, VF },
     { "udu_sei_as_ascii", "try to print user data unregistered SEI as ascii 
character when possible",
         OFFSET(udu_sei_as_ascii), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VF },
+    { "file", "Dump information to file instead of writing to log",
+        OFFSET(file_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VF },
     { NULL }
 };
 
 AVFILTER_DEFINE_CLASS(showinfo);
 
+static void write_info(AVFilterContext *ctx, const char *fmt, ...) {
+    ShowInfoContext *s = ctx->priv;
+    va_list vl;
+    va_start(vl, fmt);
+    if (s->avio_context != NULL) {
+        avio_vprintf(s->avio_context, fmt, vl);
+    } else {
+        av_vlog(ctx, AV_LOG_INFO, fmt, vl);
+    }
+    va_end(vl);
+}
+
 static void dump_spherical(AVFilterContext *ctx, AVFrame *frame, const 
AVFrameSideData *sd)
 {
     const AVSphericalMapping *spherical = (const AVSphericalMapping *)sd->data;
     double yaw, pitch, roll;
 
-    av_log(ctx, AV_LOG_INFO, "%s ", 
av_spherical_projection_name(spherical->projection));
+    write_info(ctx, "%s ", 
av_spherical_projection_name(spherical->projection));
 
     if (spherical->yaw || spherical->pitch || spherical->roll) {
         yaw = ((double)spherical->yaw) / (1 << 16);
         pitch = ((double)spherical->pitch) / (1 << 16);
         roll = ((double)spherical->roll) / (1 << 16);
-        av_log(ctx, AV_LOG_INFO, "(%f/%f/%f) ", yaw, pitch, roll);
+        write_info(ctx, "(%f/%f/%f) ", yaw, pitch, roll);
     }
 
     if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) {
         size_t l, t, r, b;
         av_spherical_tile_bounds(spherical, frame->width, frame->height,
                                  &l, &t, &r, &b);
-        av_log(ctx, AV_LOG_INFO, "[%zu, %zu, %zu, %zu] ",
+        write_info(ctx, "[%zu, %zu, %zu, %zu] ",
                l, t, r, b);
     } else if (spherical->projection == AV_SPHERICAL_CUBEMAP) {
-        av_log(ctx, AV_LOG_INFO, "[pad %"PRIu32"] ", spherical->padding);
+        write_info(ctx, "[pad %"PRIu32"] ", spherical->padding);
     }
 }
 
@@ -98,20 +115,20 @@ static void dump_stereo3d(AVFilterContext *ctx, const 
AVFrameSideData *sd)
 {
     const AVStereo3D *stereo = (const AVStereo3D *)sd->data;
 
-    av_log(ctx, AV_LOG_INFO, "type - %s", av_stereo3d_type_name(stereo->type));
+    write_info(ctx, "type - %s", av_stereo3d_type_name(stereo->type));
 
     if (stereo->flags & AV_STEREO3D_FLAG_INVERT)
-        av_log(ctx, AV_LOG_INFO, " (inverted)");
+        write_info(ctx, " (inverted)");
 
-    av_log(ctx, AV_LOG_INFO, ", view - %s, primary_eye - %s", 
av_stereo3d_view_name(stereo->view),
+    write_info(ctx, ", view - %s, primary_eye - %s", 
av_stereo3d_view_name(stereo->view),
                                                               
av_stereo3d_primary_eye_name(stereo->primary_eye));
     if (stereo->baseline)
-        av_log(ctx, AV_LOG_INFO, ", baseline: %"PRIu32"", stereo->baseline);
+        write_info(ctx, ", baseline: %"PRIu32"", stereo->baseline);
     if (stereo->horizontal_disparity_adjustment.num && 
stereo->horizontal_disparity_adjustment.den)
-        av_log(ctx, AV_LOG_INFO, ", horizontal_disparity_adjustment: %0.4f",
+        write_info(ctx, ", horizontal_disparity_adjustment: %0.4f",
                av_q2d(stereo->horizontal_disparity_adjustment));
     if (stereo->horizontal_field_of_view.num && 
stereo->horizontal_field_of_view.den)
-        av_log(ctx, AV_LOG_INFO, ", horizontal_field_of_view: %0.3f", 
av_q2d(stereo->horizontal_field_of_view));
+        write_info(ctx, ", horizontal_field_of_view: %0.3f", 
av_q2d(stereo->horizontal_field_of_view));
 }
 
 static void dump_s12m_timecode(AVFilterContext *ctx, AVFilterLink *inlink, 
const AVFrameSideData *sd)
@@ -120,14 +137,14 @@ static void dump_s12m_timecode(AVFilterContext *ctx, 
AVFilterLink *inlink, const
     const uint32_t *tc = (const uint32_t *)sd->data;
 
     if ((sd->size != sizeof(uint32_t) * 4) || (tc[0] > 3)) {
-        av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+        write_info(ctx, "invalid data\n");
         return;
     }
 
     for (int j = 1; j <= tc[0]; j++) {
         char tcbuf[AV_TIMECODE_STR_SIZE];
         av_timecode_make_smpte_tc_string2(tcbuf, l->frame_rate, tc[j], 0, 0);
-        av_log(ctx, AV_LOG_INFO, "timecode - %s%s", tcbuf, j != tc[0]  ? ", " 
: "");
+        write_info(ctx, "timecode - %s%s", tcbuf, j != tc[0]  ? ", " : "");
     }
 }
 
@@ -140,14 +157,14 @@ static void dump_roi(AVFilterContext *ctx, const 
AVFrameSideData *sd)
     roi = (const AVRegionOfInterest *)sd->data;
     roi_size = roi->self_size;
     if (!roi_size || sd->size % roi_size != 0) {
-        av_log(ctx, AV_LOG_ERROR, "Invalid AVRegionOfInterest.self_size.\n");
+        write_info(ctx, "Invalid AVRegionOfInterest.self_size.\n");
         return;
     }
     nb_rois = sd->size / roi_size;
 
     for (int i = 0; i < nb_rois; i++) {
         roi = (const AVRegionOfInterest *)(sd->data + roi_size * i);
-        av_log(ctx, AV_LOG_INFO, "index: %d, region: (%d, %d) -> (%d, %d), qp 
offset: %d/%d.\n",
+        write_info(ctx, "index: %d, region: (%d, %d) -> (%d, %d), qp offset: 
%d/%d.\n",
                i, roi->left, roi->top, roi->right, roi->bottom, 
roi->qoffset.num, roi->qoffset.den);
     }
 }
@@ -157,7 +174,7 @@ static void dump_tdrdi(AVFilterContext *ctx, const 
AVFrameSideData *sd)
     const AV3DReferenceDisplaysInfo *tdrdi = (const AV3DReferenceDisplaysInfo 
*)sd->data;
 
 
-    av_log(ctx, AV_LOG_INFO, "number of reference displays: %u", 
tdrdi->num_ref_displays);
+    write_info(ctx, "number of reference displays: %u", 
tdrdi->num_ref_displays);
 }
 
 static void dump_detection_bbox(AVFilterContext *ctx, const AVFrameSideData 
*sd)
@@ -168,16 +185,16 @@ static void dump_detection_bbox(AVFilterContext *ctx, 
const AVFrameSideData *sd)
 
     header = (const AVDetectionBBoxHeader *)sd->data;
     nb_bboxes = header->nb_bboxes;
-    av_log(ctx, AV_LOG_INFO, "source: %s\n", header->source);
+    write_info(ctx, "source: %s\n", header->source);
 
     for (int i = 0; i < nb_bboxes; i++) {
         bbox = av_get_detection_bbox(header, i);
-        av_log(ctx, AV_LOG_INFO, "index: %d,\tregion: (%d, %d) -> (%d, %d), 
label: %s, confidence: %d/%d.\n",
+        write_info(ctx, "index: %d,\tregion: (%d, %d) -> (%d, %d), label: %s, 
confidence: %d/%d.\n",
                                  i, bbox->x, bbox->y, bbox->x + bbox->w, 
bbox->y + bbox->h,
                                  bbox->detect_label, 
bbox->detect_confidence.num, bbox->detect_confidence.den);
         if (bbox->classify_count > 0) {
             for (int j = 0; j < bbox->classify_count; j++) {
-                av_log(ctx, AV_LOG_INFO, "\t\tclassify:  label: %s, 
confidence: %d/%d.\n",
+                write_info(ctx, "\t\tclassify:  label: %s, confidence: 
%d/%d.\n",
                        bbox->classify_labels[j], 
bbox->classify_confidences[j].num, bbox->classify_confidences[j].den);
             }
         }
@@ -189,13 +206,13 @@ static void dump_mastering_display(AVFilterContext *ctx, 
const AVFrameSideData *
     const AVMasteringDisplayMetadata *mastering_display;
 
     if (sd->size < sizeof(*mastering_display)) {
-        av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+        write_info(ctx, "invalid data\n");
         return;
     }
 
     mastering_display = (const AVMasteringDisplayMetadata *)sd->data;
 
-    av_log(ctx, AV_LOG_INFO, "has_primaries:%d has_luminance:%d "
+    write_info(ctx, "has_primaries:%d has_luminance:%d "
            "r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f) "
            "min_luminance=%f, max_luminance=%f",
            mastering_display->has_primaries, mastering_display->has_luminance,
@@ -214,97 +231,97 @@ static void dump_dynamic_hdr_plus(AVFilterContext *ctx, 
AVFrameSideData *sd)
     AVDynamicHDRPlus *hdr_plus;
 
     if (sd->size < sizeof(*hdr_plus)) {
-        av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+        write_info(ctx, "invalid data\n");
         return;
     }
 
     hdr_plus = (AVDynamicHDRPlus *)sd->data;
-    av_log(ctx, AV_LOG_INFO, "application version: %d, ", 
hdr_plus->application_version);
-    av_log(ctx, AV_LOG_INFO, "num_windows: %d, ", hdr_plus->num_windows);
+    write_info(ctx, "application version: %d, ", 
hdr_plus->application_version);
+    write_info(ctx, "num_windows: %d, ", hdr_plus->num_windows);
     for (int w = 1; w < hdr_plus->num_windows; w++) {
         AVHDRPlusColorTransformParams *params = &hdr_plus->params[w];
-        av_log(ctx, AV_LOG_INFO, w > 1 ? ", window %d { " : "window %d { ", w);
-        av_log(ctx, AV_LOG_INFO, "window_upper_left_corner: (%5.4f,%5.4f),",
+        write_info(ctx, w > 1 ? ", window %d { " : "window %d { ", w);
+        write_info(ctx, "window_upper_left_corner: (%5.4f,%5.4f),",
                av_q2d(params->window_upper_left_corner_x),
                av_q2d(params->window_upper_left_corner_y));
-        av_log(ctx, AV_LOG_INFO, "window_lower_right_corner: (%5.4f,%5.4f), ",
+        write_info(ctx, "window_lower_right_corner: (%5.4f,%5.4f), ",
                av_q2d(params->window_lower_right_corner_x),
                av_q2d(params->window_lower_right_corner_y));
-        av_log(ctx, AV_LOG_INFO, "window_upper_left_corner: (%5.4f, %5.4f), ",
+        write_info(ctx, "window_upper_left_corner: (%5.4f, %5.4f), ",
                av_q2d(params->window_upper_left_corner_x),
                av_q2d(params->window_upper_left_corner_y));
-        av_log(ctx, AV_LOG_INFO, "center_of_ellipse_x: (%d,%d), ",
+        write_info(ctx, "center_of_ellipse_x: (%d,%d), ",
                params->center_of_ellipse_x,
                params->center_of_ellipse_y);
-        av_log(ctx, AV_LOG_INFO, "rotation_angle: %d, ",
+        write_info(ctx, "rotation_angle: %d, ",
                params->rotation_angle);
-        av_log(ctx, AV_LOG_INFO, "semimajor_axis_internal_ellipse: %d, ",
+        write_info(ctx, "semimajor_axis_internal_ellipse: %d, ",
                params->semimajor_axis_internal_ellipse);
-        av_log(ctx, AV_LOG_INFO, "semimajor_axis_external_ellipse: %d, ",
+        write_info(ctx, "semimajor_axis_external_ellipse: %d, ",
                params->semimajor_axis_external_ellipse);
-        av_log(ctx, AV_LOG_INFO, "semiminor_axis_external_ellipse: %d, ",
+        write_info(ctx, "semiminor_axis_external_ellipse: %d, ",
                params->semiminor_axis_external_ellipse);
-        av_log(ctx, AV_LOG_INFO, "overlap_process_option: %d}",
+        write_info(ctx, "overlap_process_option: %d}",
                params->overlap_process_option);
     }
-    av_log(ctx, AV_LOG_INFO, "targeted_system_display_maximum_luminance: 
%9.4f, ",
+    write_info(ctx, "targeted_system_display_maximum_luminance: %9.4f, ",
            av_q2d(hdr_plus->targeted_system_display_maximum_luminance));
     if (hdr_plus->targeted_system_display_actual_peak_luminance_flag) {
-        av_log(ctx, AV_LOG_INFO, 
"targeted_system_display_actual_peak_luminance: {");
+        write_info(ctx, "targeted_system_display_actual_peak_luminance: {");
         for (int i = 0; i < 
hdr_plus->num_rows_targeted_system_display_actual_peak_luminance; i++) {
-            av_log(ctx, AV_LOG_INFO, "(");
+            write_info(ctx, "(");
             for (int j = 0; j < 
hdr_plus->num_cols_targeted_system_display_actual_peak_luminance; j++) {
-                av_log(ctx, AV_LOG_INFO, i ? ",%5.4f" : "%5.4f",
+                write_info(ctx, i ? ",%5.4f" : "%5.4f",
                        
av_q2d(hdr_plus->targeted_system_display_actual_peak_luminance[i][j]));
             }
-            av_log(ctx, AV_LOG_INFO, ")");
+            write_info(ctx, ")");
         }
-        av_log(ctx, AV_LOG_INFO, "}, ");
+        write_info(ctx, "}, ");
     }
 
     for (int w = 0; w < hdr_plus->num_windows; w++) {
         AVHDRPlusColorTransformParams *params = &hdr_plus->params[w];
-        av_log(ctx, AV_LOG_INFO, "window %d {maxscl: {", w);
+        write_info(ctx, "window %d {maxscl: {", w);
         for (int i = 0; i < 3; i++) {
-            av_log(ctx, AV_LOG_INFO, i ? ",%5.4f" : 
"%5.4f",av_q2d(params->maxscl[i]));
+            write_info(ctx, i ? ",%5.4f" : "%5.4f",av_q2d(params->maxscl[i]));
         }
-        av_log(ctx, AV_LOG_INFO, "}, average_maxrgb: %5.4f, ",
+        write_info(ctx, "}, average_maxrgb: %5.4f, ",
                av_q2d(params->average_maxrgb));
-        av_log(ctx, AV_LOG_INFO, "distribution_maxrgb: {");
+        write_info(ctx, "distribution_maxrgb: {");
         for (int i = 0; i < params->num_distribution_maxrgb_percentiles; i++) {
-            av_log(ctx, AV_LOG_INFO, "(%d,%5.4f)",
+            write_info(ctx, "(%d,%5.4f)",
                    params->distribution_maxrgb[i].percentage,
                    av_q2d(params->distribution_maxrgb[i].percentile));
         }
-        av_log(ctx, AV_LOG_INFO, "}, fraction_bright_pixels: %5.4f",
+        write_info(ctx, "}, fraction_bright_pixels: %5.4f",
                av_q2d(params->fraction_bright_pixels));
         if (params->tone_mapping_flag) {
-            av_log(ctx, AV_LOG_INFO, ", knee_point: (%5.4f,%5.4f), ", 
av_q2d(params->knee_point_x), av_q2d(params->knee_point_y));
-            av_log(ctx, AV_LOG_INFO, "bezier_curve_anchors: {");
+            write_info(ctx, ", knee_point: (%5.4f,%5.4f), ", 
av_q2d(params->knee_point_x), av_q2d(params->knee_point_y));
+            write_info(ctx, "bezier_curve_anchors: {");
             for (int i = 0; i < params->num_bezier_curve_anchors; i++) {
-                av_log(ctx, AV_LOG_INFO, i ? ",%5.4f" : "%5.4f",
+                write_info(ctx, i ? ",%5.4f" : "%5.4f",
                        av_q2d(params->bezier_curve_anchors[i]));
             }
-            av_log(ctx, AV_LOG_INFO, "}");
+            write_info(ctx, "}");
         }
         if (params->color_saturation_mapping_flag) {
-            av_log(ctx, AV_LOG_INFO, ", color_saturation_weight: %5.4f",
+            write_info(ctx, ", color_saturation_weight: %5.4f",
                    av_q2d(params->color_saturation_weight));
         }
-        av_log(ctx, AV_LOG_INFO, "}");
+        write_info(ctx, "}");
     }
 
     if (hdr_plus->mastering_display_actual_peak_luminance_flag) {
-        av_log(ctx, AV_LOG_INFO, ", mastering_display_actual_peak_luminance: 
{");
+        write_info(ctx, ", mastering_display_actual_peak_luminance: {");
         for (int i = 0; i < 
hdr_plus->num_rows_mastering_display_actual_peak_luminance; i++) {
-            av_log(ctx, AV_LOG_INFO, "(");
+            write_info(ctx, "(");
             for (int j = 0; j <  
hdr_plus->num_cols_mastering_display_actual_peak_luminance; j++) {
-                av_log(ctx, AV_LOG_INFO, i ? ",%5.4f" : "%5.4f",
+                write_info(ctx, i ? ",%5.4f" : "%5.4f",
                        
av_q2d(hdr_plus->mastering_display_actual_peak_luminance[i][j]));
             }
-            av_log(ctx, AV_LOG_INFO, ")");
+            write_info(ctx, ")");
         }
-        av_log(ctx, AV_LOG_INFO, "}");
+        write_info(ctx, "}");
     }
 }
 
@@ -313,77 +330,77 @@ static void dump_dynamic_hdr_vivid(AVFilterContext *ctx, 
AVFrameSideData *sd)
     AVDynamicHDRVivid *hdr_vivid;
 
     if (sd->size < sizeof(*hdr_vivid)) {
-        av_log(ctx, AV_LOG_ERROR, "invalid hdr vivid data\n");
+        write_info(ctx, "invalid hdr vivid data\n");
         return;
     }
 
     hdr_vivid = (AVDynamicHDRVivid *)sd->data;
-    av_log(ctx, AV_LOG_INFO, "system_start_code: %d, ", 
hdr_vivid->system_start_code);
-    av_log(ctx, AV_LOG_INFO, "num_windows: %d, ", hdr_vivid->num_windows);
+    write_info(ctx, "system_start_code: %d, ", hdr_vivid->system_start_code);
+    write_info(ctx, "num_windows: %d, ", hdr_vivid->num_windows);
     for (int w = 0; w < hdr_vivid->num_windows; w++) {
         const AVHDRVividColorTransformParams *params = &hdr_vivid->params[w];
 
-        av_log(ctx, AV_LOG_INFO, "minimum_maxrgb[%d]: %.4f, ", w, 
av_q2d(params->minimum_maxrgb));
-        av_log(ctx, AV_LOG_INFO, "average_maxrgb[%d]: %.4f, ", w, 
av_q2d(params->average_maxrgb));
-        av_log(ctx, AV_LOG_INFO, "variance_maxrgb[%d]:%.4f, ", w, 
av_q2d(params->variance_maxrgb));
-        av_log(ctx, AV_LOG_INFO, "maximum_maxrgb[%d]: %.4f, ", w, 
av_q2d(params->maximum_maxrgb));
+        write_info(ctx, "minimum_maxrgb[%d]: %.4f, ", w, 
av_q2d(params->minimum_maxrgb));
+        write_info(ctx, "average_maxrgb[%d]: %.4f, ", w, 
av_q2d(params->average_maxrgb));
+        write_info(ctx, "variance_maxrgb[%d]:%.4f, ", w, 
av_q2d(params->variance_maxrgb));
+        write_info(ctx, "maximum_maxrgb[%d]: %.4f, ", w, 
av_q2d(params->maximum_maxrgb));
     }
 
     for (int w = 0; w < hdr_vivid->num_windows; w++) {
         const AVHDRVividColorTransformParams *params = &hdr_vivid->params[w];
 
-        av_log(ctx, AV_LOG_INFO, "tone_mapping_mode_flag[%d]: %d, ", w, 
params->tone_mapping_mode_flag);
-        av_log(ctx, AV_LOG_INFO, "tone_mapping_param_num[%d]: %d, ", w, 
params->tone_mapping_param_num);
+        write_info(ctx, "tone_mapping_mode_flag[%d]: %d, ", w, 
params->tone_mapping_mode_flag);
+        write_info(ctx, "tone_mapping_param_num[%d]: %d, ", w, 
params->tone_mapping_param_num);
         if (params->tone_mapping_mode_flag) {
             for (int i = 0; i < params->tone_mapping_param_num; i++) {
                 const AVHDRVividColorToneMappingParams *tm_params = 
&params->tm_params[i];
 
-                av_log(ctx, AV_LOG_INFO, 
"targeted_system_display_maximum_luminance[%d][%d]: %.4f, ",
+                write_info(ctx, 
"targeted_system_display_maximum_luminance[%d][%d]: %.4f, ",
                        w, i, 
av_q2d(tm_params->targeted_system_display_maximum_luminance));
-                av_log(ctx, AV_LOG_INFO, "base_enable_flag[%d][%d]: %d, ",
+                write_info(ctx, "base_enable_flag[%d][%d]: %d, ",
                        w, i, tm_params->base_enable_flag);
                 if (tm_params->base_enable_flag) {
-                    av_log(ctx, AV_LOG_INFO, "base_param_m_p[%d][%d]: %.4f, ", 
w, i, av_q2d(tm_params->base_param_m_p));
-                    av_log(ctx, AV_LOG_INFO, "base_param_m_m[%d][%d]: %.4f, ", 
w, i, av_q2d(tm_params->base_param_m_m));
-                    av_log(ctx, AV_LOG_INFO, "base_param_m_a[%d][%d]: %.4f, ", 
w, i, av_q2d(tm_params->base_param_m_a));
-                    av_log(ctx, AV_LOG_INFO, "base_param_m_b[%d][%d]: %.4f, ", 
w, i, av_q2d(tm_params->base_param_m_b));
-                    av_log(ctx, AV_LOG_INFO, "base_param_m_n[%d][%d]: %.4f, ", 
w, i, av_q2d(tm_params->base_param_m_n));
-                    av_log(ctx, AV_LOG_INFO, "base_param_k1[%d][%d]:  %d, ", 
w, i, tm_params->base_param_k1);
-                    av_log(ctx, AV_LOG_INFO, "base_param_k2[%d][%d]:  %d, ", 
w, i, tm_params->base_param_k2);
-                    av_log(ctx, AV_LOG_INFO, "base_param_k3[%d][%d]:  %d, ", 
w, i, tm_params->base_param_k3);
-                    av_log(ctx, AV_LOG_INFO, 
"base_param_Delta_enable_mode[%d][%d]: %d, ", w, i,
+                    write_info(ctx, "base_param_m_p[%d][%d]: %.4f, ", w, i, 
av_q2d(tm_params->base_param_m_p));
+                    write_info(ctx, "base_param_m_m[%d][%d]: %.4f, ", w, i, 
av_q2d(tm_params->base_param_m_m));
+                    write_info(ctx, "base_param_m_a[%d][%d]: %.4f, ", w, i, 
av_q2d(tm_params->base_param_m_a));
+                    write_info(ctx, "base_param_m_b[%d][%d]: %.4f, ", w, i, 
av_q2d(tm_params->base_param_m_b));
+                    write_info(ctx, "base_param_m_n[%d][%d]: %.4f, ", w, i, 
av_q2d(tm_params->base_param_m_n));
+                    write_info(ctx, "base_param_k1[%d][%d]:  %d, ", w, i, 
tm_params->base_param_k1);
+                    write_info(ctx, "base_param_k2[%d][%d]:  %d, ", w, i, 
tm_params->base_param_k2);
+                    write_info(ctx, "base_param_k3[%d][%d]:  %d, ", w, i, 
tm_params->base_param_k3);
+                    write_info(ctx, "base_param_Delta_enable_mode[%d][%d]: %d, 
", w, i,
                            tm_params->base_param_Delta_enable_mode);
-                    av_log(ctx, AV_LOG_INFO, "base_param_Delta[%d][%d]: %.4f, 
", w, i, av_q2d(tm_params->base_param_Delta));
+                    write_info(ctx, "base_param_Delta[%d][%d]: %.4f, ", w, i, 
av_q2d(tm_params->base_param_Delta));
                 }
-                av_log(ctx, AV_LOG_INFO, "3Spline_enable_flag[%d][%d]: %d, ",
+                write_info(ctx, "3Spline_enable_flag[%d][%d]: %d, ",
                        w, i, tm_params->three_Spline_enable_flag);
                 if (tm_params->three_Spline_enable_flag) {
                     for (int j = 0; j < tm_params->three_Spline_num; j++) {
                         const AVHDRVivid3SplineParams *three_spline = 
&tm_params->three_spline[j];
-                        av_log(ctx, AV_LOG_INFO, "3Spline_TH_mode[%d][%d]:  
%d, ", w, i, three_spline->th_mode);
+                        write_info(ctx, "3Spline_TH_mode[%d][%d]:  %d, ", w, 
i, three_spline->th_mode);
                         if (three_spline->th_mode == 0 || 
three_spline->th_mode == 2)
-                            av_log(ctx, AV_LOG_INFO, 
"3Spline_TH_enable_MB[%d][%d][%d]: %.4f, ",
+                            write_info(ctx, "3Spline_TH_enable_MB[%d][%d][%d]: 
%.4f, ",
                                     w, i, j, 
av_q2d(three_spline->th_enable_mb));
-                        av_log(ctx, AV_LOG_INFO, 
"3Spline_TH_enable[%d][%d][%d]: %.4f, ",
+                        write_info(ctx, "3Spline_TH_enable[%d][%d][%d]: %.4f, 
",
                                 w, i, j, av_q2d(three_spline->th_enable));
-                        av_log(ctx, AV_LOG_INFO, 
"3Spline_TH_Delta1[%d][%d][%d]: %.4f, ",
+                        write_info(ctx, "3Spline_TH_Delta1[%d][%d][%d]: %.4f, 
",
                                 w, i, j, av_q2d(three_spline->th_delta1));
-                        av_log(ctx, AV_LOG_INFO, 
"3Spline_TH_Delta2[%d][%d][%d]: %.4f, ",
+                        write_info(ctx, "3Spline_TH_Delta2[%d][%d][%d]: %.4f, 
",
                                 w, i, j, av_q2d(three_spline->th_delta2));
-                        av_log(ctx, AV_LOG_INFO, 
"3Spline_enable_Strength[%d][%d][%d]: %.4f, ",
+                        write_info(ctx, "3Spline_enable_Strength[%d][%d][%d]: 
%.4f, ",
                                 w, i, j, 
av_q2d(three_spline->enable_strength));
                     }
                 }
             }
         }
 
-        av_log(ctx, AV_LOG_INFO, "color_saturation_mapping_flag[%d]: %d",
+        write_info(ctx, "color_saturation_mapping_flag[%d]: %d",
                 w, params->color_saturation_mapping_flag);
         if (params->color_saturation_mapping_flag) {
-            av_log(ctx, AV_LOG_INFO, ", color_saturation_num[%d]: %d",
+            write_info(ctx, ", color_saturation_num[%d]: %d",
                    w, params->color_saturation_num);
             for (int i = 0; i < params->color_saturation_num; i++) {
-                av_log(ctx, AV_LOG_INFO, ", color_saturation_gain[%d][%d]: 
%.4f",
+                write_info(ctx, ", color_saturation_gain[%d][%d]: %.4f",
                        w, i, av_q2d(params->color_saturation_gain[i]));
             }
         }
@@ -395,7 +412,7 @@ static void dump_content_light_metadata(AVFilterContext 
*ctx, AVFrameSideData *s
 {
     const AVContentLightMetadata *metadata = (const AVContentLightMetadata 
*)sd->data;
 
-    av_log(ctx, AV_LOG_INFO,
+    write_info(ctx,
            "MaxCLL=%d, MaxFALL=%d",
            metadata->MaxCLL, metadata->MaxFALL);
 }
@@ -405,18 +422,18 @@ static void dump_video_enc_params(AVFilterContext *ctx, 
const AVFrameSideData *s
     const AVVideoEncParams *par = (const AVVideoEncParams *)sd->data;
     int plane, acdc;
 
-    av_log(ctx, AV_LOG_INFO, "type %d; ", par->type);
+    write_info(ctx, "type %d; ", par->type);
     if (par->qp)
-        av_log(ctx, AV_LOG_INFO, "qp=%d; ", par->qp);
+        write_info(ctx, "qp=%d; ", par->qp);
     for (plane = 0; plane < FF_ARRAY_ELEMS(par->delta_qp); plane++)
         for (acdc = 0; acdc < FF_ARRAY_ELEMS(par->delta_qp[plane]); acdc++) {
             int delta_qp = par->delta_qp[plane][acdc];
             if (delta_qp)
-                av_log(ctx, AV_LOG_INFO, "delta_qp[%d][%d]=%d; ",
+                write_info(ctx, "delta_qp[%d][%d]=%d; ",
                        plane, acdc, delta_qp);
         }
     if (par->nb_blocks)
-        av_log(ctx, AV_LOG_INFO, "%u blocks; ", par->nb_blocks);
+        write_info(ctx, "%u blocks; ", par->nb_blocks);
 }
 
 static void dump_sei_unregistered_metadata(AVFilterContext *ctx, const 
AVFrameSideData *sd)
@@ -425,22 +442,22 @@ static void 
dump_sei_unregistered_metadata(AVFilterContext *ctx, const AVFrameSi
     ShowInfoContext *s = ctx->priv;
 
     if (sd->size < AV_UUID_LEN) {
-        av_log(ctx, AV_LOG_ERROR, "invalid data (%zu < UUID(%d-bytes))\n",
+        write_info(ctx, "invalid data (%zu < UUID(%d-bytes))\n",
                sd->size, AV_UUID_LEN);
         return;
     }
 
-    av_log(ctx, AV_LOG_INFO, "UUID=" AV_PRI_UUID "\n", AV_UUID_ARG(user_data));
+    write_info(ctx, "UUID=" AV_PRI_UUID "\n", AV_UUID_ARG(user_data));
 
-    av_log(ctx, AV_LOG_INFO, "User Data=");
+    write_info(ctx, "User Data=");
     for (size_t i = 16; i < sd->size; i++) {
         const char *format = "%02x";
 
         if (s->udu_sei_as_ascii)
             format = isprint(user_data[i]) ? "%c" : "\\x%02x";
-        av_log(ctx, AV_LOG_INFO, format, user_data[i]);
+        write_info(ctx, format, user_data[i]);
     }
-    av_log(ctx, AV_LOG_INFO, "\n");
+    write_info(ctx, "\n");
 }
 
 static void dump_sei_film_grain_params_metadata(AVFilterContext *ctx, const 
AVFrameSideData *sd)
@@ -458,22 +475,22 @@ static void 
dump_sei_film_grain_params_metadata(AVFilterContext *ctx, const AVFr
     const char *colorspace_str      = av_color_space_name(fgp->color_space);
 
     if (fgp->type >= FF_ARRAY_ELEMS(film_grain_type_names)) {
-        av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+        write_info(ctx, "invalid data\n");
         return;
     }
 
-    av_log(ctx, AV_LOG_INFO, "type %s; ", film_grain_type_names[fgp->type]);
-    av_log(ctx, AV_LOG_INFO, "seed=%"PRIu64"; ", fgp->seed);
-    av_log(ctx, AV_LOG_INFO, "width=%d; ", fgp->width);
-    av_log(ctx, AV_LOG_INFO, "height=%d; ", fgp->height);
-    av_log(ctx, AV_LOG_INFO, "subsampling_x=%d; ", fgp->subsampling_x);
-    av_log(ctx, AV_LOG_INFO, "subsampling_y=%d; ", fgp->subsampling_y);
-    av_log(ctx, AV_LOG_INFO, "color_range=%s; ", color_range_str ? 
color_range_str : "unknown");
-    av_log(ctx, AV_LOG_INFO, "color_primaries=%s; ", color_primaries_str ? 
color_primaries_str : "unknown");
-    av_log(ctx, AV_LOG_INFO, "color_trc=%s; ", color_trc_str ? color_trc_str : 
"unknown");
-    av_log(ctx, AV_LOG_INFO, "color_space=%s; ", colorspace_str ? 
colorspace_str : "unknown");
-    av_log(ctx, AV_LOG_INFO, "bit_depth_luma=%d; ", fgp->bit_depth_luma);
-    av_log(ctx, AV_LOG_INFO, "bit_depth_chroma=%d; ", fgp->bit_depth_chroma);
+    write_info(ctx, "type %s; ", film_grain_type_names[fgp->type]);
+    write_info(ctx, "seed=%"PRIu64"; ", fgp->seed);
+    write_info(ctx, "width=%d; ", fgp->width);
+    write_info(ctx, "height=%d; ", fgp->height);
+    write_info(ctx, "subsampling_x=%d; ", fgp->subsampling_x);
+    write_info(ctx, "subsampling_y=%d; ", fgp->subsampling_y);
+    write_info(ctx, "color_range=%s; ", color_range_str ? color_range_str : 
"unknown");
+    write_info(ctx, "color_primaries=%s; ", color_primaries_str ? 
color_primaries_str : "unknown");
+    write_info(ctx, "color_trc=%s; ", color_trc_str ? color_trc_str : 
"unknown");
+    write_info(ctx, "color_space=%s; ", colorspace_str ? colorspace_str : 
"unknown");
+    write_info(ctx, "bit_depth_luma=%d; ", fgp->bit_depth_luma);
+    write_info(ctx, "bit_depth_chroma=%d; ", fgp->bit_depth_chroma);
 
     switch (fgp->type) {
     case AV_FILM_GRAIN_PARAMS_NONE:
@@ -482,52 +499,52 @@ static void 
dump_sei_film_grain_params_metadata(AVFilterContext *ctx, const AVFr
         const AVFilmGrainAOMParams *aom = &fgp->codec.aom;
         const int num_ar_coeffs_y = 2 * aom->ar_coeff_lag * (aom->ar_coeff_lag 
+ 1);
         const int num_ar_coeffs_uv = num_ar_coeffs_y + !!aom->num_y_points;
-        av_log(ctx, AV_LOG_INFO, "y_points={ ");
+        write_info(ctx, "y_points={ ");
         for (int i = 0; i < aom->num_y_points; i++)
-            av_log(ctx, AV_LOG_INFO, "(%d,%d) ", aom->y_points[i][0], 
aom->y_points[i][1]);
-        av_log(ctx, AV_LOG_INFO, "}; chroma_scaling_from_luma=%d; ", 
aom->chroma_scaling_from_luma);
+            write_info(ctx, "(%d,%d) ", aom->y_points[i][0], 
aom->y_points[i][1]);
+        write_info(ctx, "}; chroma_scaling_from_luma=%d; ", 
aom->chroma_scaling_from_luma);
         for (int uv = 0; uv < 2; uv++) {
-            av_log(ctx, AV_LOG_INFO, "uv_points[%d]={ ", uv);
+            write_info(ctx, "uv_points[%d]={ ", uv);
             for (int i = 0; i < aom->num_uv_points[uv]; i++)
-                av_log(ctx, AV_LOG_INFO, "(%d,%d) ", aom->uv_points[uv][i][0], 
aom->uv_points[uv][i][1]);
-            av_log(ctx, AV_LOG_INFO, "}; ");
+                write_info(ctx, "(%d,%d) ", aom->uv_points[uv][i][0], 
aom->uv_points[uv][i][1]);
+            write_info(ctx, "}; ");
         }
-        av_log(ctx, AV_LOG_INFO, "scaling_shift=%d; ", aom->scaling_shift);
-        av_log(ctx, AV_LOG_INFO, "ar_coeff_lag=%d; ", aom->ar_coeff_lag);
+        write_info(ctx, "scaling_shift=%d; ", aom->scaling_shift);
+        write_info(ctx, "ar_coeff_lag=%d; ", aom->ar_coeff_lag);
         if (num_ar_coeffs_y) {
-            av_log(ctx, AV_LOG_INFO, "ar_coeffs_y={ ");
+            write_info(ctx, "ar_coeffs_y={ ");
             for (int i = 0; i < num_ar_coeffs_y; i++)
-                av_log(ctx, AV_LOG_INFO, "%d ", aom->ar_coeffs_y[i]);
-            av_log(ctx, AV_LOG_INFO, "}; ");
+                write_info(ctx, "%d ", aom->ar_coeffs_y[i]);
+            write_info(ctx, "}; ");
         }
         for (int uv = 0; num_ar_coeffs_uv && uv < 2; uv++) {
-            av_log(ctx, AV_LOG_INFO, "ar_coeffs_uv[%d]={ ", uv);
+            write_info(ctx, "ar_coeffs_uv[%d]={ ", uv);
             for (int i = 0; i < num_ar_coeffs_uv; i++)
-                av_log(ctx, AV_LOG_INFO, "%d ", aom->ar_coeffs_uv[uv][i]);
-            av_log(ctx, AV_LOG_INFO, "}; ");
+                write_info(ctx, "%d ", aom->ar_coeffs_uv[uv][i]);
+            write_info(ctx, "}; ");
         }
-        av_log(ctx, AV_LOG_INFO, "ar_coeff_shift=%d; ", aom->ar_coeff_shift);
-        av_log(ctx, AV_LOG_INFO, "grain_scale_shift=%d; ", 
aom->grain_scale_shift);
+        write_info(ctx, "ar_coeff_shift=%d; ", aom->ar_coeff_shift);
+        write_info(ctx, "grain_scale_shift=%d; ", aom->grain_scale_shift);
         for (int uv = 0; uv < 2; uv++) {
-            av_log(ctx, AV_LOG_INFO, "uv_mult[%d] = %d; ", uv, 
aom->uv_mult[uv]);
-            av_log(ctx, AV_LOG_INFO, "uv_mult_luma[%d] = %d; ", uv, 
aom->uv_mult_luma[uv]);
-            av_log(ctx, AV_LOG_INFO, "uv_offset[%d] = %d; ", uv, 
aom->uv_offset[uv]);
+            write_info(ctx, "uv_mult[%d] = %d; ", uv, aom->uv_mult[uv]);
+            write_info(ctx, "uv_mult_luma[%d] = %d; ", uv, 
aom->uv_mult_luma[uv]);
+            write_info(ctx, "uv_offset[%d] = %d; ", uv, aom->uv_offset[uv]);
         }
-        av_log(ctx, AV_LOG_INFO, "overlap_flag=%d; ", aom->overlap_flag);
-        av_log(ctx, AV_LOG_INFO, "limit_output_range=%d; ", 
aom->limit_output_range);
+        write_info(ctx, "overlap_flag=%d; ", aom->overlap_flag);
+        write_info(ctx, "limit_output_range=%d; ", aom->limit_output_range);
         break;
     }
     case AV_FILM_GRAIN_PARAMS_H274: {
         const AVFilmGrainH274Params *h274 = &fgp->codec.h274;
-        av_log(ctx, AV_LOG_INFO, "model_id=%d; ", h274->model_id);
-        av_log(ctx, AV_LOG_INFO, "blending_mode_id=%d; ", 
h274->blending_mode_id);
-        av_log(ctx, AV_LOG_INFO, "log2_scale_factor=%d; ", 
h274->log2_scale_factor);
+        write_info(ctx, "model_id=%d; ", h274->model_id);
+        write_info(ctx, "blending_mode_id=%d; ", h274->blending_mode_id);
+        write_info(ctx, "log2_scale_factor=%d; ", h274->log2_scale_factor);
 
         for (int c = 0; c < 3; c++)
             if (h274->component_model_present[c] && (h274->num_model_values[c] 
> 6 ||
                                                      
h274->num_intensity_intervals[c] < 1 ||
                                                      
h274->num_intensity_intervals[c] > 256)) {
-                av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+                write_info(ctx, "invalid data\n");
                 return;
             }
 
@@ -535,15 +552,15 @@ static void 
dump_sei_film_grain_params_metadata(AVFilterContext *ctx, const AVFr
             if (!h274->component_model_present[c])
                 continue;
 
-            av_log(ctx, AV_LOG_INFO, "num_intensity_intervals[%d]=%u; ", c, 
h274->num_intensity_intervals[c]);
-            av_log(ctx, AV_LOG_INFO, "num_model_values[%d]=%u; ", c, 
h274->num_model_values[c]);
+            write_info(ctx, "num_intensity_intervals[%d]=%u; ", c, 
h274->num_intensity_intervals[c]);
+            write_info(ctx, "num_model_values[%d]=%u; ", c, 
h274->num_model_values[c]);
             for (int i = 0; i < h274->num_intensity_intervals[c]; i++) {
-                av_log(ctx, AV_LOG_INFO, 
"intensity_interval_lower_bound[%d][%d]=%u; ",
+                write_info(ctx, "intensity_interval_lower_bound[%d][%d]=%u; ",
                                          c, i, 
h274->intensity_interval_lower_bound[c][i]);
-                av_log(ctx, AV_LOG_INFO, 
"intensity_interval_upper_bound[%d][%d]=%u; ",
+                write_info(ctx, "intensity_interval_upper_bound[%d][%d]=%u; ",
                                          c, i, 
h274->intensity_interval_upper_bound[c][i]);
                 for (int j = 0; j < h274->num_model_values[c]; j++)
-                    av_log(ctx, AV_LOG_INFO, "comp_model_value[%d][%d][%d]=%d; 
",
+                    write_info(ctx, "comp_model_value[%d][%d][%d]=%d; ",
                                              c, i, j, 
h274->comp_model_value[c][i][j]);
             }
         }
@@ -559,100 +576,100 @@ static void dump_dovi_metadata(AVFilterContext *ctx, 
const AVFrameSideData *sd)
     const AVDOVIDataMapping *mapping = av_dovi_get_mapping(dovi);
     const AVDOVIColorMetadata *color = av_dovi_get_color(dovi);
 
-    av_log(ctx, AV_LOG_INFO, "    rpu_type=%"PRIu8"; ", hdr->rpu_type);
-    av_log(ctx, AV_LOG_INFO, "rpu_format=%"PRIu16"; ", hdr->rpu_format);
-    av_log(ctx, AV_LOG_INFO, "vdr_rpu_profile=%"PRIu8"; ", 
hdr->vdr_rpu_profile);
-    av_log(ctx, AV_LOG_INFO, "vdr_rpu_level=%"PRIu8"; ", hdr->vdr_rpu_level);
-    av_log(ctx, AV_LOG_INFO, "chroma_resampling_explicit_filter_flag=%"PRIu8"; 
", hdr->chroma_resampling_explicit_filter_flag);
-    av_log(ctx, AV_LOG_INFO, "coef_data_type=%"PRIu8"; ", hdr->coef_data_type);
-    av_log(ctx, AV_LOG_INFO, "coef_log2_denom=%"PRIu8"; ", 
hdr->coef_log2_denom);
-    av_log(ctx, AV_LOG_INFO, "vdr_rpu_normalized_idc=%"PRIu8"; ", 
hdr->vdr_rpu_normalized_idc);
-    av_log(ctx, AV_LOG_INFO, "bl_video_full_range_flag=%"PRIu8"; ", 
hdr->bl_video_full_range_flag);
-    av_log(ctx, AV_LOG_INFO, "bl_bit_depth=%"PRIu8"; ", hdr->bl_bit_depth);
-    av_log(ctx, AV_LOG_INFO, "el_bit_depth=%"PRIu8"; ", hdr->el_bit_depth);
-    av_log(ctx, AV_LOG_INFO, "vdr_bit_depth=%"PRIu8"; ", hdr->vdr_bit_depth);
-    av_log(ctx, AV_LOG_INFO, "spatial_resampling_filter_flag=%"PRIu8"; ", 
hdr->spatial_resampling_filter_flag);
-    av_log(ctx, AV_LOG_INFO, "el_spatial_resampling_filter_flag=%"PRIu8"; ", 
hdr->el_spatial_resampling_filter_flag);
-    av_log(ctx, AV_LOG_INFO, "disable_residual_flag=%"PRIu8"\n", 
hdr->disable_residual_flag);
+    write_info(ctx, "    rpu_type=%"PRIu8"; ", hdr->rpu_type);
+    write_info(ctx, "rpu_format=%"PRIu16"; ", hdr->rpu_format);
+    write_info(ctx, "vdr_rpu_profile=%"PRIu8"; ", hdr->vdr_rpu_profile);
+    write_info(ctx, "vdr_rpu_level=%"PRIu8"; ", hdr->vdr_rpu_level);
+    write_info(ctx, "chroma_resampling_explicit_filter_flag=%"PRIu8"; ", 
hdr->chroma_resampling_explicit_filter_flag);
+    write_info(ctx, "coef_data_type=%"PRIu8"; ", hdr->coef_data_type);
+    write_info(ctx, "coef_log2_denom=%"PRIu8"; ", hdr->coef_log2_denom);
+    write_info(ctx, "vdr_rpu_normalized_idc=%"PRIu8"; ", 
hdr->vdr_rpu_normalized_idc);
+    write_info(ctx, "bl_video_full_range_flag=%"PRIu8"; ", 
hdr->bl_video_full_range_flag);
+    write_info(ctx, "bl_bit_depth=%"PRIu8"; ", hdr->bl_bit_depth);
+    write_info(ctx, "el_bit_depth=%"PRIu8"; ", hdr->el_bit_depth);
+    write_info(ctx, "vdr_bit_depth=%"PRIu8"; ", hdr->vdr_bit_depth);
+    write_info(ctx, "spatial_resampling_filter_flag=%"PRIu8"; ", 
hdr->spatial_resampling_filter_flag);
+    write_info(ctx, "el_spatial_resampling_filter_flag=%"PRIu8"; ", 
hdr->el_spatial_resampling_filter_flag);
+    write_info(ctx, "disable_residual_flag=%"PRIu8"\n", 
hdr->disable_residual_flag);
 
-    av_log(ctx, AV_LOG_INFO, "    data mapping: ");
-    av_log(ctx, AV_LOG_INFO, "vdr_rpu_id=%"PRIu8"; ", mapping->vdr_rpu_id);
-    av_log(ctx, AV_LOG_INFO, "mapping_color_space=%"PRIu8"; ", 
mapping->mapping_color_space);
-    av_log(ctx, AV_LOG_INFO, "mapping_chroma_format_idc=%"PRIu8"; ", 
mapping->mapping_chroma_format_idc);
-    av_log(ctx, AV_LOG_INFO, "nlq_method_idc=%d; ", (int) 
mapping->nlq_method_idc);
-    av_log(ctx, AV_LOG_INFO, "num_x_partitions=%"PRIu32"; ", 
mapping->num_x_partitions);
-    av_log(ctx, AV_LOG_INFO, "num_y_partitions=%"PRIu32"\n", 
mapping->num_y_partitions);
+    write_info(ctx, "    data mapping: ");
+    write_info(ctx, "vdr_rpu_id=%"PRIu8"; ", mapping->vdr_rpu_id);
+    write_info(ctx, "mapping_color_space=%"PRIu8"; ", 
mapping->mapping_color_space);
+    write_info(ctx, "mapping_chroma_format_idc=%"PRIu8"; ", 
mapping->mapping_chroma_format_idc);
+    write_info(ctx, "nlq_method_idc=%d; ", (int) mapping->nlq_method_idc);
+    write_info(ctx, "num_x_partitions=%"PRIu32"; ", mapping->num_x_partitions);
+    write_info(ctx, "num_y_partitions=%"PRIu32"\n", mapping->num_y_partitions);
 
     for (int c = 0; c < 3; c++) {
         const AVDOVIReshapingCurve *curve = &mapping->curves[c];
         const AVDOVINLQParams *nlq = &mapping->nlq[c];
-        av_log(ctx, AV_LOG_INFO, "      channel %d: ", c);
-        av_log(ctx, AV_LOG_INFO, "pivots={ ");
+        write_info(ctx, "      channel %d: ", c);
+        write_info(ctx, "pivots={ ");
         for (int i = 0; i < curve->num_pivots; i++)
-            av_log(ctx, AV_LOG_INFO, "%"PRIu16" ", curve->pivots[i]);
-        av_log(ctx, AV_LOG_INFO, "}; mapping_idc={ ");
+            write_info(ctx, "%"PRIu16" ", curve->pivots[i]);
+        write_info(ctx, "}; mapping_idc={ ");
         for (int i = 0; i < curve->num_pivots - 1; i++)
-            av_log(ctx, AV_LOG_INFO, "%d ", (int) curve->mapping_idc[i]);
-        av_log(ctx, AV_LOG_INFO, "}; poly_order={ ");
+            write_info(ctx, "%d ", (int) curve->mapping_idc[i]);
+        write_info(ctx, "}; poly_order={ ");
         for (int i = 0; i < curve->num_pivots - 1; i++)
-            av_log(ctx, AV_LOG_INFO, "%"PRIu8" ", curve->poly_order[i]);
-        av_log(ctx, AV_LOG_INFO, "}; poly_coef={ ");
+            write_info(ctx, "%"PRIu8" ", curve->poly_order[i]);
+        write_info(ctx, "}; poly_coef={ ");
         for (int i = 0; i < curve->num_pivots - 1; i++) {
-            av_log(ctx, AV_LOG_INFO, "{%"PRIi64", %"PRIi64", %"PRIi64"} ",
+            write_info(ctx, "{%"PRIi64", %"PRIi64", %"PRIi64"} ",
                    curve->poly_coef[i][0],
                    curve->poly_coef[i][1],
                    curve->poly_coef[i][2]);
         }
 
-        av_log(ctx, AV_LOG_INFO, "}; mmr_order={ ");
+        write_info(ctx, "}; mmr_order={ ");
         for (int i = 0; i < curve->num_pivots - 1; i++)
-            av_log(ctx, AV_LOG_INFO, "%"PRIu8" ", curve->mmr_order[i]);
-        av_log(ctx, AV_LOG_INFO, "}; mmr_constant={ ");
+            write_info(ctx, "%"PRIu8" ", curve->mmr_order[i]);
+        write_info(ctx, "}; mmr_constant={ ");
         for (int i = 0; i < curve->num_pivots - 1; i++)
-            av_log(ctx, AV_LOG_INFO, "%"PRIi64" ", curve->mmr_constant[i]);
-        av_log(ctx, AV_LOG_INFO, "}; mmr_coef={ ");
+            write_info(ctx, "%"PRIi64" ", curve->mmr_constant[i]);
+        write_info(ctx, "}; mmr_coef={ ");
         for (int i = 0; i < curve->num_pivots - 1; i++) {
-            av_log(ctx, AV_LOG_INFO, "{");
+            write_info(ctx, "{");
             for (int j = 0; j < curve->mmr_order[i]; j++) {
                 for (int k = 0; k < 7; k++)
-                    av_log(ctx, AV_LOG_INFO, "%"PRIi64" ", 
curve->mmr_coef[i][j][k]);
+                    write_info(ctx, "%"PRIi64" ", curve->mmr_coef[i][j][k]);
             }
-            av_log(ctx, AV_LOG_INFO, "} ");
+            write_info(ctx, "} ");
         }
 
-        av_log(ctx, AV_LOG_INFO, "}; nlq_offset=%"PRIu16"; ", nlq->nlq_offset);
-        av_log(ctx, AV_LOG_INFO, "vdr_in_max=%"PRIu64"; ", nlq->vdr_in_max);
+        write_info(ctx, "}; nlq_offset=%"PRIu16"; ", nlq->nlq_offset);
+        write_info(ctx, "vdr_in_max=%"PRIu64"; ", nlq->vdr_in_max);
         switch (mapping->nlq_method_idc) {
         case AV_DOVI_NLQ_LINEAR_DZ:
-            av_log(ctx, AV_LOG_INFO, "linear_deadzone_slope=%"PRIu64"; ", 
nlq->linear_deadzone_slope);
-            av_log(ctx, AV_LOG_INFO, "linear_deadzone_threshold=%"PRIu64"\n", 
nlq->linear_deadzone_threshold);
+            write_info(ctx, "linear_deadzone_slope=%"PRIu64"; ", 
nlq->linear_deadzone_slope);
+            write_info(ctx, "linear_deadzone_threshold=%"PRIu64"\n", 
nlq->linear_deadzone_threshold);
             break;
         }
     }
 
-    av_log(ctx, AV_LOG_INFO, "    color metadata: ");
-    av_log(ctx, AV_LOG_INFO, "dm_metadata_id=%"PRIu8"; ", 
color->dm_metadata_id);
-    av_log(ctx, AV_LOG_INFO, "scene_refresh_flag=%"PRIu8"; ", 
color->scene_refresh_flag);
-    av_log(ctx, AV_LOG_INFO, "ycc_to_rgb_matrix={ ");
+    write_info(ctx, "    color metadata: ");
+    write_info(ctx, "dm_metadata_id=%"PRIu8"; ", color->dm_metadata_id);
+    write_info(ctx, "scene_refresh_flag=%"PRIu8"; ", 
color->scene_refresh_flag);
+    write_info(ctx, "ycc_to_rgb_matrix={ ");
     for (int i = 0; i < 9; i++)
-        av_log(ctx, AV_LOG_INFO, "%f ", av_q2d(color->ycc_to_rgb_matrix[i]));
-    av_log(ctx, AV_LOG_INFO, "}; ycc_to_rgb_offset={ ");
+        write_info(ctx, "%f ", av_q2d(color->ycc_to_rgb_matrix[i]));
+    write_info(ctx, "}; ycc_to_rgb_offset={ ");
     for (int i = 0; i < 3; i++)
-        av_log(ctx, AV_LOG_INFO, "%f ", av_q2d(color->ycc_to_rgb_offset[i]));
-    av_log(ctx, AV_LOG_INFO, "}; rgb_to_lms_matrix={ ");
+        write_info(ctx, "%f ", av_q2d(color->ycc_to_rgb_offset[i]));
+    write_info(ctx, "}; rgb_to_lms_matrix={ ");
     for (int i = 0; i < 9; i++)
-        av_log(ctx, AV_LOG_INFO, "%f ", av_q2d(color->rgb_to_lms_matrix[i]));
-    av_log(ctx, AV_LOG_INFO, "}; signal_eotf=%"PRIu16"; ", color->signal_eotf);
-    av_log(ctx, AV_LOG_INFO, "signal_eotf_param0=%"PRIu16"; ", 
color->signal_eotf_param0);
-    av_log(ctx, AV_LOG_INFO, "signal_eotf_param1=%"PRIu16"; ", 
color->signal_eotf_param1);
-    av_log(ctx, AV_LOG_INFO, "signal_eotf_param2=%"PRIu32"; ", 
color->signal_eotf_param2);
-    av_log(ctx, AV_LOG_INFO, "signal_bit_depth=%"PRIu8"; ", 
color->signal_bit_depth);
-    av_log(ctx, AV_LOG_INFO, "signal_color_space=%"PRIu8"; ", 
color->signal_color_space);
-    av_log(ctx, AV_LOG_INFO, "signal_chroma_format=%"PRIu8"; ", 
color->signal_chroma_format);
-    av_log(ctx, AV_LOG_INFO, "signal_full_range_flag=%"PRIu8"; ", 
color->signal_full_range_flag);
-    av_log(ctx, AV_LOG_INFO, "source_min_pq=%"PRIu16"; ", 
color->source_min_pq);
-    av_log(ctx, AV_LOG_INFO, "source_max_pq=%"PRIu16"; ", 
color->source_max_pq);
-    av_log(ctx, AV_LOG_INFO, "source_diagonal=%"PRIu16"; ", 
color->source_diagonal);
+        write_info(ctx, "%f ", av_q2d(color->rgb_to_lms_matrix[i]));
+    write_info(ctx, "}; signal_eotf=%"PRIu16"; ", color->signal_eotf);
+    write_info(ctx, "signal_eotf_param0=%"PRIu16"; ", 
color->signal_eotf_param0);
+    write_info(ctx, "signal_eotf_param1=%"PRIu16"; ", 
color->signal_eotf_param1);
+    write_info(ctx, "signal_eotf_param2=%"PRIu32"; ", 
color->signal_eotf_param2);
+    write_info(ctx, "signal_bit_depth=%"PRIu8"; ", color->signal_bit_depth);
+    write_info(ctx, "signal_color_space=%"PRIu8"; ", 
color->signal_color_space);
+    write_info(ctx, "signal_chroma_format=%"PRIu8"; ", 
color->signal_chroma_format);
+    write_info(ctx, "signal_full_range_flag=%"PRIu8"; ", 
color->signal_full_range_flag);
+    write_info(ctx, "source_min_pq=%"PRIu16"; ", color->source_min_pq);
+    write_info(ctx, "source_max_pq=%"PRIu16"; ", color->source_max_pq);
+    write_info(ctx, "source_diagonal=%"PRIu16"; ", color->source_diagonal);
 }
 
 static void dump_ambient_viewing_environment(AVFilterContext *ctx, const 
AVFrameSideData *sd)
@@ -660,7 +677,7 @@ static void 
dump_ambient_viewing_environment(AVFilterContext *ctx, const AVFrame
     const AVAmbientViewingEnvironment *ambient_viewing_environment =
                                                (const 
AVAmbientViewingEnvironment *)sd->data;
 
-    av_log(ctx, AV_LOG_INFO, "ambient_illuminance=%f, ambient_light_x=%f, 
ambient_light_y=%f",
+    write_info(ctx, "ambient_illuminance=%f, ambient_light_x=%f, 
ambient_light_y=%f",
            av_q2d(ambient_viewing_environment->ambient_illuminance),
            av_q2d(ambient_viewing_environment->ambient_light_x),
            av_q2d(ambient_viewing_environment->ambient_light_y));
@@ -674,29 +691,29 @@ static void dump_color_property(AVFilterContext *ctx, 
AVFrame *frame)
     const char *color_trc_str       = av_color_transfer_name(frame->color_trc);
 
     if (!color_range_str || frame->color_range == AVCOL_RANGE_UNSPECIFIED) {
-        av_log(ctx, AV_LOG_INFO, "color_range:unknown");
+        write_info(ctx, "color_range:unknown");
     } else {
-        av_log(ctx, AV_LOG_INFO, "color_range:%s", color_range_str);
+        write_info(ctx, "color_range:%s", color_range_str);
     }
 
     if (!colorspace_str || frame->colorspace == AVCOL_SPC_UNSPECIFIED) {
-        av_log(ctx, AV_LOG_INFO, " color_space:unknown");
+        write_info(ctx, " color_space:unknown");
     } else {
-        av_log(ctx, AV_LOG_INFO, " color_space:%s", colorspace_str);
+        write_info(ctx, " color_space:%s", colorspace_str);
     }
 
     if (!color_primaries_str || frame->color_primaries == 
AVCOL_PRI_UNSPECIFIED) {
-        av_log(ctx, AV_LOG_INFO, " color_primaries:unknown");
+        write_info(ctx, " color_primaries:unknown");
     } else {
-        av_log(ctx, AV_LOG_INFO, " color_primaries:%s", color_primaries_str);
+        write_info(ctx, " color_primaries:%s", color_primaries_str);
     }
 
     if (!color_trc_str || frame->color_trc == AVCOL_TRC_UNSPECIFIED) {
-        av_log(ctx, AV_LOG_INFO, " color_trc:unknown");
+        write_info(ctx, " color_trc:unknown");
     } else {
-        av_log(ctx, AV_LOG_INFO, " color_trc:%s", color_trc_str);
+        write_info(ctx, " color_trc:%s", color_trc_str);
     }
-    av_log(ctx, AV_LOG_INFO, "\n");
+    write_info(ctx, "\n");
 }
 
 static void update_sample_stats_8(const uint8_t *src, int len, int64_t *sum, 
int64_t *sum2)
@@ -733,6 +750,39 @@ static void update_sample_stats(int depth, int be, const 
uint8_t *src, int len,
         update_sample_stats_16(be, src, len, sum, sum2);
 }
 
+static av_cold int init(AVFilterContext *ctx) {
+    int ret;
+    ShowInfoContext *s = ctx->priv;
+
+    if (s->file_str != NULL) {
+        if (!strcmp("-", s->file_str)) {
+            ret = avio_open(&s->avio_context, "pipe:1", AVIO_FLAG_WRITE);
+        } else {
+            ret = avio_open(&s->avio_context, s->file_str, AVIO_FLAG_WRITE);
+        }
+
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Could not open %s: %s\n",
+                   s->file_str, av_err2str(ret));
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx) {
+    int ret;
+    ShowInfoContext *s = ctx->priv;
+
+    if (s->avio_context) {
+        ret = avio_close(s->avio_context);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to close file: %s\n", 
s->file_str);
+        }
+    }
+}
+
 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 {
     FilterLink *inl = ff_filter_link(inlink);
@@ -765,7 +815,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame 
*frame)
         }
     }
 
-    av_log(ctx, AV_LOG_INFO,
+    write_info(ctx,
            "n:%4"PRId64" pts:%7s pts_time:%-7s duration:%7"PRId64
            " duration_time:%-7s "
            "fmt:%s cl:%s sar:%d/%d s:%dx%d i:%c iskey:%d type:%c ",
@@ -781,33 +831,33 @@ static int filter_frame(AVFilterLink *inlink, AVFrame 
*frame)
            av_get_picture_type_char(frame->pict_type));
 
     if (s->calculate_checksums) {
-        av_log(ctx, AV_LOG_INFO,
+        write_info(ctx,
                "checksum:%08"PRIX32" plane_checksum:[%08"PRIX32,
                checksum, plane_checksum[0]);
 
         for (plane = 1; plane < 4 && frame->data[plane] && 
frame->linesize[plane]; plane++)
-            av_log(ctx, AV_LOG_INFO, " %08"PRIX32, plane_checksum[plane]);
-        av_log(ctx, AV_LOG_INFO, "] mean:[");
+            write_info(ctx, " %08"PRIX32, plane_checksum[plane]);
+        write_info(ctx, "] mean:[");
         for (plane = 0; plane < 4 && frame->data[plane] && 
frame->linesize[plane]; plane++)
-            av_log(ctx, AV_LOG_INFO, "%s%"PRId64,
+            write_info(ctx, "%s%"PRId64,
                    plane ? " ":"",
                    (sum[plane] + pixelcount[plane]/2) / pixelcount[plane]);
-        av_log(ctx, AV_LOG_INFO, "] stdev:[");
+        write_info(ctx, "] stdev:[");
         for (plane = 0; plane < 4 && frame->data[plane] && 
frame->linesize[plane]; plane++)
-            av_log(ctx, AV_LOG_INFO, "%s%3.1f",
+            write_info(ctx, "%s%3.1f",
                    plane ? " ":"",
                    sqrt((sum2[plane] - 
sum[plane]*(double)sum[plane]/pixelcount[plane])/pixelcount[plane]));
-        av_log(ctx, AV_LOG_INFO, "]");
+        write_info(ctx, "]");
     }
-    av_log(ctx, AV_LOG_INFO, "\n");
+    write_info(ctx, "\n");
 
     for (i = 0; i < frame->nb_side_data; i++) {
         AVFrameSideData *sd = frame->side_data[i];
         const char *name = av_frame_side_data_name(sd->type);
 
-        av_log(ctx, AV_LOG_INFO, "  side data - ");
+        write_info(ctx, "  side data - ");
         if (name)
-            av_log(ctx, AV_LOG_INFO, "%s: ", name);
+            write_info(ctx, "%s: ", name);
         switch (sd->type) {
         case AV_FRAME_DATA_SPHERICAL:
             dump_spherical(ctx, frame, sd);
@@ -820,11 +870,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame 
*frame)
             break;
         }
         case AV_FRAME_DATA_DISPLAYMATRIX:
-            av_log(ctx, AV_LOG_INFO, "rotation of %.2f degrees",
+            write_info(ctx, "rotation of %.2f degrees",
                    av_display_rotation_get((int32_t *)sd->data));
             break;
         case AV_FRAME_DATA_AFD:
-            av_log(ctx, AV_LOG_INFO, "value of %"PRIu8, sd->data[0]);
+            write_info(ctx, "value of %"PRIu8, sd->data[0]);
             break;
         case AV_FRAME_DATA_REGIONS_OF_INTEREST:
             dump_roi(ctx, sd);
@@ -847,7 +897,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame 
*frame)
         case AV_FRAME_DATA_GOP_TIMECODE: {
             char tcbuf[AV_TIMECODE_STR_SIZE];
             av_timecode_make_mpeg_tc_string(tcbuf, *(int64_t *)(sd->data));
-            av_log(ctx, AV_LOG_INFO, "%s", tcbuf);
+            write_info(ctx, "%s", tcbuf);
             break;
         }
         case AV_FRAME_DATA_VIDEO_ENC_PARAMS:
@@ -866,21 +916,21 @@ static int filter_frame(AVFilterLink *inlink, AVFrame 
*frame)
             dump_ambient_viewing_environment(ctx, sd);
             break;
         case AV_FRAME_DATA_VIEW_ID:
-            av_log(ctx, AV_LOG_INFO, "view id: %d\n", *(int*)sd->data);
+            write_info(ctx, "view id: %d\n", *(int*)sd->data);
             break;
         case AV_FRAME_DATA_3D_REFERENCE_DISPLAYS:
             dump_tdrdi(ctx, sd);
             break;
         default:
             if (name)
-                av_log(ctx, AV_LOG_INFO, "(%zu bytes)", sd->size);
+                write_info(ctx, "(%zu bytes)", sd->size);
             else
-                av_log(ctx, AV_LOG_WARNING, "unknown side data type %d "
+                write_info(ctx, "unknown side data type %d "
                        "(%zu bytes)", sd->type, sd->size);
             break;
         }
 
-        av_log(ctx, AV_LOG_INFO, "\n");
+        write_info(ctx, "\n");
     }
 
     dump_color_property(ctx, frame);
@@ -888,9 +938,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame 
*frame)
     if (desc->flags & AV_PIX_FMT_FLAG_ALPHA) {
         const char *alpha_mode_str = av_alpha_mode_name(frame->alpha_mode);
         if (!alpha_mode_str || frame->alpha_mode == AVALPHA_MODE_UNSPECIFIED)
-            av_log(ctx, AV_LOG_INFO, "alpha_mode:unspecified\n");
+            write_info(ctx, "alpha_mode:unspecified\n");
         else
-            av_log(ctx, AV_LOG_INFO, "alpha_mode:%s\n", alpha_mode_str);
+            write_info(ctx, "alpha_mode:%s\n", alpha_mode_str);
+    }
+
+    if (s->avio_context) {
+        avio_flush(s->avio_context);
+        if (s->avio_context->error < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to write to %s: %s\n",
+                   s->file_str, av_err2str(s->avio_context->error));
+        }
     }
 
     return ff_filter_frame(inlink->dst->outputs[0], frame);
@@ -900,7 +958,7 @@ static int config_props(AVFilterContext *ctx, AVFilterLink 
*link, int is_out)
 {
     FilterLink *l = ff_filter_link(link);
 
-    av_log(ctx, AV_LOG_INFO, "config %s time_base: %d/%d, frame_rate: %d/%d\n",
+    write_info(ctx, "config %s time_base: %d/%d, frame_rate: %d/%d\n",
            is_out ? "out" : "in",
            link->time_base.num, link->time_base.den,
            l->frame_rate.num, l->frame_rate.den);
@@ -945,4 +1003,6 @@ const FFFilter ff_vf_showinfo = {
     FILTER_INPUTS(avfilter_vf_showinfo_inputs),
     FILTER_OUTPUTS(avfilter_vf_showinfo_outputs),
     .priv_size   = sizeof(ShowInfoContext),
+    .init        = init,
+    .uninit      = uninit,
 };
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to