Commit | Line | Data |
---|---|---|
2ba45a60 DM |
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/pixdesc.h" | |
32 | #include "libavutil/stereo3d.h" | |
33 | #include "libavutil/timestamp.h" | |
34 | ||
35 | #include "avfilter.h" | |
36 | #include "internal.h" | |
37 | #include "video.h" | |
38 | ||
39 | static void dump_stereo3d(AVFilterContext *ctx, AVFrameSideData *sd) | |
40 | { | |
41 | AVStereo3D *stereo; | |
42 | ||
43 | av_log(ctx, AV_LOG_INFO, "stereoscopic information: "); | |
44 | if (sd->size < sizeof(*stereo)) { | |
45 | av_log(ctx, AV_LOG_INFO, "invalid data"); | |
46 | return; | |
47 | } | |
48 | ||
49 | stereo = (AVStereo3D *)sd->data; | |
50 | ||
51 | av_log(ctx, AV_LOG_INFO, "type - "); | |
52 | switch (stereo->type) { | |
53 | case AV_STEREO3D_2D: av_log(ctx, AV_LOG_INFO, "2D"); break; | |
54 | case AV_STEREO3D_SIDEBYSIDE: av_log(ctx, AV_LOG_INFO, "side by side"); break; | |
55 | case AV_STEREO3D_TOPBOTTOM: av_log(ctx, AV_LOG_INFO, "top and bottom"); break; | |
56 | case AV_STEREO3D_FRAMESEQUENCE: av_log(ctx, AV_LOG_INFO, "frame alternate"); break; | |
57 | case AV_STEREO3D_CHECKERBOARD: av_log(ctx, AV_LOG_INFO, "checkerboard"); break; | |
58 | case AV_STEREO3D_LINES: av_log(ctx, AV_LOG_INFO, "interleaved lines"); break; | |
59 | case AV_STEREO3D_COLUMNS: av_log(ctx, AV_LOG_INFO, "interleaved columns"); break; | |
60 | case AV_STEREO3D_SIDEBYSIDE_QUINCUNX: av_log(ctx, AV_LOG_INFO, "side by side " | |
61 | "(quincunx subsampling)"); break; | |
62 | default: av_log(ctx, AV_LOG_WARNING, "unknown"); break; | |
63 | } | |
64 | ||
65 | if (stereo->flags & AV_STEREO3D_FLAG_INVERT) | |
66 | av_log(ctx, AV_LOG_INFO, " (inverted)"); | |
67 | } | |
68 | ||
69 | static void update_sample_stats(const uint8_t *src, int len, int64_t *sum, int64_t *sum2) | |
70 | { | |
71 | int i; | |
72 | ||
73 | for (i = 0; i < len; i++) { | |
74 | *sum += src[i]; | |
75 | *sum2 += src[i] * src[i]; | |
76 | } | |
77 | } | |
78 | ||
79 | static int filter_frame(AVFilterLink *inlink, AVFrame *frame) | |
80 | { | |
81 | AVFilterContext *ctx = inlink->dst; | |
82 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); | |
83 | uint32_t plane_checksum[4] = {0}, checksum = 0; | |
84 | int64_t sum[4] = {0}, sum2[4] = {0}; | |
85 | int32_t pixelcount[4] = {0}; | |
86 | int i, plane, vsub = desc->log2_chroma_h; | |
87 | ||
88 | for (plane = 0; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++) { | |
2ba45a60 DM |
89 | uint8_t *data = frame->data[plane]; |
90 | int h = plane == 1 || plane == 2 ? FF_CEIL_RSHIFT(inlink->h, vsub) : inlink->h; | |
f6fa7814 | 91 | int linesize = av_image_get_linesize(frame->format, frame->width, plane); |
2ba45a60 DM |
92 | |
93 | if (linesize < 0) | |
94 | return linesize; | |
95 | ||
96 | for (i = 0; i < h; i++) { | |
97 | plane_checksum[plane] = av_adler32_update(plane_checksum[plane], data, linesize); | |
98 | checksum = av_adler32_update(checksum, data, linesize); | |
99 | ||
100 | update_sample_stats(data, linesize, sum+plane, sum2+plane); | |
101 | pixelcount[plane] += linesize; | |
102 | data += frame->linesize[plane]; | |
103 | } | |
104 | } | |
105 | ||
106 | av_log(ctx, AV_LOG_INFO, | |
107 | "n:%"PRId64" pts:%s pts_time:%s pos:%"PRId64" " | |
108 | "fmt:%s sar:%d/%d s:%dx%d i:%c iskey:%d type:%c " | |
109 | "checksum:%08"PRIX32" plane_checksum:[%08"PRIX32, | |
110 | inlink->frame_count, | |
111 | av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base), av_frame_get_pkt_pos(frame), | |
112 | desc->name, | |
113 | frame->sample_aspect_ratio.num, frame->sample_aspect_ratio.den, | |
114 | frame->width, frame->height, | |
115 | !frame->interlaced_frame ? 'P' : /* Progressive */ | |
116 | frame->top_field_first ? 'T' : 'B', /* Top / Bottom */ | |
117 | frame->key_frame, | |
118 | av_get_picture_type_char(frame->pict_type), | |
119 | checksum, plane_checksum[0]); | |
120 | ||
121 | for (plane = 1; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++) | |
122 | av_log(ctx, AV_LOG_INFO, " %08"PRIX32, plane_checksum[plane]); | |
123 | av_log(ctx, AV_LOG_INFO, "] mean:["); | |
124 | for (plane = 0; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++) | |
125 | av_log(ctx, AV_LOG_INFO, "%"PRId64" ", (sum[plane] + pixelcount[plane]/2) / pixelcount[plane]); | |
126 | av_log(ctx, AV_LOG_INFO, "\b] stdev:["); | |
127 | for (plane = 0; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++) | |
128 | av_log(ctx, AV_LOG_INFO, "%3.1f ", | |
129 | sqrt((sum2[plane] - sum[plane]*(double)sum[plane]/pixelcount[plane])/pixelcount[plane])); | |
130 | av_log(ctx, AV_LOG_INFO, "\b]\n"); | |
131 | ||
132 | for (i = 0; i < frame->nb_side_data; i++) { | |
133 | AVFrameSideData *sd = frame->side_data[i]; | |
134 | ||
135 | av_log(ctx, AV_LOG_INFO, " side data - "); | |
136 | switch (sd->type) { | |
137 | case AV_FRAME_DATA_PANSCAN: | |
138 | av_log(ctx, AV_LOG_INFO, "pan/scan"); | |
139 | break; | |
140 | case AV_FRAME_DATA_A53_CC: | |
141 | av_log(ctx, AV_LOG_INFO, "A/53 closed captions (%d bytes)", sd->size); | |
142 | break; | |
143 | case AV_FRAME_DATA_STEREO3D: | |
144 | dump_stereo3d(ctx, sd); | |
145 | break; | |
146 | case AV_FRAME_DATA_DISPLAYMATRIX: | |
147 | av_log(ctx, AV_LOG_INFO, "displaymatrix: rotation of %.2f degrees", | |
148 | av_display_rotation_get((int32_t *)sd->data)); | |
149 | break; | |
150 | case AV_FRAME_DATA_AFD: | |
151 | av_log(ctx, AV_LOG_INFO, "afd: value of %"PRIu8, sd->data[0]); | |
152 | break; | |
153 | default: | |
154 | av_log(ctx, AV_LOG_WARNING, "unknown side data type %d (%d bytes)", | |
155 | sd->type, sd->size); | |
156 | break; | |
157 | } | |
158 | ||
159 | av_log(ctx, AV_LOG_INFO, "\n"); | |
160 | } | |
161 | ||
162 | return ff_filter_frame(inlink->dst->outputs[0], frame); | |
163 | } | |
164 | ||
165 | static const AVFilterPad avfilter_vf_showinfo_inputs[] = { | |
166 | { | |
167 | .name = "default", | |
168 | .type = AVMEDIA_TYPE_VIDEO, | |
169 | .filter_frame = filter_frame, | |
170 | }, | |
171 | { NULL } | |
172 | }; | |
173 | ||
174 | static const AVFilterPad avfilter_vf_showinfo_outputs[] = { | |
175 | { | |
176 | .name = "default", | |
177 | .type = AVMEDIA_TYPE_VIDEO | |
178 | }, | |
179 | { NULL } | |
180 | }; | |
181 | ||
182 | AVFilter ff_vf_showinfo = { | |
183 | .name = "showinfo", | |
184 | .description = NULL_IF_CONFIG_SMALL("Show textual information for each video frame."), | |
185 | .inputs = avfilter_vf_showinfo_inputs, | |
186 | .outputs = avfilter_vf_showinfo_outputs, | |
187 | }; |