- /*
- * storyboard generate a storyboard from a movie
- * Copyright (c) 2009 Sam Hocevar <sam@zoy.org>
- * All Rights Reserved
- *
- * $Id$
- *
- * This program is free software. It comes without any warranty, to
- * the extent permitted by applicable law. You can redistribute it
- * and/or modify it under the terms of the Do What The Fuck You Want
- * To Public License, Version 2, as published by Sam Hocevar. See
- * http://sam.zoy.org/wtfpl/COPYING for more details.
- */
-
- #include "config.h"
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- #include <libavcodec/avcodec.h>
- #include <libavformat/avformat.h>
- #include <libswscale/swscale.h>
-
- #include <pipi.h>
-
- #define STEP 12
-
- #define TWIDTH 90
- #define THEIGHT 60
- #define TCOLS 10
- #define TROWS 50
- #define NTHUMBS (TCOLS*TROWS)
-
- static int similar(uint8_t *img1, uint8_t *img2);
- static void decorate(uint8_t *img);
-
- int main(int argc, char *argv[])
- {
- char fmtstr[1024];
- AVPacket packet;
- AVFormatContext *fmt;
- AVCodecContext *ctx;
- AVCodec *codec;
- AVFrame *frame;
- struct SwsContext *sws = NULL;
- pipi_image_t *image;
- pipi_pixels_t *p;
- uint8_t *buffer;
- char *parser;
- int stream, i, n = 0, k = 0, idx = 0;
-
- if(argc < 2)
- return EXIT_FAILURE;
-
- parser = strrchr(argv[1], '/');
- strcpy(fmtstr, parser ? parser + 1 : argv[1]);
- parser = strrchr(fmtstr, '.');
- if(parser)
- *parser = '\0';
- strcat(fmtstr, ".t%02i.jpeg");
-
- image = pipi_new(TWIDTH * TCOLS, THEIGHT * TROWS);
- p = pipi_get_pixels(image, PIPI_PIXELS_RGBA_U8);
- buffer = (uint8_t *)p->pixels;
-
- av_register_all();
- if(av_open_input_file(&fmt, argv[1], NULL, 0, NULL) != 0)
- return EXIT_FAILURE;
- if(av_find_stream_info(fmt) < 0 )
- return EXIT_FAILURE;
-
- stream = -1;
- for(i = 0; stream == -1 && i < (int)fmt->nb_streams; i++)
- if(fmt->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
- stream = i;
- if(stream == -1)
- return EXIT_FAILURE;
- ctx = fmt->streams[stream]->codec;
-
- codec = avcodec_find_decoder(ctx->codec_id);
- if(codec == NULL)
- return EXIT_FAILURE;
- if(avcodec_open(ctx, codec) < 0)
- return EXIT_FAILURE;
-
- frame = avcodec_alloc_frame();
-
- for(;;)
- {
- int finished, ret;
-
- ret = av_read_frame(fmt, &packet);
-
- if(idx == NTHUMBS || (idx > 0 && ret < 0))
- {
- char buf[1024];
- sprintf(buf, fmtstr, k++);
- printf("saving %i thumbs in %s\n", idx, buf);
- pipi_save(image, buf);
- memset(buffer, 0, TWIDTH * TCOLS * THEIGHT * TROWS * 4);
- idx = 0;
- }
-
- if(ret < 0)
- break;
-
- if(packet.stream_index != stream)
- {
- av_free_packet(&packet);
- continue;
- }
-
- avcodec_decode_video(ctx, frame, &finished, packet.data, packet.size);
- if(!finished)
- {
- av_free_packet(&packet);
- continue;
- }
-
- /* Only process every 20th image */
- if((++n % STEP) == STEP / 2)
- {
- uint8_t *start;
- int pitch = TWIDTH * TCOLS * 4;
- int good = 1;
-
- start = buffer + (idx % TCOLS) * TWIDTH * 4
- + (idx / TCOLS) * TWIDTH * TCOLS * 4 * THEIGHT;
-
- if(!sws)
- sws = sws_getContext(ctx->width, ctx->height, ctx->pix_fmt,
- TWIDTH, THEIGHT, PIX_FMT_RGB32,
- SWS_BICUBIC, NULL, NULL, NULL);
-
- sws_scale(sws, frame->data, frame->linesize, 0, ctx->height,
- &start, &pitch);
-
- decorate(start);
-
- if(idx > 0)
- {
- uint8_t *prev;
-
- if(idx % TCOLS)
- prev = start - TWIDTH * 4;
- else
- prev = start + (TCOLS - 1) * TWIDTH * 4
- - TWIDTH * TCOLS * 4 * THEIGHT;
-
- /* Now check whether the new image is really different
- * from the previous one (> 10% pixel changes) */
- if(similar(start, prev))
- good = 0;
- }
-
- if(good)
- {
- idx++;
- }
- }
-
- av_free_packet(&packet);
- }
-
- return EXIT_SUCCESS;
- }
-
- static int similar(uint8_t *img1, uint8_t *img2)
- {
- int x, y, t, a, b, changed = 0;
-
- for(y = 2; y < THEIGHT - 2; y++)
- for(x = 2; x < TWIDTH - 2; x++)
- {
- int offset = y * TWIDTH * TCOLS + x;
- int ok = 0;
-
- for(t = 0; t < 3; t++)
- {
- a = 2 * img1[offset * 4 + t];
- a += img1[(offset - TWIDTH * TCOLS - 1) * 4 + t];
- a += img1[(offset - TWIDTH * TCOLS) * 4 + t];
- a += img1[(offset - TWIDTH * TCOLS + 1) * 4 + t];
- a += img1[(offset - 1) * 4 + t];
- a += img1[(offset + 1) * 4 + t];
- a += img1[(offset + TWIDTH * TCOLS - 1) * 4 + t];
- a += img1[(offset + TWIDTH * TCOLS) * 4 + t];
- a += img1[(offset + TWIDTH * TCOLS + 1) * 4 + t];
- a /= 10;
-
- b = 2 * img2[offset * 4 + t];
- b += img2[(offset - TWIDTH * TCOLS - 1) * 4 + t];
- b += img2[(offset - TWIDTH * TCOLS) * 4 + t];
- b += img2[(offset - TWIDTH * TCOLS + 1) * 4 + t];
- b += img2[(offset - 1) * 4 + t];
- b += img2[(offset + 1) * 4 + t];
- b += img2[(offset + TWIDTH * TCOLS - 1) * 4 + t];
- b += img2[(offset + TWIDTH * TCOLS) * 4 + t];
- b += img2[(offset + TWIDTH * TCOLS + 1) * 4 + t];
- b /= 10;
-
- if(a < b - 8 || a > b + 8)
- {
- ok = 1;
- break;
- }
- }
-
- changed += ok;
- }
-
- return changed < (TWIDTH * THEIGHT * 10 / 100);
- }
-
- static void decorate(uint8_t *img)
- {
- static int const hi = 200;
- static int const lo = 50;
- static int const mid = 127;
- int x, y;
-
- for(y = 0; y < THEIGHT; y++)
- {
- img[(y * TWIDTH * TCOLS) * 4] = hi;
- img[(y * TWIDTH * TCOLS) * 4 + 1] = hi;
- img[(y * TWIDTH * TCOLS) * 4 + 2] = hi;
- img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4] = lo;
- img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 1] = lo;
- img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 2] = lo;
- }
-
- for(x = 0; x < TWIDTH; x++)
- {
- img[x * 4] = hi;
- img[x * 4 + 1] = hi;
- img[x * 4 + 2] = hi;
- img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4] = lo;
- img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4 + 1] = lo;
- img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4 + 2] = lo;
- }
-
- img[0] = (mid + hi) / 2;
- img[1] = (mid + hi) / 2;
- img[2] = (mid + hi) / 2;
-
- img[(TWIDTH - 1) * 4 + 0] = mid;
- img[(TWIDTH - 1) * 4 + 1] = mid;
- img[(TWIDTH - 1) * 4 + 2] = mid;
-
- img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 0] = mid;
- img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 1] = mid;
- img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 2] = mid;
-
- img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 0] = (mid + lo) / 2;
- img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 1] = (mid + lo) / 2;
- img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 2] = (mid + lo) / 2;
- }
-
|