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.
 
 
 
 
 
 

260 lines
7.3 KiB

  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. stream = i;
  64. if(stream == -1)
  65. return EXIT_FAILURE;
  66. ctx = fmt->streams[stream]->codec;
  67. codec = avcodec_find_decoder(ctx->codec_id);
  68. if(codec == NULL)
  69. return EXIT_FAILURE;
  70. if(avcodec_open(ctx, codec) < 0)
  71. return EXIT_FAILURE;
  72. frame = avcodec_alloc_frame();
  73. for(;;)
  74. {
  75. int finished, ret;
  76. ret = av_read_frame(fmt, &packet);
  77. if(idx == NTHUMBS || (idx > 0 && ret < 0))
  78. {
  79. char buf[1024];
  80. sprintf(buf, fmtstr, k++);
  81. printf("saving %i thumbs in %s\n", idx, buf);
  82. pipi_save(image, buf);
  83. memset(buffer, 0, TWIDTH * TCOLS * THEIGHT * TROWS * 4);
  84. idx = 0;
  85. }
  86. if(ret < 0)
  87. break;
  88. if(packet.stream_index != stream)
  89. {
  90. av_free_packet(&packet);
  91. continue;
  92. }
  93. avcodec_decode_video(ctx, frame, &finished, packet.data, packet.size);
  94. if(!finished)
  95. {
  96. av_free_packet(&packet);
  97. continue;
  98. }
  99. /* Only process every 20th image */
  100. if((++n % STEP) == STEP / 2)
  101. {
  102. uint8_t *start;
  103. int pitch = TWIDTH * TCOLS * 4;
  104. int good = 1;
  105. start = buffer + (idx % TCOLS) * TWIDTH * 4
  106. + (idx / TCOLS) * TWIDTH * TCOLS * 4 * THEIGHT;
  107. if(!sws)
  108. sws = sws_getContext(ctx->width, ctx->height, ctx->pix_fmt,
  109. TWIDTH, THEIGHT, PIX_FMT_RGB32,
  110. SWS_BICUBIC, NULL, NULL, NULL);
  111. sws_scale(sws, frame->data, frame->linesize, 0, ctx->height,
  112. &start, &pitch);
  113. decorate(start);
  114. if(idx > 0)
  115. {
  116. uint8_t *prev;
  117. if(idx % TCOLS)
  118. prev = start - TWIDTH * 4;
  119. else
  120. prev = start + (TCOLS - 1) * TWIDTH * 4
  121. - TWIDTH * TCOLS * 4 * THEIGHT;
  122. /* Now check whether the new image is really different
  123. * from the previous one (> 10% pixel changes) */
  124. if(similar(start, prev))
  125. good = 0;
  126. }
  127. if(good)
  128. {
  129. idx++;
  130. }
  131. }
  132. av_free_packet(&packet);
  133. }
  134. return EXIT_SUCCESS;
  135. }
  136. static int similar(uint8_t *img1, uint8_t *img2)
  137. {
  138. int x, y, t, a, b, changed = 0;
  139. for(y = 2; y < THEIGHT - 2; y++)
  140. for(x = 2; x < TWIDTH - 2; x++)
  141. {
  142. int offset = y * TWIDTH * TCOLS + x;
  143. int ok = 0;
  144. for(t = 0; t < 3; t++)
  145. {
  146. a = 2 * img1[offset * 4 + t];
  147. a += img1[(offset - TWIDTH * TCOLS - 1) * 4 + t];
  148. a += img1[(offset - TWIDTH * TCOLS) * 4 + t];
  149. a += img1[(offset - TWIDTH * TCOLS + 1) * 4 + t];
  150. a += img1[(offset - 1) * 4 + t];
  151. a += img1[(offset + 1) * 4 + t];
  152. a += img1[(offset + TWIDTH * TCOLS - 1) * 4 + t];
  153. a += img1[(offset + TWIDTH * TCOLS) * 4 + t];
  154. a += img1[(offset + TWIDTH * TCOLS + 1) * 4 + t];
  155. a /= 10;
  156. b = 2 * img2[offset * 4 + t];
  157. b += img2[(offset - TWIDTH * TCOLS - 1) * 4 + t];
  158. b += img2[(offset - TWIDTH * TCOLS) * 4 + t];
  159. b += img2[(offset - TWIDTH * TCOLS + 1) * 4 + t];
  160. b += img2[(offset - 1) * 4 + t];
  161. b += img2[(offset + 1) * 4 + t];
  162. b += img2[(offset + TWIDTH * TCOLS - 1) * 4 + t];
  163. b += img2[(offset + TWIDTH * TCOLS) * 4 + t];
  164. b += img2[(offset + TWIDTH * TCOLS + 1) * 4 + t];
  165. b /= 10;
  166. if(a < b - 8 || a > b + 8)
  167. {
  168. ok = 1;
  169. break;
  170. }
  171. }
  172. changed += ok;
  173. }
  174. return changed < (TWIDTH * THEIGHT * 10 / 100);
  175. }
  176. static void decorate(uint8_t *img)
  177. {
  178. static int const hi = 200;
  179. static int const lo = 50;
  180. static int const mid = 127;
  181. int x, y;
  182. for(y = 0; y < THEIGHT; y++)
  183. {
  184. img[(y * TWIDTH * TCOLS) * 4] = hi;
  185. img[(y * TWIDTH * TCOLS) * 4 + 1] = hi;
  186. img[(y * TWIDTH * TCOLS) * 4 + 2] = hi;
  187. img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4] = lo;
  188. img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 1] = lo;
  189. img[(y * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 2] = lo;
  190. }
  191. for(x = 0; x < TWIDTH; x++)
  192. {
  193. img[x * 4] = hi;
  194. img[x * 4 + 1] = hi;
  195. img[x * 4 + 2] = hi;
  196. img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4] = lo;
  197. img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4 + 1] = lo;
  198. img[((THEIGHT - 1) * TWIDTH * TCOLS + x) * 4 + 2] = lo;
  199. }
  200. img[0] = (mid + hi) / 2;
  201. img[1] = (mid + hi) / 2;
  202. img[2] = (mid + hi) / 2;
  203. img[(TWIDTH - 1) * 4 + 0] = mid;
  204. img[(TWIDTH - 1) * 4 + 1] = mid;
  205. img[(TWIDTH - 1) * 4 + 2] = mid;
  206. img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 0] = mid;
  207. img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 1] = mid;
  208. img[((THEIGHT - 1) * TWIDTH * TCOLS) * 4 + 2] = mid;
  209. img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 0] = (mid + lo) / 2;
  210. img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 1] = (mid + lo) / 2;
  211. img[((THEIGHT - 1) * TWIDTH * TCOLS + TWIDTH - 1) * 4 + 2] = (mid + lo) / 2;
  212. }