FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/af_ashowinfo.c
Date: 2026-09-16 18:55:35
Exec Total Coverage
Lines: 0 185 0.0%
Functions: 0 11 0.0%
Branches: 0 81 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2011 Stefano Sabatini
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * @file
23 * filter for showing textual audio frame information
24 */
25
26 #include <inttypes.h>
27
28 #include "libavutil/adler32.h"
29 #include "libavutil/attributes.h"
30 #include "libavutil/channel_layout.h"
31 #include "libavutil/downmix_info.h"
32 #include "libavutil/iamf.h"
33 #include "libavutil/mem.h"
34 #include "libavutil/replaygain.h"
35 #include "libavutil/timestamp.h"
36 #include "libavutil/samplefmt.h"
37
38 #include "libavcodec/defs.h"
39
40 #include "audio.h"
41 #include "avfilter.h"
42 #include "filters.h"
43
44 typedef struct AShowInfoContext {
45 /**
46 * Scratch space for individual plane checksums for planar audio
47 */
48 uint32_t *plane_checksums;
49 } AShowInfoContext;
50
51 static av_cold void uninit(AVFilterContext *ctx)
52 {
53 AShowInfoContext *s = ctx->priv;
54 av_freep(&s->plane_checksums);
55 }
56
57 static void dump_matrixenc(AVFilterContext *ctx, AVFrameSideData *sd)
58 {
59 enum AVMatrixEncoding enc;
60
61 av_log(ctx, AV_LOG_INFO, "matrix encoding: ");
62
63 if (sd->size < sizeof(enum AVMatrixEncoding)) {
64 av_log(ctx, AV_LOG_INFO, "invalid data");
65 return;
66 }
67
68 enc = *(enum AVMatrixEncoding *)sd->data;
69 switch (enc) {
70 case AV_MATRIX_ENCODING_NONE: av_log(ctx, AV_LOG_INFO, "none"); break;
71 case AV_MATRIX_ENCODING_DOLBY: av_log(ctx, AV_LOG_INFO, "Dolby Surround"); break;
72 case AV_MATRIX_ENCODING_DPLII: av_log(ctx, AV_LOG_INFO, "Dolby Pro Logic II"); break;
73 case AV_MATRIX_ENCODING_DPLIIX: av_log(ctx, AV_LOG_INFO, "Dolby Pro Logic IIx"); break;
74 case AV_MATRIX_ENCODING_DPLIIZ: av_log(ctx, AV_LOG_INFO, "Dolby Pro Logic IIz"); break;
75 case AV_MATRIX_ENCODING_DOLBYEX: av_log(ctx, AV_LOG_INFO, "Dolby EX"); break;
76 case AV_MATRIX_ENCODING_DOLBYHEADPHONE: av_log(ctx, AV_LOG_INFO, "Dolby Headphone"); break;
77 default: av_log(ctx, AV_LOG_WARNING, "unknown"); break;
78 }
79 }
80
81 static void dump_downmix(AVFilterContext *ctx, AVFrameSideData *sd)
82 {
83 AVDownmixInfo *di;
84
85 av_log(ctx, AV_LOG_INFO, "downmix: ");
86 if (sd->size < sizeof(*di)) {
87 av_log(ctx, AV_LOG_INFO, "invalid data");
88 return;
89 }
90
91 di = (AVDownmixInfo *)sd->data;
92
93 av_log(ctx, AV_LOG_INFO, "preferred downmix type - ");
94 switch (di->preferred_downmix_type) {
95 case AV_DOWNMIX_TYPE_LORO: av_log(ctx, AV_LOG_INFO, "Lo/Ro"); break;
96 case AV_DOWNMIX_TYPE_LTRT: av_log(ctx, AV_LOG_INFO, "Lt/Rt"); break;
97 case AV_DOWNMIX_TYPE_DPLII: av_log(ctx, AV_LOG_INFO, "Dolby Pro Logic II"); break;
98 default: av_log(ctx, AV_LOG_WARNING, "unknown"); break;
99 }
100
101 av_log(ctx, AV_LOG_INFO, " Mix levels: center %f (%f ltrt) - "
102 "surround %f (%f ltrt) - lfe %f",
103 di->center_mix_level, di->center_mix_level_ltrt,
104 di->surround_mix_level, di->surround_mix_level_ltrt,
105 di->lfe_mix_level);
106 }
107
108 static const int downmix_type_map[AV_DOWNMIX_TYPE_NB] = {
109 [AV_DOWNMIX_TYPE_UNKNOWN] = 0,
110 [AV_DOWNMIX_TYPE_LORO] = 2,
111 [AV_DOWNMIX_TYPE_LTRT] = 2,
112 [AV_DOWNMIX_TYPE_DPLII] = 2,
113 };
114
115 static void dump_downmix_matrix(AVFilterContext *ctx, AVFrameSideData *sd, AVChannelLayout *ch_layout)
116 {
117 AVDownmixMatrix *dm = (AVDownmixMatrix *)sd->data;
118 char buf[128];
119
120 av_log(ctx, AV_LOG_INFO, "downmix matrix: ");
121 if (dm->in_ch_count != ch_layout->nb_channels) {
122 av_log(ctx, AV_LOG_INFO, "invalid data");
123 return;
124 }
125
126 av_log(ctx, AV_LOG_INFO, "downmix type - ");
127 switch (dm->downmix_type) {
128 case AV_DOWNMIX_TYPE_LORO: av_log(ctx, AV_LOG_INFO, "Lo/Ro\n"); break;
129 case AV_DOWNMIX_TYPE_LTRT: av_log(ctx, AV_LOG_INFO, "Lt/Rt\n"); break;
130 case AV_DOWNMIX_TYPE_DPLII: av_log(ctx, AV_LOG_INFO, "Dolby Pro Logic II\n"); break;
131 default: av_log(ctx, AV_LOG_WARNING, "invalid data"); return;
132 }
133
134 for (int i = 0; i < downmix_type_map[dm->downmix_type]; i++) {
135 av_log(ctx, AV_LOG_INFO, "[%s] = { ", i ? "FR" : "FL");
136 for (int j = 0; j < dm->in_ch_count; j++) {
137 av_channel_name(buf, sizeof(buf), av_channel_layout_channel_from_index(ch_layout, j));
138 av_log(ctx, AV_LOG_INFO, ".%s = %f, ", buf, *av_downmix_matrix_coeff(dm, i, j));
139 }
140 av_log(ctx, AV_LOG_INFO, "}%s", i == downmix_type_map[dm->downmix_type] - 1 ? "" : ",\n");
141 }
142 }
143
144 static void print_gain(AVFilterContext *ctx, const char *str, int32_t gain)
145 {
146 av_log(ctx, AV_LOG_INFO, "%s - ", str);
147 if (gain == INT32_MIN)
148 av_log(ctx, AV_LOG_INFO, "unknown");
149 else
150 av_log(ctx, AV_LOG_INFO, "%f", gain / 100000.0f);
151 av_log(ctx, AV_LOG_INFO, ", ");
152 }
153
154 static void print_peak(AVFilterContext *ctx, const char *str, uint32_t peak)
155 {
156 av_log(ctx, AV_LOG_INFO, "%s - ", str);
157 if (!peak)
158 av_log(ctx, AV_LOG_INFO, "unknown");
159 else
160 av_log(ctx, AV_LOG_INFO, "%f", peak / 100000.0f);
161 av_log(ctx, AV_LOG_INFO, ", ");
162 }
163
164 static void dump_replaygain(AVFilterContext *ctx, AVFrameSideData *sd)
165 {
166 AVReplayGain *rg;
167
168 av_log(ctx, AV_LOG_INFO, "replaygain: ");
169 if (sd->size < sizeof(*rg)) {
170 av_log(ctx, AV_LOG_INFO, "invalid data");
171 return;
172 }
173 rg = (AVReplayGain*)sd->data;
174
175 print_gain(ctx, "track gain", rg->track_gain);
176 print_peak(ctx, "track peak", rg->track_peak);
177 print_gain(ctx, "album gain", rg->album_gain);
178 print_peak(ctx, "album peak", rg->album_peak);
179 }
180
181 static void dump_audio_service_type(AVFilterContext *ctx, AVFrameSideData *sd)
182 {
183 enum AVAudioServiceType *ast;
184
185 av_log(ctx, AV_LOG_INFO, "audio service type: ");
186 if (sd->size < sizeof(*ast)) {
187 av_log(ctx, AV_LOG_INFO, "invalid data");
188 return;
189 }
190 ast = (enum AVAudioServiceType*)sd->data;
191 switch (*ast) {
192 case AV_AUDIO_SERVICE_TYPE_MAIN: av_log(ctx, AV_LOG_INFO, "Main Audio Service"); break;
193 case AV_AUDIO_SERVICE_TYPE_EFFECTS: av_log(ctx, AV_LOG_INFO, "Effects"); break;
194 case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED: av_log(ctx, AV_LOG_INFO, "Visually Impaired"); break;
195 case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED: av_log(ctx, AV_LOG_INFO, "Hearing Impaired"); break;
196 case AV_AUDIO_SERVICE_TYPE_DIALOGUE: av_log(ctx, AV_LOG_INFO, "Dialogue"); break;
197 case AV_AUDIO_SERVICE_TYPE_COMMENTARY: av_log(ctx, AV_LOG_INFO, "Commentary"); break;
198 case AV_AUDIO_SERVICE_TYPE_EMERGENCY: av_log(ctx, AV_LOG_INFO, "Emergency"); break;
199 case AV_AUDIO_SERVICE_TYPE_VOICE_OVER: av_log(ctx, AV_LOG_INFO, "Voice Over"); break;
200 case AV_AUDIO_SERVICE_TYPE_KARAOKE: av_log(ctx, AV_LOG_INFO, "Karaoke"); break;
201 default: av_log(ctx, AV_LOG_INFO, "unknown"); break;
202 }
203 }
204
205 static void dump_iamf_parameter_definition(AVFilterContext *ctx, const AVFrameSideData *sd)
206 {
207 const AVIAMFParamDefinition *param = (AVIAMFParamDefinition *)sd->data;
208
209 switch (param->type) {
210 case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN:
211 av_log(ctx, AV_LOG_INFO, "iamf mix gain parameters: ");
212 break;
213 case AV_IAMF_PARAMETER_DEFINITION_DEMIXING:
214 av_log(ctx, AV_LOG_INFO, "iamf demixing parameters: ");
215 break;
216 case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN:
217 av_log(ctx, AV_LOG_INFO, "iamf recon gain parameters: ");
218 break;
219 default:
220 av_log(ctx, AV_LOG_ERROR, "unknown iamf parameter definition type: %d",
221 param->type);
222 return;
223 }
224
225 av_log(ctx, AV_LOG_INFO,
226 "nb_subblocks=%d, "
227 "parameter_id=%d, "
228 "parameter_rate=%d, "
229 "duration=%d, "
230 "constant_subblock_duration=%d,",
231 param->nb_subblocks,
232 param->parameter_id,
233 param->parameter_rate,
234 param->duration,
235 param->constant_subblock_duration
236 );
237
238 for (unsigned i = 0; i < param->nb_subblocks; i++) {
239 const void *subblock = av_iamf_param_definition_get_subblock(param, i);
240
241 av_log(ctx, AV_LOG_INFO, " subblock[%d]={ ", i);
242 switch (param->type) {
243 case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
244 const AVIAMFMixGain *mix = subblock;
245 av_log(ctx, AV_LOG_INFO,
246 "subblock_duration=%d, "
247 "animation_type=%d, "
248 "start_point_value=%d/%d, "
249 "end_point_value=%d/%d, "
250 "control_point_value=%d/%d, "
251 "control_point_relative_time=%d/%d",
252 mix->subblock_duration,
253 mix->animation_type,
254 mix->start_point_value.num, mix->start_point_value.den,
255 mix->end_point_value.num, mix->end_point_value.den,
256 mix->control_point_value.num, mix->control_point_value.den,
257 mix->control_point_relative_time.num, mix->control_point_relative_time.den
258 );
259 break;
260 }
261 case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
262 const AVIAMFDemixingInfo *demix = subblock;
263 av_log(ctx, AV_LOG_INFO,
264 "subblock_duration=%d, "
265 "dmixp_mode=%d",
266 demix->subblock_duration,
267 demix->dmixp_mode
268 );
269 break;
270 }
271 case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
272 const AVIAMFReconGain *recon = subblock;
273 av_log(ctx, AV_LOG_INFO,
274 "subblock_duration=%d", recon->subblock_duration
275 );
276 break;
277 }
278 }
279 av_log(ctx, AV_LOG_INFO, (i == param->nb_subblocks - 1) ? " }" : " },");
280 }
281
282 }
283
284 static void dump_unknown(AVFilterContext *ctx, AVFrameSideData *sd)
285 {
286 av_log(ctx, AV_LOG_INFO, "unknown side data type: %d, size "
287 "%zu bytes", sd->type, sd->size);
288 }
289
290 static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
291 {
292 FilterLink *inl = ff_filter_link(inlink);
293 AVFilterContext *ctx = inlink->dst;
294 AShowInfoContext *s = ctx->priv;
295 char chlayout_str[128];
296 uint32_t checksum = 0;
297 int channels = inlink->ch_layout.nb_channels;
298 int planar = av_sample_fmt_is_planar(buf->format);
299 int block_align = av_get_bytes_per_sample(buf->format) * (planar ? 1 : channels);
300 int data_size = buf->nb_samples * block_align;
301 int planes = planar ? channels : 1;
302 int i;
303 void *tmp_ptr = av_realloc_array(s->plane_checksums, channels, sizeof(*s->plane_checksums));
304
305 if (!tmp_ptr)
306 return AVERROR(ENOMEM);
307 s->plane_checksums = tmp_ptr;
308
309 for (i = 0; i < planes; i++) {
310 uint8_t *data = buf->extended_data[i];
311
312 s->plane_checksums[i] = av_adler32_update(0, data, data_size);
313 checksum = i ? av_adler32_update(checksum, data, data_size) :
314 s->plane_checksums[0];
315 }
316
317 av_channel_layout_describe(&buf->ch_layout, chlayout_str, sizeof(chlayout_str));
318
319 av_log(ctx, AV_LOG_INFO,
320 "n:%"PRId64" pts:%s pts_time:%s "
321 "fmt:%s channels:%d chlayout:%s rate:%d nb_samples:%d "
322 "checksum:%08"PRIX32" ",
323 inl->frame_count_out,
324 av_ts2str(buf->pts), av_ts2timestr(buf->pts, &inlink->time_base),
325 av_get_sample_fmt_name(buf->format), buf->ch_layout.nb_channels, chlayout_str,
326 buf->sample_rate, buf->nb_samples,
327 checksum);
328
329 av_log(ctx, AV_LOG_INFO, "plane_checksums: [ ");
330 for (i = 0; i < planes; i++)
331 av_log(ctx, AV_LOG_INFO, "%08"PRIX32" ", s->plane_checksums[i]);
332 av_log(ctx, AV_LOG_INFO, "]\n");
333
334 for (i = 0; i < buf->nb_side_data; i++) {
335 AVFrameSideData *sd = buf->side_data[i];
336
337 av_log(ctx, AV_LOG_INFO, " side data - ");
338 switch (sd->type) {
339 case AV_FRAME_DATA_MATRIXENCODING: dump_matrixenc (ctx, sd); break;
340 case AV_FRAME_DATA_DOWNMIX_INFO: dump_downmix (ctx, sd); break;
341 case AV_FRAME_DATA_DOWNMIX_MATRIX: dump_downmix_matrix(ctx, sd, &buf->ch_layout); break;
342 case AV_FRAME_DATA_REPLAYGAIN: dump_replaygain(ctx, sd); break;
343 case AV_FRAME_DATA_AUDIO_SERVICE_TYPE: dump_audio_service_type(ctx, sd); break;
344 case AV_FRAME_DATA_IAMF_DEMIXING_INFO_PARAM:
345 case AV_FRAME_DATA_IAMF_MIX_GAIN_PARAM:
346 case AV_FRAME_DATA_IAMF_RECON_GAIN_INFO_PARAM:
347 dump_iamf_parameter_definition(ctx, sd);
348 break;
349 default: dump_unknown (ctx, sd); break;
350 }
351
352 av_log(ctx, AV_LOG_INFO, "\n");
353 }
354
355 return ff_filter_frame(inlink->dst->outputs[0], buf);
356 }
357
358 static const AVFilterPad inputs[] = {
359 {
360 .name = "default",
361 .type = AVMEDIA_TYPE_AUDIO,
362 .filter_frame = filter_frame,
363 },
364 };
365
366 const FFFilter ff_af_ashowinfo = {
367 .p.name = "ashowinfo",
368 .p.description = NULL_IF_CONFIG_SMALL("Show textual information for each audio frame."),
369 .p.flags = AVFILTER_FLAG_METADATA_ONLY,
370 .priv_size = sizeof(AShowInfoContext),
371 .uninit = uninit,
372 FILTER_INPUTS(inputs),
373 FILTER_OUTPUTS(ff_audio_default_filterpad),
374 };
375