1 | /* |
---|
2 | * storyboard generate a storyboard from a movie |
---|
3 | * Copyright (c) 2009 Sam Hocevar <sam@zoy.org> |
---|
4 | * All Rights Reserved |
---|
5 | * |
---|
6 | * $Id$ |
---|
7 | * |
---|
8 | * This program is free software. It comes without any warranty, to |
---|
9 | * the extent permitted by applicable law. You can redistribute it |
---|
10 | * and/or modify it under the terms of the Do What The Fuck You Want |
---|
11 | * To Public License, Version 2, as published by Sam Hocevar. See |
---|
12 | * http://sam.zoy.org/wtfpl/COPYING for more details. |
---|
13 | */ |
---|
14 | |
---|
15 | #include "config.h" |
---|
16 | |
---|
17 | #include <stdio.h> |
---|
18 | #include <stdlib.h> |
---|
19 | #include <string.h> |
---|
20 | |
---|
21 | #include <libavcodec/avcodec.h> |
---|
22 | #include <libavformat/avformat.h> |
---|
23 | #include <libswscale/swscale.h> |
---|
24 | |
---|
25 | #include <pipi.h> |
---|
26 | |
---|
27 | #define STEP 12 |
---|
28 | |
---|
29 | #define TWIDTH 90 |
---|
30 | #define THEIGHT 60 |
---|
31 | #define TCOLS 10 |
---|
32 | #define TROWS 50 |
---|
33 | #define NTHUMBS (TCOLS*TROWS) |
---|
34 | |
---|
35 | static int similar(uint8_t *img1, uint8_t *img2); |
---|
36 | static void decorate(uint8_t *img); |
---|
37 | |
---|
38 | int main(int argc, char *argv[]) |
---|
39 | { |
---|
40 | char fmtstr[1024]; |
---|
41 | AVPacket packet; |
---|
42 | AVFormatContext *fmt; |
---|
43 | AVCodecContext *ctx; |
---|
44 | AVCodec *codec; |
---|
45 | AVFrame *frame; |
---|
46 | struct SwsContext *sws = NULL; |
---|
47 | pipi_image_t *image; |
---|
48 | pipi_pixels_t *p; |
---|
49 | uint8_t *buffer; |
---|
50 | char *parser; |
---|
51 | int stream, i, n = 0, k = 0, idx = 0; |
---|
52 | |
---|
53 | if(argc < 2) |
---|
54 | return EXIT_FAILURE; |
---|
55 | |
---|
56 | parser = strrchr(argv[1], '/'); |
---|
57 | strcpy(fmtstr, parser ? parser + 1 : argv[1]); |
---|
58 | parser = strrchr(fmtstr, '.'); |
---|
59 | if(parser) |
---|
60 | *parser = '\0'; |
---|
61 | strcat(fmtstr, ".t%02i.jpeg"); |
---|
62 | |
---|
63 | image = pipi_new(TWIDTH * TCOLS, THEIGHT * TROWS); |
---|
64 | p = pipi_get_pixels(image, PIPI_PIXELS_RGBA_U8); |
---|
65 | buffer = (uint8_t *)p->pixels; |
---|
66 | |
---|
67 | av_register_all(); |
---|
68 | if(av_open_input_file(&fmt, argv[1], NULL, 0, NULL) != 0) |
---|
69 | return EXIT_FAILURE; |
---|
70 | if(av_find_stream_info(fmt) < 0 ) |
---|
71 | return EXIT_FAILURE; |
---|
72 | |
---|
73 | stream = -1; |
---|
74 | for(i = 0; stream == -1 && i < (int)fmt->nb_streams; i++) |
---|
75 | if(fmt->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) |
---|
76 | { |
---|
77 | stream = i; |
---|
78 | break; |
---|
79 | } |
---|
80 | if(stream == -1) |
---|
81 | return EXIT_FAILURE; |
---|
82 | ctx = fmt->streams[stream]->codec; |
---|
83 | |
---|
84 | codec = avcodec_find_decoder(ctx->codec_id); |
---|
85 | if(codec == NULL) |
---|
86 | return EXIT_FAILURE; |
---|
87 | if(avcodec_open(ctx, codec) < 0) |
---|
88 | return EXIT_FAILURE; |
---|
89 | |
---|
90 | frame = avcodec_alloc_frame(); |
---|
91 | |
---|
92 | for(;;) |
---|
93 | { |
---|
94 | int finished, ret; |
---|
95 | |
---|
96 | ret = av_read_frame(fmt, &packet); |
---|
97 | |
---|
98 | if(idx == NTHUMBS || (idx > 0 && ret < 0)) |
---|
99 | { |
---|
100 | char buf[1024]; |
---|
101 | sprintf(buf, fmtstr, k++); |
---|
102 | printf("saving %i thumbs in %s\n", idx, buf); |
---|
103 | pipi_save(image, buf); |
---|
104 | memset(buffer, 0, TWIDTH * TCOLS * THEIGHT * TROWS * 4); |
---|
105 | idx = 0; |
---|
106 | } |
---|
107 | |
---|
108 | if(ret < 0) |
---|
109 | break; |
---|
110 | |
---|
111 | if(packet.stream_index != stream) |
---|
112 | { |
---|
113 | av_free_packet(&packet); |
---|
114 | continue; |
---|
115 | } |
---|
116 | |
---|
117 | avcodec_decode_video2(ctx, frame, &finished, packet.data, packet.size); |
---|
118 | if(!finished) |
---|
119 | { |
---|
120 | av_free_packet(&packet); |
---|
121 | continue; |
---|
122 | } |
---|
123 | |
---|
124 | /* Only process every 20th image */ |
---|
125 | if((++n % STEP) == STEP / 2) |
---|
126 | { |
---|
127 | uint8_t *start; |
---|
128 | int pitch = TWIDTH * TCOLS * 4; |
---|
129 | int good = 1; |
---|
130 | |
---|
131 | start = buffer + (idx % TCOLS) * TWIDTH * 4 |
---|
132 | + (idx / TCOLS) * TWIDTH * TCOLS * 4 * THEIGHT; |
---|
133 | |
---|
134 | if(!sws) |
---|
135 | sws = sws_getContext(ctx->width, ctx->height, ctx->pix_fmt, |
---|
136 | TWIDTH, THEIGHT, PIX_FMT_RGB32, |
---|
137 | SWS_BICUBIC, NULL, NULL, NULL); |
---|
138 | |
---|
139 | sws_scale(sws, (uint8_t const **)frame->data, frame->linesize, 0, |
---|
140 | ctx->height, &start, &pitch); |
---|
141 | |
---|
142 | decorate(start); |
---|
143 | |
---|
144 | if(idx > 0) |
---|
145 | { |
---|
146 | uint8_t *prev; |
---|
147 | |
---|
148 | if(idx % TCOLS) |
---|
149 | prev = start - TWIDTH * 4; |
---|
150 | else |
---|
151 | prev = start + (TCOLS - 1) * TWIDTH * 4 |
---|
152 | - TWIDTH * TCOLS * 4 * THEIGHT; |
---|
153 | |
---|
154 | /* Now check whether the new image is really different |
---|
155 | * from the previous one (> 10% pixel changes) */ |
---|
156 | if(similar(start, prev)) |
---|
157 | good = 0; |
---|
158 | } |
---|
159 | |
---|
160 | if(good) |
---|
161 | { |
---|
162 | idx++; |
---|
163 | } |
---|
164 | } |
---|
165 | |
---|
166 | av_free_packet(&packet); |
---|
167 | } |
---|
168 | |
---|
169 | return EXIT_SUCCESS; |
---|
170 | } |
---|
171 | |
---|
172 | static int similar(uint8_t *img1, uint8_t *img2) |
---|
173 | { |
---|
174 | int x, y, t, a, b, changed = 0; |
---|
175 | |
---|
176 | for(y = 2; y < THEIGHT - 2; y++) |
---|
177 | for(x = 2; x < TWIDTH - 2; x++) |
---|
178 | { |
---|
179 | int offset = y * TWIDTH * TCOLS + x; |
---|
180 | int ok = 0; |
---|
181 | |
---|
182 | for(t = 0; t < 3; t++) |
---|
183 | { |
---|
184 | a = 2 * img1[offset * 4 + t]; |
---|
185 | a += img1[(offset - TWIDTH * TCOLS - 1) * 4 + t]; |
---|
186 | a += img1[(offset - TWIDTH * TCOLS) * 4 + t]; |
---|
187 | a += img1[(offset - TWIDTH * TCOLS + 1) * 4 + t]; |
---|
188 | a += img1[(offset - 1) * 4 + t]; |
---|
189 | a += img1[(offset + 1) * 4 + t]; |
---|
190 | a += img1[(offset + TWIDTH * TCOLS - 1) * 4 + t]; |
---|
191 | a += img1[(offset + TWIDTH * TCOLS) * 4 + t]; |
---|
192 | a += img1[(offset + TWIDTH * TCOLS + 1) * 4 + t]; |
---|
193 | a /= 10; |
---|
194 | |
---|
195 | b = 2 * img2[offset * 4 + t]; |
---|
196 | b += img2[(offset - TWIDTH * TCOLS - 1) * 4 + t]; |
---|
197 | b += img2[(offset - TWIDTH * TCOLS) * 4 + t]; |
---|
198 | b += img2[(offset - TWIDTH * TCOLS + 1) * 4 + t]; |
---|
199 | b += img2[(offset - 1) * 4 + t]; |
---|
200 | b += img2[(offset + 1) * 4 + t]; |
---|
201 | b += img2[(offset + TWIDTH * TCOLS - 1) * 4 + t]; |
---|
202 | b += img2[(offset + TWIDTH * TCOLS) * 4 + t]; |
---|
203 | b += img2[(offset + TWIDTH * TCOLS + 1) * 4 + t]; |
---|
204 | b /= 10; |
---|
205 | |
---|
206 | if(a < b - 8 || a > b + 8) |
---|
207 | { |
---|
208 | ok = 1; |
---|
209 | break; |
---|
210 | } |
---|
211 | } |
---|
212 | |
---|
213 | changed += ok; |
---|
214 | } |
---|
215 | |
---|
216 | return changed < (TWIDTH * THEIGHT * 10 / 100); |
---|
217 | } |
---|
218 | |
---|
219 | static void decorate(uint8_t *img) |
---|
220 | { |
---|
221 | static int const hi = 200; |
---|
222 | static int const lo = 50; |
---|
223 | static int const mid = 127; |
---|
224 | int x, y; |
---|
225 | |
---|
226 | for(y = 0; y < THEIGHT; y++) |
---|
227 | { |
---|
228 | img[(y * TWIDTH * TCOLS) * 4] = hi; |
---|
229 | img[(y * TWIDTH * TCOLS) * 4 + 1] = hi; |
---|
230 | img[(y * TWIDTH * TCOLS) * 4 + 2] = hi; |
---|
231 | img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4] = lo; |
---|
232 | img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 1] = lo; |
---|
233 | img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 2] = lo; |
---|
234 | } |
---|
235 | |
---|
236 | for(x = 0; x < TWIDTH; x++) |
---|
237 | { |
---|
238 | img[x * 4] = hi; |
---|
239 | img[x * 4 + 1] = hi; |
---|
240 | img[x * 4 + 2] = hi; |
---|
241 | img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4] = lo; |
---|
242 | img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4 + 1] = lo; |
---|
243 | img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4 + 2] = lo; |
---|
244 | } |
---|
245 | |
---|
246 | img[0] = (mid + hi) / 2; |
---|
247 | img[1] = (mid + hi) / 2; |
---|
248 | img[2] = (mid + hi) / 2; |
---|
249 | |
---|
250 | img[(TWIDTH - 1) * 4 + 0] = mid; |
---|
251 | img[(TWIDTH - 1) * 4 + 1] = mid; |
---|
252 | img[(TWIDTH - 1) * 4 + 2] = mid; |
---|
253 | |
---|
254 | img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 0] = mid; |
---|
255 | img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 1] = mid; |
---|
256 | img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 2] = mid; |
---|
257 | |
---|
258 | img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 0] = (mid + lo) / 2; |
---|
259 | img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 1] = (mid + lo) / 2; |
---|
260 | img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 2] = (mid + lo) / 2; |
---|
261 | } |
---|
262 | |
---|