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 <avcodec.h> |
---|
22 | #include <avformat.h> |
---|
23 | #include <swscale.h> |
---|
24 | |
---|
25 | #include <pipi.h> |
---|
26 | |
---|
27 | #define STEP 20 |
---|
28 | |
---|
29 | #define TWIDTH 90 |
---|
30 | #define THEIGHT 60 |
---|
31 | #define TCOLS 10 |
---|
32 | #define TROWS 200 |
---|
33 | #define NTHUMBS (TCOLS*TROWS) |
---|
34 | |
---|
35 | int main(int argc, char *argv[]) |
---|
36 | { |
---|
37 | char fmtstr[1024]; |
---|
38 | AVPacket packet; |
---|
39 | AVFormatContext *fmt; |
---|
40 | AVCodecContext *ctx; |
---|
41 | AVCodec *codec; |
---|
42 | AVFrame *frame; |
---|
43 | struct SwsContext *sws; |
---|
44 | pipi_image_t *image; |
---|
45 | pipi_pixels_t *p; |
---|
46 | uint8_t *buffer; |
---|
47 | char *parser; |
---|
48 | int stream, i, n = 0, k = 0, idx = 0; |
---|
49 | |
---|
50 | if(argc < 2) |
---|
51 | return EXIT_FAILURE; |
---|
52 | |
---|
53 | parser = strrchr(argv[1], '/'); |
---|
54 | strcpy(fmtstr, parser ? parser + 1 : argv[1]); |
---|
55 | parser = strrchr(fmtstr, '.'); |
---|
56 | if(parser) |
---|
57 | *parser = '\0'; |
---|
58 | strcat(fmtstr, ".t%02i.jpeg"); |
---|
59 | |
---|
60 | image = pipi_new(TWIDTH * TCOLS, THEIGHT * TROWS); |
---|
61 | p = pipi_get_pixels(image, PIPI_PIXELS_RGBA_U8); |
---|
62 | buffer = (uint8_t *)p->pixels; |
---|
63 | |
---|
64 | av_register_all(); |
---|
65 | if(av_open_input_file(&fmt, argv[1], NULL, 0, NULL) != 0) |
---|
66 | return EXIT_FAILURE; |
---|
67 | if(av_find_stream_info(fmt) < 0 ) |
---|
68 | return EXIT_FAILURE; |
---|
69 | |
---|
70 | stream = -1; |
---|
71 | for(i = 0; stream == -1 && i < (int)fmt->nb_streams; i++) |
---|
72 | if(fmt->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) |
---|
73 | stream = i; |
---|
74 | if(stream == -1) |
---|
75 | return EXIT_FAILURE; |
---|
76 | ctx = fmt->streams[stream]->codec; |
---|
77 | |
---|
78 | codec = avcodec_find_decoder(ctx->codec_id); |
---|
79 | if(codec == NULL) |
---|
80 | return EXIT_FAILURE; |
---|
81 | if(avcodec_open(ctx, codec) < 0) |
---|
82 | return EXIT_FAILURE; |
---|
83 | |
---|
84 | frame = avcodec_alloc_frame(); |
---|
85 | |
---|
86 | sws = sws_getContext(ctx->width, ctx->height, ctx->pix_fmt, |
---|
87 | TWIDTH, THEIGHT, PIX_FMT_RGB32, |
---|
88 | SWS_BICUBIC, NULL, NULL, NULL); |
---|
89 | |
---|
90 | for(;;) |
---|
91 | { |
---|
92 | int finished, ret; |
---|
93 | |
---|
94 | ret = av_read_frame(fmt, &packet); |
---|
95 | |
---|
96 | if(idx == NTHUMBS || (idx > 0 && ret < 0)) |
---|
97 | { |
---|
98 | /* Only process every 20th image */ |
---|
99 | char buf[1024]; |
---|
100 | sprintf(buf, fmtstr, k++); |
---|
101 | printf("saving %i thumbs in %s\n", idx, buf); |
---|
102 | pipi_save(image, buf); |
---|
103 | memset(buffer, 0, TWIDTH * TCOLS * THEIGHT * TROWS * 4); |
---|
104 | idx = 0; |
---|
105 | } |
---|
106 | |
---|
107 | if(ret < 0) |
---|
108 | break; |
---|
109 | |
---|
110 | if(packet.stream_index != stream) |
---|
111 | { |
---|
112 | av_free_packet(&packet); |
---|
113 | continue; |
---|
114 | } |
---|
115 | |
---|
116 | avcodec_decode_video(ctx, frame, &finished, packet.data, packet.size); |
---|
117 | if(!finished) |
---|
118 | { |
---|
119 | av_free_packet(&packet); |
---|
120 | continue; |
---|
121 | } |
---|
122 | |
---|
123 | n++; |
---|
124 | |
---|
125 | if((n % STEP) == STEP / 2) |
---|
126 | { |
---|
127 | int pitch = TWIDTH * TCOLS * 4; |
---|
128 | uint8_t *start; |
---|
129 | |
---|
130 | start = buffer + (idx % TCOLS) * TWIDTH * 4 |
---|
131 | + (idx / TCOLS) * TWIDTH * TCOLS * 4 * THEIGHT; |
---|
132 | |
---|
133 | sws_scale(sws, frame->data, frame->linesize, 0, ctx->height, |
---|
134 | &start, &pitch); |
---|
135 | |
---|
136 | /* Now check whether the new image is really different |
---|
137 | * from the previous one (> 8% pixel changes) */ |
---|
138 | if(idx == 0) |
---|
139 | { |
---|
140 | idx++; |
---|
141 | } |
---|
142 | else |
---|
143 | { |
---|
144 | uint8_t *prev; |
---|
145 | int x, y, t, a, b, changed = 0; |
---|
146 | |
---|
147 | if(idx % TCOLS) |
---|
148 | prev = start - TWIDTH * 4; |
---|
149 | else |
---|
150 | prev = start + (TCOLS - 1) * TWIDTH * 4 |
---|
151 | - TWIDTH * TCOLS * 4 * THEIGHT; |
---|
152 | |
---|
153 | for(y = 0; y < THEIGHT; y++) |
---|
154 | for(x = 0; x < TWIDTH; x++) |
---|
155 | { |
---|
156 | int ok = 0; |
---|
157 | |
---|
158 | for(t = 0; t < 3; t++) |
---|
159 | { |
---|
160 | int offset = y * TWIDTH * TCOLS + x; |
---|
161 | a = start[offset * 4 + t]; |
---|
162 | b = prev[offset * 4 + t]; |
---|
163 | |
---|
164 | if(a < b - 5 || a > b + 5) |
---|
165 | { |
---|
166 | ok = 1; |
---|
167 | break; |
---|
168 | } |
---|
169 | } |
---|
170 | |
---|
171 | changed += ok; |
---|
172 | } |
---|
173 | |
---|
174 | if(changed > TWIDTH * THEIGHT * 8 / 100) |
---|
175 | idx++; |
---|
176 | } |
---|
177 | } |
---|
178 | |
---|
179 | av_free_packet(&packet); |
---|
180 | } |
---|
181 | |
---|
182 | return EXIT_SUCCESS; |
---|
183 | } |
---|
184 | |
---|