You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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