From 70e7f92416d91e47f1a938f129f4697b5a795fa1 Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 12 Jan 2009 23:51:52 +0000 Subject: [PATCH] Added a movie thumbnailer (storyboard generator) using libavcodec. git-svn-id: file:///srv/caca.zoy.org/var/lib/svn/libpipi/trunk@3345 92316355-f0b4-4df1-b90c-862c8a59935f --- examples/Makefile.am | 8 +- examples/storyboard.c | 184 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 examples/storyboard.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 70c8b49..d35145e 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/pipi -I../pipi noinst_PROGRAMS = edd img2rubik sharpen floodfill line bezier histogram \ - colorstring + colorstring storyboard edd_SOURCES = edd.c edd_LDADD = ../pipi/libpipi.la @@ -28,3 +28,9 @@ histogram_LDADD = ../pipi/libpipi.la colorstring_SOURCES = colorstring.c colorstring_LDADD = ../pipi/libpipi.la + +storyboard_SOURCES = storyboard.c +storyboard_LDADD = ../pipi/libpipi.la +storyboard_CFLAGS = `pkg-config --cflags libavformat libavcodec libswscale` +storyboard_LDFLAGS = `pkg-config --libs libavformat libavcodec libswscale` + diff --git a/examples/storyboard.c b/examples/storyboard.c new file mode 100644 index 0000000..b8ee9c3 --- /dev/null +++ b/examples/storyboard.c @@ -0,0 +1,184 @@ +/* + * storyboard generate a storyboard from a movie + * Copyright (c) 2009 Sam Hocevar + * 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 +#include +#include + +#include +#include +#include + +#include + +#define STEP 20 + +#define TWIDTH 90 +#define THEIGHT 60 +#define TCOLS 10 +#define TROWS 200 +#define NTHUMBS (TCOLS*TROWS) + +int main(int argc, char *argv[]) +{ + char fmtstr[1024]; + AVPacket packet; + AVFormatContext *fmt; + AVCodecContext *ctx; + AVCodec *codec; + AVFrame *frame; + struct SwsContext *sws; + 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; + + strcpy(fmtstr, argv[1]); + parser = strrchr(fmtstr, '.'); + if(parser) + strcpy(parser, "%03i.jpeg"); + else + strcat(fmtstr, "%03i.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(); + + sws = sws_getContext(ctx->width, ctx->height, ctx->pix_fmt, + TWIDTH, THEIGHT, PIX_FMT_RGB32, + SWS_BICUBIC, NULL, NULL, NULL); + + for(;;) + { + int finished, ret; + + ret = av_read_frame(fmt, &packet); + + if(idx == NTHUMBS || (idx > 0 && ret < 0)) + { + /* Only process every 20th image */ + 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; + } + + n++; + + if((n % STEP) == STEP / 2) + { + int pitch = TWIDTH * TCOLS * 4; + uint8_t *start; + + start = buffer + (idx % TCOLS) * TWIDTH * 4 + + (idx / TCOLS) * TWIDTH * TCOLS * 4 * THEIGHT; + + sws_scale(sws, frame->data, frame->linesize, 0, ctx->height, + &start, &pitch); + + /* Now check whether the new image is really different + * from the previous one (> 8% pixel changes) */ + if(idx == 0) + { + idx++; + } + else + { + uint8_t *prev; + int x, y, t, a, b, changed = 0; + + if(idx % TCOLS) + prev = start - TWIDTH * 4; + else + prev = start + (TCOLS - 1) * TWIDTH * 4 + - TWIDTH * TCOLS * 4 * THEIGHT; + + for(y = 0; y < THEIGHT; y++) + for(x = 0; x < TWIDTH; x++) + { + int ok = 0; + + for(t = 0; t < 3; t++) + { + int offset = y * TWIDTH * TCOLS + x; + a = start[offset * 4 + t]; + b = prev[offset * 4 + t]; + + if(a < b - 5 || a > b + 5) + { + ok = 1; + break; + } + } + + changed += ok; + } + + if(changed > TWIDTH * THEIGHT * 8 / 100) + idx++; + } + } + + av_free_packet(&packet); + } + + return EXIT_SUCCESS; +} +