source: libpipi/trunk/examples/storyboard.c @ 3352

Last change on this file since 3352 was 3352, checked in by Sam Hocevar, 12 years ago

storyboard.c: add decorations around thumbnails

File size: 7.3 KB
Line 
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 12
28
29#define TWIDTH 90
30#define THEIGHT 60
31#define TCOLS 10
32#define TROWS 50
33#define NTHUMBS (TCOLS*TROWS)
34
35static int similar(uint8_t *img1, uint8_t *img2);
36static void decorate(uint8_t *img);
37
38int 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            stream = i;
77    if(stream == -1)
78        return EXIT_FAILURE;
79    ctx = fmt->streams[stream]->codec;
80
81    codec = avcodec_find_decoder(ctx->codec_id);
82    if(codec == NULL)
83        return EXIT_FAILURE;
84    if(avcodec_open(ctx, codec) < 0)
85        return EXIT_FAILURE;
86
87    frame = avcodec_alloc_frame();
88
89    for(;;)
90    {
91        int finished, ret;
92
93        ret = av_read_frame(fmt, &packet);
94
95        if(idx == NTHUMBS || (idx > 0 && ret < 0))
96        {
97            char buf[1024];
98            sprintf(buf, fmtstr, k++);
99            printf("saving %i thumbs in %s\n", idx, buf);
100            pipi_save(image, buf);
101            memset(buffer, 0, TWIDTH * TCOLS * THEIGHT * TROWS * 4);
102            idx = 0;
103        }
104
105        if(ret < 0)
106            break;
107
108        if(packet.stream_index != stream)
109        {
110            av_free_packet(&packet);
111            continue;
112        }
113
114        avcodec_decode_video(ctx, frame, &finished, packet.data, packet.size);
115        if(!finished)
116        {
117            av_free_packet(&packet);
118            continue;
119        }
120
121        /* Only process every 20th image */
122        if((++n % STEP) == STEP / 2)
123        {
124            uint8_t *start;
125            int pitch = TWIDTH * TCOLS * 4;
126            int good = 1;
127
128            start = buffer + (idx % TCOLS) * TWIDTH * 4
129                           + (idx / TCOLS) * TWIDTH * TCOLS * 4 * THEIGHT;
130
131            if(!sws)
132                sws = sws_getContext(ctx->width, ctx->height, ctx->pix_fmt,
133                                     TWIDTH, THEIGHT, PIX_FMT_RGB32,
134                                     SWS_BICUBIC, NULL, NULL, NULL);
135
136            sws_scale(sws, frame->data, frame->linesize, 0, ctx->height,
137                      &start, &pitch);
138
139            decorate(start);
140
141            if(idx > 0)
142            {
143                uint8_t *prev;
144
145                if(idx % TCOLS)
146                    prev = start - TWIDTH * 4;
147                else
148                    prev = start + (TCOLS - 1) * TWIDTH * 4
149                                 - TWIDTH * TCOLS * 4 * THEIGHT;
150
151                /* Now check whether the new image is really different
152                 * from the previous one (> 10% pixel changes) */
153                if(similar(start, prev))
154                    good = 0;
155            }
156
157            if(good)
158            {
159                idx++;
160            }
161        }
162
163        av_free_packet(&packet);
164    }
165
166    return EXIT_SUCCESS;
167}
168
169static int similar(uint8_t *img1, uint8_t *img2)
170{
171    int x, y, t, a, b, changed = 0;
172
173    for(y = 2; y < THEIGHT - 2; y++)
174        for(x = 2; x < TWIDTH - 2; x++)
175        {
176            int offset = y * TWIDTH * TCOLS + x;
177            int ok = 0;
178
179            for(t = 0; t < 3; t++)
180            {
181                a = 2 * img1[offset * 4 + t];
182                a += img1[(offset - TWIDTH * TCOLS - 1) * 4 + t];
183                a += img1[(offset - TWIDTH * TCOLS) * 4 + t];
184                a += img1[(offset - TWIDTH * TCOLS + 1) * 4 + t];
185                a += img1[(offset - 1) * 4 + t];
186                a += img1[(offset + 1) * 4 + t];
187                a += img1[(offset + TWIDTH * TCOLS - 1) * 4 + t];
188                a += img1[(offset + TWIDTH * TCOLS) * 4 + t];
189                a += img1[(offset + TWIDTH * TCOLS + 1) * 4 + t];
190                a /= 10;
191
192                b = 2 * img2[offset * 4 + t];
193                b += img2[(offset - TWIDTH * TCOLS - 1) * 4 + t];
194                b += img2[(offset - TWIDTH * TCOLS) * 4 + t];
195                b += img2[(offset - TWIDTH * TCOLS + 1) * 4 + t];
196                b += img2[(offset - 1) * 4 + t];
197                b += img2[(offset + 1) * 4 + t];
198                b += img2[(offset + TWIDTH * TCOLS - 1) * 4 + t];
199                b += img2[(offset + TWIDTH * TCOLS) * 4 + t];
200                b += img2[(offset + TWIDTH * TCOLS + 1) * 4 + t];
201                b /= 10;
202
203                if(a < b - 8 || a > b + 8)
204                {
205                    ok = 1;
206                    break;
207                }
208            }
209
210            changed += ok;
211        }
212
213    return changed < (TWIDTH * THEIGHT * 10 / 100);
214}
215
216static void decorate(uint8_t *img)
217{
218    static int const hi = 200;
219    static int const lo = 50;
220    static int const mid = (hi + lo) / 2;
221    int x, y;
222
223    for(y = 0; y < THEIGHT; y++)
224    {
225        img[(y * TWIDTH * TCOLS) * 4] = hi;
226        img[(y * TWIDTH * TCOLS) * 4 + 1] = hi;
227        img[(y * TWIDTH * TCOLS) * 4 + 2] = hi;
228        img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4] = lo;
229        img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 1] = lo;
230        img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 2] = lo;
231    }
232
233    for(x = 0; x < TWIDTH; x++)
234    {
235        img[x * 4] = hi;
236        img[x * 4 + 1] = hi;
237        img[x * 4 + 2] = hi;
238        img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4] = lo;
239        img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4 + 1] = lo;
240        img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4 + 2] = lo;
241    }
242
243    img[0] = (mid + hi) / 2;
244    img[1] = (mid + hi) / 2;
245    img[2] = (mid + hi) / 2;
246
247    img[(TWIDTH - 1) * 4 + 0] = mid;
248    img[(TWIDTH - 1) * 4 + 1] = mid;
249    img[(TWIDTH - 1) * 4 + 2] = mid;
250
251    img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 0] = mid;
252    img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 1] = mid;
253    img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 2] = mid;
254
255    img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 0] = (mid + lo) / 2;
256    img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 1] = (mid + lo) / 2;
257    img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 2] = (mid + lo) / 2;
258}
259
Note: See TracBrowser for help on using the repository browser.