]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_showinfo.c
0ac1f900000878201d3ec63907409d2c3c7d3c1d
[ffmpeg] / libavfilter / vf_showinfo.c
1 /*
2  * Copyright (c) 2011 Stefano Sabatini
3  * This file is part of FFmpeg.
4  *
5  * FFmpeg is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * FFmpeg is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with FFmpeg; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19
20 /**
21  * @file
22  * filter for showing textual video frame information
23  */
24
25 #include <inttypes.h>
26
27 #include "libavutil/bswap.h"
28 #include "libavutil/adler32.h"
29 #include "libavutil/display.h"
30 #include "libavutil/imgutils.h"
31 #include "libavutil/internal.h"
32 #include "libavutil/opt.h"
33 #include "libavutil/pixdesc.h"
34 #include "libavutil/spherical.h"
35 #include "libavutil/stereo3d.h"
36 #include "libavutil/timestamp.h"
37 #include "libavutil/timecode.h"
38 #include "libavutil/mastering_display_metadata.h"
39 #include "libavutil/video_enc_params.h"
40
41 #include "avfilter.h"
42 #include "internal.h"
43 #include "video.h"
44
45 typedef struct ShowInfoContext {
46     const AVClass *class;
47     int calculate_checksums;
48 } ShowInfoContext;
49
50 #define OFFSET(x) offsetof(ShowInfoContext, x)
51 #define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
52
53 static const AVOption showinfo_options[] = {
54     { "checksum", "calculate checksums", OFFSET(calculate_checksums), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, VF },
55     { NULL }
56 };
57
58 AVFILTER_DEFINE_CLASS(showinfo);
59
60 static void dump_spherical(AVFilterContext *ctx, AVFrame *frame, AVFrameSideData *sd)
61 {
62     AVSphericalMapping *spherical = (AVSphericalMapping *)sd->data;
63     double yaw, pitch, roll;
64
65     av_log(ctx, AV_LOG_INFO, "spherical information: ");
66     if (sd->size < sizeof(*spherical)) {
67         av_log(ctx, AV_LOG_ERROR, "invalid data\n");
68         return;
69     }
70
71     if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR)
72         av_log(ctx, AV_LOG_INFO, "equirectangular ");
73     else if (spherical->projection == AV_SPHERICAL_CUBEMAP)
74         av_log(ctx, AV_LOG_INFO, "cubemap ");
75     else if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE)
76         av_log(ctx, AV_LOG_INFO, "tiled equirectangular ");
77     else {
78         av_log(ctx, AV_LOG_WARNING, "unknown\n");
79         return;
80     }
81
82     yaw = ((double)spherical->yaw) / (1 << 16);
83     pitch = ((double)spherical->pitch) / (1 << 16);
84     roll = ((double)spherical->roll) / (1 << 16);
85     av_log(ctx, AV_LOG_INFO, "(%f/%f/%f) ", yaw, pitch, roll);
86
87     if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) {
88         size_t l, t, r, b;
89         av_spherical_tile_bounds(spherical, frame->width, frame->height,
90                                  &l, &t, &r, &b);
91         av_log(ctx, AV_LOG_INFO,
92                "[%"SIZE_SPECIFIER", %"SIZE_SPECIFIER", %"SIZE_SPECIFIER", %"SIZE_SPECIFIER"] ",
93                l, t, r, b);
94     } else if (spherical->projection == AV_SPHERICAL_CUBEMAP) {
95         av_log(ctx, AV_LOG_INFO, "[pad %"PRIu32"] ", spherical->padding);
96     }
97 }
98
99 static void dump_stereo3d(AVFilterContext *ctx, AVFrameSideData *sd)
100 {
101     AVStereo3D *stereo;
102
103     av_log(ctx, AV_LOG_INFO, "stereoscopic information: ");
104     if (sd->size < sizeof(*stereo)) {
105         av_log(ctx, AV_LOG_ERROR, "invalid data\n");
106         return;
107     }
108
109     stereo = (AVStereo3D *)sd->data;
110
111     av_log(ctx, AV_LOG_INFO, "type - %s", av_stereo3d_type_name(stereo->type));
112
113     if (stereo->flags & AV_STEREO3D_FLAG_INVERT)
114         av_log(ctx, AV_LOG_INFO, " (inverted)");
115 }
116
117 static void dump_s12m_timecode(AVFilterContext *ctx, AVRational frame_rate, AVFrameSideData *sd)
118 {
119     const uint32_t *tc = (const uint32_t *)sd->data;
120
121     if ((sd->size != sizeof(uint32_t) * 4) || (tc[0] > 3)) {
122         av_log(ctx, AV_LOG_ERROR, "invalid data\n");
123         return;
124     }
125
126     for (int j = 1; j <= tc[0]; j++) {
127         char tcbuf[AV_TIMECODE_STR_SIZE];
128         av_timecode_make_smpte_tc_string2(tcbuf, frame_rate, tc[j], 0, 0);
129         av_log(ctx, AV_LOG_INFO, "timecode - %s%s", tcbuf, j != tc[0]  ? ", " : "");
130     }
131 }
132
133 static void dump_roi(AVFilterContext *ctx, AVFrameSideData *sd)
134 {
135     int nb_rois;
136     const AVRegionOfInterest *roi;
137     uint32_t roi_size;
138
139     roi = (const AVRegionOfInterest *)sd->data;
140     roi_size = roi->self_size;
141     if (!roi_size || sd->size % roi_size != 0) {
142         av_log(ctx, AV_LOG_ERROR, "Invalid AVRegionOfInterest.self_size.\n");
143         return;
144     }
145     nb_rois = sd->size / roi_size;
146
147     av_log(ctx, AV_LOG_INFO, "Regions Of Interest(RoI) information: ");
148     for (int i = 0; i < nb_rois; i++) {
149         roi = (const AVRegionOfInterest *)(sd->data + roi_size * i);
150         av_log(ctx, AV_LOG_INFO, "index: %d, region: (%d, %d)/(%d, %d), qp offset: %d/%d.\n",
151                i, roi->left, roi->top, roi->right, roi->bottom, roi->qoffset.num, roi->qoffset.den);
152     }
153 }
154
155 static void dump_mastering_display(AVFilterContext *ctx, AVFrameSideData *sd)
156 {
157     AVMasteringDisplayMetadata *mastering_display;
158
159     av_log(ctx, AV_LOG_INFO, "mastering display: ");
160     if (sd->size < sizeof(*mastering_display)) {
161         av_log(ctx, AV_LOG_ERROR, "invalid data\n");
162         return;
163     }
164
165     mastering_display = (AVMasteringDisplayMetadata *)sd->data;
166
167     av_log(ctx, AV_LOG_INFO, "has_primaries:%d has_luminance:%d "
168            "r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f) "
169            "min_luminance=%f, max_luminance=%f",
170            mastering_display->has_primaries, mastering_display->has_luminance,
171            av_q2d(mastering_display->display_primaries[0][0]),
172            av_q2d(mastering_display->display_primaries[0][1]),
173            av_q2d(mastering_display->display_primaries[1][0]),
174            av_q2d(mastering_display->display_primaries[1][1]),
175            av_q2d(mastering_display->display_primaries[2][0]),
176            av_q2d(mastering_display->display_primaries[2][1]),
177            av_q2d(mastering_display->white_point[0]), av_q2d(mastering_display->white_point[1]),
178            av_q2d(mastering_display->min_luminance), av_q2d(mastering_display->max_luminance));
179 }
180
181 static void dump_content_light_metadata(AVFilterContext *ctx, AVFrameSideData *sd)
182 {
183     AVContentLightMetadata* metadata = (AVContentLightMetadata*)sd->data;
184
185     av_log(ctx, AV_LOG_INFO, "Content Light Level information: "
186            "MaxCLL=%d, MaxFALL=%d",
187            metadata->MaxCLL, metadata->MaxFALL);
188 }
189
190 static void dump_video_enc_params(AVFilterContext *ctx, AVFrameSideData *sd)
191 {
192     AVVideoEncParams *par = (AVVideoEncParams*)sd->data;
193     int plane, acdc;
194
195     av_log(ctx, AV_LOG_INFO, "video encoding parameters: type %d; ", par->type);
196     if (par->qp)
197         av_log(ctx, AV_LOG_INFO, "qp=%d; ", par->qp);
198     for (plane = 0; plane < FF_ARRAY_ELEMS(par->delta_qp); plane++)
199         for (acdc = 0; acdc < FF_ARRAY_ELEMS(par->delta_qp[plane]); acdc++) {
200             int delta_qp = par->delta_qp[plane][acdc];
201             if (delta_qp)
202                 av_log(ctx, AV_LOG_INFO, "delta_qp[%d][%d]=%d; ",
203                        plane, acdc, delta_qp);
204         }
205     if (par->nb_blocks)
206         av_log(ctx, AV_LOG_INFO, "%u blocks; ", par->nb_blocks);
207 }
208
209 static void dump_sei_unregistered_metadata(AVFilterContext *ctx, AVFrameSideData *sd)
210 {
211     const int uuid_size = 16;
212     uint8_t *user_data = sd->data;
213     int i;
214
215     if (sd->size < uuid_size) {
216         av_log(ctx, AV_LOG_ERROR, "invalid data(%d < UUID(%d-bytes))\n", sd->size, uuid_size);
217         return;
218     }
219
220     av_log(ctx, AV_LOG_INFO, "User Data Unregistered:\n");
221     av_log(ctx, AV_LOG_INFO, "UUID=");
222     for (i = 0; i < uuid_size; i++) {
223         av_log(ctx, AV_LOG_INFO, "%02x", user_data[i]);
224         if (i == 3 || i == 5 || i == 7 || i == 9)
225             av_log(ctx, AV_LOG_INFO, "-");
226     }
227     av_log(ctx, AV_LOG_INFO, "\n");
228
229     av_log(ctx, AV_LOG_INFO, "User Data=");
230     for (; i < sd->size; i++) {
231         av_log(ctx, AV_LOG_INFO, "%02x", user_data[i]);
232     }
233     av_log(ctx, AV_LOG_INFO, "\n");
234 }
235
236 static void dump_color_property(AVFilterContext *ctx, AVFrame *frame)
237 {
238     const char *color_range_str     = av_color_range_name(frame->color_range);
239     const char *colorspace_str      = av_color_space_name(frame->colorspace);
240     const char *color_primaries_str = av_color_primaries_name(frame->color_primaries);
241     const char *color_trc_str       = av_color_transfer_name(frame->color_trc);
242
243     if (!color_range_str || frame->color_range == AVCOL_RANGE_UNSPECIFIED) {
244         av_log(ctx, AV_LOG_INFO, "color_range:unknown");
245     } else {
246         av_log(ctx, AV_LOG_INFO, "color_range:%s", color_range_str);
247     }
248
249     if (!colorspace_str || frame->colorspace == AVCOL_SPC_UNSPECIFIED) {
250         av_log(ctx, AV_LOG_INFO, " color_space:unknown");
251     } else {
252         av_log(ctx, AV_LOG_INFO, " color_space:%s", colorspace_str);
253     }
254
255     if (!color_primaries_str || frame->color_primaries == AVCOL_PRI_UNSPECIFIED) {
256         av_log(ctx, AV_LOG_INFO, " color_primaries:unknown");
257     } else {
258         av_log(ctx, AV_LOG_INFO, " color_primaries:%s", color_primaries_str);
259     }
260
261     if (!color_trc_str || frame->color_trc == AVCOL_TRC_UNSPECIFIED) {
262         av_log(ctx, AV_LOG_INFO, " color_trc:unknown");
263     } else {
264         av_log(ctx, AV_LOG_INFO, " color_trc:%s", color_trc_str);
265     }
266     av_log(ctx, AV_LOG_INFO, "\n");
267 }
268
269 static void update_sample_stats_8(const uint8_t *src, int len, int64_t *sum, int64_t *sum2)
270 {
271     int i;
272
273     for (i = 0; i < len; i++) {
274         *sum += src[i];
275         *sum2 += src[i] * src[i];
276     }
277 }
278
279 static void update_sample_stats_16(int be, const uint8_t *src, int len, int64_t *sum, int64_t *sum2)
280 {
281     const uint16_t *src1 = (const uint16_t *)src;
282     int i;
283
284     for (i = 0; i < len / 2; i++) {
285         if ((HAVE_BIGENDIAN && !be) || (!HAVE_BIGENDIAN && be)) {
286             *sum += av_bswap16(src1[i]);
287             *sum2 += (uint32_t)av_bswap16(src1[i]) * (uint32_t)av_bswap16(src1[i]);
288         } else {
289             *sum += src1[i];
290             *sum2 += (uint32_t)src1[i] * (uint32_t)src1[i];
291         }
292     }
293 }
294
295 static void update_sample_stats(int depth, int be, const uint8_t *src, int len, int64_t *sum, int64_t *sum2)
296 {
297     if (depth <= 8)
298         update_sample_stats_8(src, len, sum, sum2);
299     else
300         update_sample_stats_16(be, src, len, sum, sum2);
301 }
302
303 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
304 {
305     AVFilterContext *ctx = inlink->dst;
306     ShowInfoContext *s = ctx->priv;
307     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
308     uint32_t plane_checksum[4] = {0}, checksum = 0;
309     int64_t sum[4] = {0}, sum2[4] = {0};
310     int32_t pixelcount[4] = {0};
311     int bitdepth = desc->comp[0].depth;
312     int be = desc->flags & AV_PIX_FMT_FLAG_BE;
313     int i, plane, vsub = desc->log2_chroma_h;
314
315     for (plane = 0; plane < 4 && s->calculate_checksums && frame->data[plane] && frame->linesize[plane]; plane++) {
316         uint8_t *data = frame->data[plane];
317         int h = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT(inlink->h, vsub) : inlink->h;
318         int linesize = av_image_get_linesize(frame->format, frame->width, plane);
319         int width = linesize >> (bitdepth > 8);
320
321         if (linesize < 0)
322             return linesize;
323
324         for (i = 0; i < h; i++) {
325             plane_checksum[plane] = av_adler32_update(plane_checksum[plane], data, linesize);
326             checksum = av_adler32_update(checksum, data, linesize);
327
328             update_sample_stats(bitdepth, be, data, linesize, sum+plane, sum2+plane);
329             pixelcount[plane] += width;
330             data += frame->linesize[plane];
331         }
332     }
333
334     av_log(ctx, AV_LOG_INFO,
335            "n:%4"PRId64" pts:%7s pts_time:%-7s pos:%9"PRId64" "
336            "fmt:%s sar:%d/%d s:%dx%d i:%c iskey:%d type:%c ",
337            inlink->frame_count_out,
338            av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base), frame->pkt_pos,
339            desc->name,
340            frame->sample_aspect_ratio.num, frame->sample_aspect_ratio.den,
341            frame->width, frame->height,
342            !frame->interlaced_frame ? 'P' :         /* Progressive  */
343            frame->top_field_first   ? 'T' : 'B',    /* Top / Bottom */
344            frame->key_frame,
345            av_get_picture_type_char(frame->pict_type));
346
347     if (s->calculate_checksums) {
348         av_log(ctx, AV_LOG_INFO,
349                "checksum:%08"PRIX32" plane_checksum:[%08"PRIX32,
350                checksum, plane_checksum[0]);
351
352         for (plane = 1; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++)
353             av_log(ctx, AV_LOG_INFO, " %08"PRIX32, plane_checksum[plane]);
354         av_log(ctx, AV_LOG_INFO, "] mean:[");
355         for (plane = 0; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++)
356             av_log(ctx, AV_LOG_INFO, "%"PRId64" ", (sum[plane] + pixelcount[plane]/2) / pixelcount[plane]);
357         av_log(ctx, AV_LOG_INFO, "\b] stdev:[");
358         for (plane = 0; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++)
359             av_log(ctx, AV_LOG_INFO, "%3.1f ",
360                    sqrt((sum2[plane] - sum[plane]*(double)sum[plane]/pixelcount[plane])/pixelcount[plane]));
361         av_log(ctx, AV_LOG_INFO, "\b]");
362     }
363     av_log(ctx, AV_LOG_INFO, "\n");
364
365     for (i = 0; i < frame->nb_side_data; i++) {
366         AVFrameSideData *sd = frame->side_data[i];
367
368         av_log(ctx, AV_LOG_INFO, "  side data - ");
369         switch (sd->type) {
370         case AV_FRAME_DATA_PANSCAN:
371             av_log(ctx, AV_LOG_INFO, "pan/scan");
372             break;
373         case AV_FRAME_DATA_A53_CC:
374             av_log(ctx, AV_LOG_INFO, "A/53 closed captions (%d bytes)", sd->size);
375             break;
376         case AV_FRAME_DATA_SPHERICAL:
377             dump_spherical(ctx, frame, sd);
378             break;
379         case AV_FRAME_DATA_STEREO3D:
380             dump_stereo3d(ctx, sd);
381             break;
382         case AV_FRAME_DATA_S12M_TIMECODE: {
383             dump_s12m_timecode(ctx, inlink->frame_rate, sd);
384             break;
385         }
386         case AV_FRAME_DATA_DISPLAYMATRIX:
387             av_log(ctx, AV_LOG_INFO, "displaymatrix: rotation of %.2f degrees",
388                    av_display_rotation_get((int32_t *)sd->data));
389             break;
390         case AV_FRAME_DATA_AFD:
391             av_log(ctx, AV_LOG_INFO, "afd: value of %"PRIu8, sd->data[0]);
392             break;
393         case AV_FRAME_DATA_REGIONS_OF_INTEREST:
394             dump_roi(ctx, sd);
395             break;
396         case AV_FRAME_DATA_MASTERING_DISPLAY_METADATA:
397             dump_mastering_display(ctx, sd);
398             break;
399         case AV_FRAME_DATA_CONTENT_LIGHT_LEVEL:
400             dump_content_light_metadata(ctx, sd);
401             break;
402         case AV_FRAME_DATA_GOP_TIMECODE: {
403             char tcbuf[AV_TIMECODE_STR_SIZE];
404             av_timecode_make_mpeg_tc_string(tcbuf, *(int64_t *)(sd->data));
405             av_log(ctx, AV_LOG_INFO, "GOP timecode - %s", tcbuf);
406             break;
407         }
408         case AV_FRAME_DATA_VIDEO_ENC_PARAMS:
409             dump_video_enc_params(ctx, sd);
410             break;
411         case AV_FRAME_DATA_SEI_UNREGISTERED:
412             dump_sei_unregistered_metadata(ctx, sd);
413             break;
414         default:
415             av_log(ctx, AV_LOG_WARNING, "unknown side data type %d (%d bytes)\n",
416                    sd->type, sd->size);
417             break;
418         }
419
420         av_log(ctx, AV_LOG_INFO, "\n");
421     }
422
423     dump_color_property(ctx, frame);
424
425     return ff_filter_frame(inlink->dst->outputs[0], frame);
426 }
427
428 static int config_props(AVFilterContext *ctx, AVFilterLink *link, int is_out)
429 {
430
431     av_log(ctx, AV_LOG_INFO, "config %s time_base: %d/%d, frame_rate: %d/%d\n",
432            is_out ? "out" : "in",
433            link->time_base.num, link->time_base.den,
434            link->frame_rate.num, link->frame_rate.den);
435
436     return 0;
437 }
438
439 static int config_props_in(AVFilterLink *link)
440 {
441     AVFilterContext *ctx = link->dst;
442     return config_props(ctx, link, 0);
443 }
444
445 static int config_props_out(AVFilterLink *link)
446 {
447     AVFilterContext *ctx = link->src;
448     return config_props(ctx, link, 1);
449 }
450
451 static const AVFilterPad avfilter_vf_showinfo_inputs[] = {
452     {
453         .name             = "default",
454         .type             = AVMEDIA_TYPE_VIDEO,
455         .filter_frame     = filter_frame,
456         .config_props     = config_props_in,
457     },
458     { NULL }
459 };
460
461 static const AVFilterPad avfilter_vf_showinfo_outputs[] = {
462     {
463         .name = "default",
464         .type = AVMEDIA_TYPE_VIDEO,
465         .config_props  = config_props_out,
466     },
467     { NULL }
468 };
469
470 AVFilter ff_vf_showinfo = {
471     .name        = "showinfo",
472     .description = NULL_IF_CONFIG_SMALL("Show textual information for each video frame."),
473     .inputs      = avfilter_vf_showinfo_inputs,
474     .outputs     = avfilter_vf_showinfo_outputs,
475     .priv_size   = sizeof(ShowInfoContext),
476     .priv_class  = &showinfo_class,
477 };