]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_showinfo.c
Merge commit 'eb8a8115994434b548523cf0bca6a4a74784e79c'
[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/adler32.h"
28 #include "libavutil/display.h"
29 #include "libavutil/imgutils.h"
30 #include "libavutil/internal.h"
31 #include "libavutil/opt.h"
32 #include "libavutil/pixdesc.h"
33 #include "libavutil/spherical.h"
34 #include "libavutil/stereo3d.h"
35 #include "libavutil/timestamp.h"
36 #include "libavutil/timecode.h"
37
38 #include "avfilter.h"
39 #include "internal.h"
40 #include "video.h"
41
42 typedef struct ShowInfoContext {
43     const AVClass *class;
44     int calculate_checksums;
45 } ShowInfoContext;
46
47 #define OFFSET(x) offsetof(ShowInfoContext, x)
48 #define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
49
50 static const AVOption showinfo_options[] = {
51     { "checksum", "calculate checksums", OFFSET(calculate_checksums), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, VF },
52     { NULL }
53 };
54
55 AVFILTER_DEFINE_CLASS(showinfo);
56
57 static void dump_spherical(AVFilterContext *ctx, AVFrame *frame, AVFrameSideData *sd)
58 {
59     AVSphericalMapping *spherical = (AVSphericalMapping *)sd->data;
60     double yaw, pitch, roll;
61
62     av_log(ctx, AV_LOG_INFO, "spherical information: ");
63     if (sd->size < sizeof(*spherical)) {
64         av_log(ctx, AV_LOG_INFO, "invalid data");
65         return;
66     }
67
68     if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR)
69         av_log(ctx, AV_LOG_INFO, "equirectangular ");
70     else if (spherical->projection == AV_SPHERICAL_CUBEMAP)
71         av_log(ctx, AV_LOG_INFO, "cubemap ");
72     else if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE)
73         av_log(ctx, AV_LOG_INFO, "tiled equirectangular ");
74     else {
75         av_log(ctx, AV_LOG_WARNING, "unknown");
76         return;
77     }
78
79     yaw = ((double)spherical->yaw) / (1 << 16);
80     pitch = ((double)spherical->pitch) / (1 << 16);
81     roll = ((double)spherical->roll) / (1 << 16);
82     av_log(ctx, AV_LOG_INFO, "(%f/%f/%f) ", yaw, pitch, roll);
83
84     if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) {
85         size_t l, t, r, b;
86         av_spherical_tile_bounds(spherical, frame->width, frame->height,
87                                  &l, &t, &r, &b);
88         av_log(ctx, AV_LOG_INFO,
89                "[%"SIZE_SPECIFIER", %"SIZE_SPECIFIER", %"SIZE_SPECIFIER", %"SIZE_SPECIFIER"] ",
90                l, t, r, b);
91     } else if (spherical->projection == AV_SPHERICAL_CUBEMAP) {
92         av_log(ctx, AV_LOG_INFO, "[pad %"PRIu32"] ", spherical->padding);
93     }
94 }
95
96 static void dump_stereo3d(AVFilterContext *ctx, AVFrameSideData *sd)
97 {
98     AVStereo3D *stereo;
99
100     av_log(ctx, AV_LOG_INFO, "stereoscopic information: ");
101     if (sd->size < sizeof(*stereo)) {
102         av_log(ctx, AV_LOG_INFO, "invalid data");
103         return;
104     }
105
106     stereo = (AVStereo3D *)sd->data;
107
108     av_log(ctx, AV_LOG_INFO, "type - %s", av_stereo3d_type_name(stereo->type));
109
110     if (stereo->flags & AV_STEREO3D_FLAG_INVERT)
111         av_log(ctx, AV_LOG_INFO, " (inverted)");
112 }
113
114 static void dump_color_property(AVFilterContext *ctx, AVFrame *frame)
115 {
116     const char *color_range_str     = av_color_range_name(frame->color_range);
117     const char *colorspace_str      = av_color_space_name(frame->colorspace);
118     const char *color_primaries_str = av_color_primaries_name(frame->color_primaries);
119     const char *color_trc_str       = av_color_transfer_name(frame->color_trc);
120
121     if (!color_range_str || frame->color_range == AVCOL_RANGE_UNSPECIFIED) {
122         av_log(ctx, AV_LOG_INFO, "color_range:unknown");
123     } else {
124         av_log(ctx, AV_LOG_INFO, "color_range:%s", color_range_str);
125     }
126
127     if (!colorspace_str || frame->colorspace == AVCOL_SPC_UNSPECIFIED) {
128         av_log(ctx, AV_LOG_INFO, " color_space:unknown");
129     } else {
130         av_log(ctx, AV_LOG_INFO, " color_space:%s", colorspace_str);
131     }
132
133     if (!color_primaries_str || frame->color_primaries == AVCOL_PRI_UNSPECIFIED) {
134         av_log(ctx, AV_LOG_INFO, " color_primaries:unknown");
135     } else {
136         av_log(ctx, AV_LOG_INFO, " color_primaries:%s", color_primaries_str);
137     }
138
139     if (!color_trc_str || frame->color_trc == AVCOL_TRC_UNSPECIFIED) {
140         av_log(ctx, AV_LOG_INFO, " color_trc:unknown");
141     } else {
142         av_log(ctx, AV_LOG_INFO, " color_trc:%s", color_trc_str);
143     }
144     av_log(ctx, AV_LOG_INFO, "\n");
145 }
146
147 static void update_sample_stats(const uint8_t *src, int len, int64_t *sum, int64_t *sum2)
148 {
149     int i;
150
151     for (i = 0; i < len; i++) {
152         *sum += src[i];
153         *sum2 += src[i] * src[i];
154     }
155 }
156
157 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
158 {
159     AVFilterContext *ctx = inlink->dst;
160     ShowInfoContext *s = ctx->priv;
161     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
162     uint32_t plane_checksum[4] = {0}, checksum = 0;
163     int64_t sum[4] = {0}, sum2[4] = {0};
164     int32_t pixelcount[4] = {0};
165     int i, plane, vsub = desc->log2_chroma_h;
166
167     for (plane = 0; plane < 4 && s->calculate_checksums && frame->data[plane] && frame->linesize[plane]; plane++) {
168         uint8_t *data = frame->data[plane];
169         int h = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT(inlink->h, vsub) : inlink->h;
170         int linesize = av_image_get_linesize(frame->format, frame->width, plane);
171
172         if (linesize < 0)
173             return linesize;
174
175         for (i = 0; i < h; i++) {
176             plane_checksum[plane] = av_adler32_update(plane_checksum[plane], data, linesize);
177             checksum = av_adler32_update(checksum, data, linesize);
178
179             update_sample_stats(data, linesize, sum+plane, sum2+plane);
180             pixelcount[plane] += linesize;
181             data += frame->linesize[plane];
182         }
183     }
184
185     av_log(ctx, AV_LOG_INFO,
186            "n:%4"PRId64" pts:%7s pts_time:%-7s pos:%9"PRId64" "
187            "fmt:%s sar:%d/%d s:%dx%d i:%c iskey:%d type:%c ",
188            inlink->frame_count_out,
189            av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base), frame->pkt_pos,
190            desc->name,
191            frame->sample_aspect_ratio.num, frame->sample_aspect_ratio.den,
192            frame->width, frame->height,
193            !frame->interlaced_frame ? 'P' :         /* Progressive  */
194            frame->top_field_first   ? 'T' : 'B',    /* Top / Bottom */
195            frame->key_frame,
196            av_get_picture_type_char(frame->pict_type));
197
198     if (s->calculate_checksums) {
199         av_log(ctx, AV_LOG_INFO,
200                "checksum:%08"PRIX32" plane_checksum:[%08"PRIX32,
201                checksum, plane_checksum[0]);
202
203         for (plane = 1; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++)
204             av_log(ctx, AV_LOG_INFO, " %08"PRIX32, plane_checksum[plane]);
205         av_log(ctx, AV_LOG_INFO, "] mean:[");
206         for (plane = 0; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++)
207             av_log(ctx, AV_LOG_INFO, "%"PRId64" ", (sum[plane] + pixelcount[plane]/2) / pixelcount[plane]);
208         av_log(ctx, AV_LOG_INFO, "\b] stdev:[");
209         for (plane = 0; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++)
210             av_log(ctx, AV_LOG_INFO, "%3.1f ",
211                    sqrt((sum2[plane] - sum[plane]*(double)sum[plane]/pixelcount[plane])/pixelcount[plane]));
212         av_log(ctx, AV_LOG_INFO, "\b]");
213     }
214     av_log(ctx, AV_LOG_INFO, "\n");
215
216     for (i = 0; i < frame->nb_side_data; i++) {
217         AVFrameSideData *sd = frame->side_data[i];
218
219         av_log(ctx, AV_LOG_INFO, "  side data - ");
220         switch (sd->type) {
221         case AV_FRAME_DATA_PANSCAN:
222             av_log(ctx, AV_LOG_INFO, "pan/scan");
223             break;
224         case AV_FRAME_DATA_A53_CC:
225             av_log(ctx, AV_LOG_INFO, "A/53 closed captions (%d bytes)", sd->size);
226             break;
227         case AV_FRAME_DATA_SPHERICAL:
228             dump_spherical(ctx, frame, sd);
229             break;
230         case AV_FRAME_DATA_STEREO3D:
231             dump_stereo3d(ctx, sd);
232             break;
233         case AV_FRAME_DATA_S12M_TIMECODE: {
234             uint32_t *tc = (uint32_t*)sd->data;
235             for (int j = 1; j <= tc[0]; j++) {
236                 char tcbuf[AV_TIMECODE_STR_SIZE];
237                 av_timecode_make_smpte_tc_string(tcbuf, tc[j], 0);
238                 av_log(ctx, AV_LOG_INFO, "timecode - %s%s", tcbuf, j != tc[0] ? ", " : "");
239             }
240             break;
241         }
242         case AV_FRAME_DATA_DISPLAYMATRIX:
243             av_log(ctx, AV_LOG_INFO, "displaymatrix: rotation of %.2f degrees",
244                    av_display_rotation_get((int32_t *)sd->data));
245             break;
246         case AV_FRAME_DATA_AFD:
247             av_log(ctx, AV_LOG_INFO, "afd: value of %"PRIu8, sd->data[0]);
248             break;
249         default:
250             av_log(ctx, AV_LOG_WARNING, "unknown side data type %d (%d bytes)",
251                    sd->type, sd->size);
252             break;
253         }
254
255         av_log(ctx, AV_LOG_INFO, "\n");
256     }
257
258     dump_color_property(ctx, frame);
259
260     return ff_filter_frame(inlink->dst->outputs[0], frame);
261 }
262
263 static int config_props(AVFilterContext *ctx, AVFilterLink *link, int is_out)
264 {
265
266     av_log(ctx, AV_LOG_INFO, "config %s time_base: %d/%d, frame_rate: %d/%d\n",
267            is_out ? "out" : "in",
268            link->time_base.num, link->time_base.den,
269            link->frame_rate.num, link->frame_rate.den);
270
271     return 0;
272 }
273
274 static int config_props_in(AVFilterLink *link)
275 {
276     AVFilterContext *ctx = link->dst;
277     return config_props(ctx, link, 0);
278 }
279
280 static int config_props_out(AVFilterLink *link)
281 {
282     AVFilterContext *ctx = link->src;
283     return config_props(ctx, link, 1);
284 }
285
286 static const AVFilterPad avfilter_vf_showinfo_inputs[] = {
287     {
288         .name             = "default",
289         .type             = AVMEDIA_TYPE_VIDEO,
290         .filter_frame     = filter_frame,
291         .config_props     = config_props_in,
292     },
293     { NULL }
294 };
295
296 static const AVFilterPad avfilter_vf_showinfo_outputs[] = {
297     {
298         .name = "default",
299         .type = AVMEDIA_TYPE_VIDEO,
300         .config_props  = config_props_out,
301     },
302     { NULL }
303 };
304
305 AVFilter ff_vf_showinfo = {
306     .name        = "showinfo",
307     .description = NULL_IF_CONFIG_SMALL("Show textual information for each video frame."),
308     .inputs      = avfilter_vf_showinfo_inputs,
309     .outputs     = avfilter_vf_showinfo_outputs,
310     .priv_size   = sizeof(ShowInfoContext),
311     .priv_class  = &showinfo_class,
312 };