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.

339 lines
11KB

  1. /*
  2. * Copyright (c) 2007 Nicolas George <nicolas.george@normalesup.org>
  3. * Copyright (c) 2011 Stefano Sabatini
  4. *
  5. * This file is part of FFmpeg.
  6. *
  7. * FFmpeg is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * FFmpeg is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with FFmpeg; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. /**
  22. * @file
  23. * Based on the test pattern generator demuxer by Nicolas George:
  24. * http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2007-October/037845.html
  25. */
  26. #include <float.h>
  27. #include "libavutil/opt.h"
  28. #include "libavutil/parseutils.h"
  29. #include "avfilter.h"
  30. typedef struct {
  31. const AVClass *class;
  32. int h, w;
  33. unsigned int nb_frame;
  34. AVRational time_base;
  35. int64_t pts, max_pts;
  36. char *size; ///< video frame size
  37. char *rate; ///< video frame rate
  38. char *duration; ///< total duration of the generated video
  39. } TestSourceContext;
  40. #define OFFSET(x) offsetof(TestSourceContext, x)
  41. static const AVOption testsrc_options[]= {
  42. { "size", "set video size", OFFSET(size), FF_OPT_TYPE_STRING, {.str = "320x240"}, 0, 0 },
  43. { "s", "set video size", OFFSET(size), FF_OPT_TYPE_STRING, {.str = "320x240"}, 0, 0 },
  44. { "rate", "set video rate", OFFSET(rate), FF_OPT_TYPE_STRING, {.str = "25"}, 0, 0 },
  45. { "r", "set video rate", OFFSET(rate), FF_OPT_TYPE_STRING, {.str = "25"}, 0, 0 },
  46. { "duration", "set video duration", OFFSET(duration), FF_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
  47. { NULL },
  48. };
  49. static const char *testsrc_get_name(void *ctx)
  50. {
  51. return "testsrc";
  52. }
  53. static const AVClass testsrc_class = {
  54. "TestSourceContext",
  55. testsrc_get_name,
  56. testsrc_options
  57. };
  58. static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
  59. {
  60. TestSourceContext *test = ctx->priv;
  61. AVRational frame_rate_q;
  62. int64_t duration = -1;
  63. int ret = 0;
  64. test->class = &testsrc_class;
  65. av_opt_set_defaults2(test, 0, 0);
  66. if ((ret = (av_set_options_string(test, args, "=", ":"))) < 0) {
  67. av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
  68. return ret;
  69. }
  70. if ((ret = av_parse_video_size(&test->w, &test->h, test->size)) < 0) {
  71. av_log(ctx, AV_LOG_ERROR, "Invalid frame size: '%s'\n", test->size);
  72. return ret;
  73. }
  74. if ((ret = av_parse_video_rate(&frame_rate_q, test->rate)) < 0 ||
  75. frame_rate_q.den <= 0 || frame_rate_q.num <= 0) {
  76. av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: '%s'\n", test->rate);
  77. return ret;
  78. }
  79. if ((test->duration) && (ret = av_parse_time(&duration, test->duration, 1)) < 0) {
  80. av_log(ctx, AV_LOG_ERROR, "Invalid duration: '%s'\n", test->duration);
  81. return ret;
  82. }
  83. test->time_base.num = frame_rate_q.den;
  84. test->time_base.den = frame_rate_q.num;
  85. test->max_pts = duration >= 0 ?
  86. av_rescale_q(duration, AV_TIME_BASE_Q, test->time_base) : -1;
  87. test->nb_frame = 0;
  88. test->pts = 0;
  89. av_log(ctx, AV_LOG_INFO, "size:%dx%d rate:%d/%d duration:%f\n",
  90. test->w, test->h, frame_rate_q.num, frame_rate_q.den,
  91. duration < 0 ? -1 : test->max_pts * av_q2d(test->time_base));
  92. return 0;
  93. }
  94. static int config_props(AVFilterLink *outlink)
  95. {
  96. TestSourceContext *test = outlink->src->priv;
  97. outlink->w = test->w;
  98. outlink->h = test->h;
  99. outlink->time_base = test->time_base;
  100. return 0;
  101. }
  102. /**
  103. * Fill a rectangle with value val.
  104. *
  105. * @param val the RGB value to set
  106. * @param dst pointer to the destination buffer to fill
  107. * @param dst_linesize linesize of destination
  108. * @param segment_width width of the segment
  109. * @param x horizontal coordinate where to draw the rectangle in the destination buffer
  110. * @param y horizontal coordinate where to draw the rectangle in the destination buffer
  111. * @param w width of the rectangle to draw, expressed as a number of segment_width units
  112. * @param h height of the rectangle to draw, expressed as a number of segment_width units
  113. */
  114. static void draw_rectangle(unsigned val, uint8_t *dst, int dst_linesize, unsigned segment_width,
  115. unsigned x, unsigned y, unsigned w, unsigned h)
  116. {
  117. int i;
  118. int step = 3;
  119. dst += segment_width * (step * x + y * dst_linesize);
  120. w *= segment_width * step;
  121. h *= segment_width;
  122. for (i = 0; i < h; i++) {
  123. memset(dst, val, w);
  124. dst += dst_linesize;
  125. }
  126. }
  127. static void draw_digit(int digit, uint8_t *dst, unsigned dst_linesize,
  128. unsigned segment_width)
  129. {
  130. #define TOP_HBAR 1
  131. #define MID_HBAR 2
  132. #define BOT_HBAR 4
  133. #define LEFT_TOP_VBAR 8
  134. #define LEFT_BOT_VBAR 16
  135. #define RIGHT_TOP_VBAR 32
  136. #define RIGHT_BOT_VBAR 64
  137. struct {
  138. int x, y, w, h;
  139. } segments[] = {
  140. { 1, 0, 5, 1 }, /* TOP_HBAR */
  141. { 1, 6, 5, 1 }, /* MID_HBAR */
  142. { 1, 12, 5, 1 }, /* BOT_HBAR */
  143. { 0, 1, 1, 5 }, /* LEFT_TOP_VBAR */
  144. { 0, 7, 1, 5 }, /* LEFT_BOT_VBAR */
  145. { 6, 1, 1, 5 }, /* RIGHT_TOP_VBAR */
  146. { 6, 7, 1, 5 } /* RIGHT_BOT_VBAR */
  147. };
  148. static const unsigned char masks[10] = {
  149. /* 0 */ TOP_HBAR |BOT_HBAR|LEFT_TOP_VBAR|LEFT_BOT_VBAR|RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
  150. /* 1 */ RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
  151. /* 2 */ TOP_HBAR|MID_HBAR|BOT_HBAR|LEFT_BOT_VBAR |RIGHT_TOP_VBAR,
  152. /* 3 */ TOP_HBAR|MID_HBAR|BOT_HBAR |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
  153. /* 4 */ MID_HBAR |LEFT_TOP_VBAR |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
  154. /* 5 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR |RIGHT_BOT_VBAR,
  155. /* 6 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR|LEFT_BOT_VBAR |RIGHT_BOT_VBAR,
  156. /* 7 */ TOP_HBAR |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
  157. /* 8 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR|LEFT_BOT_VBAR|RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
  158. /* 9 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
  159. };
  160. unsigned mask = masks[digit];
  161. int i;
  162. draw_rectangle(0, dst, dst_linesize, segment_width, 0, 0, 8, 13);
  163. for (i = 0; i < FF_ARRAY_ELEMS(segments); i++)
  164. if (mask & (1<<i))
  165. draw_rectangle(255, dst, dst_linesize, segment_width,
  166. segments[i].x, segments[i].y, segments[i].w, segments[i].h);
  167. }
  168. #define GRADIENT_SIZE (6 * 256)
  169. static void fill_picture(TestSourceContext *test, AVFilterBufferRef *picref)
  170. {
  171. uint8_t *p, *p0;
  172. int x, y;
  173. int color, color_rest;
  174. int icolor;
  175. int radius;
  176. int quad0, quad;
  177. int dquad_x, dquad_y;
  178. int grad, dgrad, rgrad, drgrad;
  179. int seg_size;
  180. int second;
  181. int i;
  182. uint8_t *data = picref->data[0];
  183. int width = picref->video->w;
  184. int height = picref->video->h;
  185. /* draw colored bars and circle */
  186. radius = (width + height) / 4;
  187. quad0 = width * width / 4 + height * height / 4 - radius * radius;
  188. dquad_y = 1 - height;
  189. p0 = data;
  190. for (y = 0; y < height; y++) {
  191. p = p0;
  192. color = 0;
  193. color_rest = 0;
  194. quad = quad0;
  195. dquad_x = 1 - width;
  196. for (x = 0; x < width; x++) {
  197. icolor = color;
  198. if (quad < 0)
  199. icolor ^= 7;
  200. quad += dquad_x;
  201. dquad_x += 2;
  202. *(p++) = icolor & 1 ? 255 : 0;
  203. *(p++) = icolor & 2 ? 255 : 0;
  204. *(p++) = icolor & 4 ? 255 : 0;
  205. color_rest += 8;
  206. if (color_rest >= width) {
  207. color_rest -= width;
  208. color++;
  209. }
  210. }
  211. quad0 += dquad_y;
  212. dquad_y += 2;
  213. p0 += picref->linesize[0];
  214. }
  215. /* draw sliding color line */
  216. p = data + picref->linesize[0] * height * 3/4;
  217. grad = (256 * test->nb_frame * test->time_base.num / test->time_base.den) %
  218. GRADIENT_SIZE;
  219. rgrad = 0;
  220. dgrad = GRADIENT_SIZE / width;
  221. drgrad = GRADIENT_SIZE % width;
  222. for (x = 0; x < width; x++) {
  223. *(p++) =
  224. grad < 256 || grad >= 5 * 256 ? 255 :
  225. grad >= 2 * 256 && grad < 4 * 256 ? 0 :
  226. grad < 2 * 256 ? 2 * 256 - 1 - grad : grad - 4 * 256;
  227. *(p++) =
  228. grad >= 4 * 256 ? 0 :
  229. grad >= 1 * 256 && grad < 3 * 256 ? 255 :
  230. grad < 1 * 256 ? grad : 4 * 256 - 1 - grad;
  231. *(p++) =
  232. grad < 2 * 256 ? 0 :
  233. grad >= 3 * 256 && grad < 5 * 256 ? 255 :
  234. grad < 3 * 256 ? grad - 2 * 256 : 6 * 256 - 1 - grad;
  235. grad += dgrad;
  236. rgrad += drgrad;
  237. if (rgrad >= GRADIENT_SIZE) {
  238. grad++;
  239. rgrad -= GRADIENT_SIZE;
  240. }
  241. if (grad >= GRADIENT_SIZE)
  242. grad -= GRADIENT_SIZE;
  243. }
  244. for (y = height / 8; y > 0; y--) {
  245. memcpy(p, p - picref->linesize[0], 3 * width);
  246. p += picref->linesize[0];
  247. }
  248. /* draw digits */
  249. seg_size = width / 80;
  250. if (seg_size >= 1 && height >= 13 * seg_size) {
  251. second = test->nb_frame * test->time_base.num / test->time_base.den;
  252. x = width - (width - seg_size * 64) / 2;
  253. y = (height - seg_size * 13) / 2;
  254. p = data + (x*3 + y * picref->linesize[0]);
  255. for (i = 0; i < 8; i++) {
  256. p -= 3 * 8 * seg_size;
  257. draw_digit(second % 10, p, picref->linesize[0], seg_size);
  258. second /= 10;
  259. if (second == 0)
  260. break;
  261. }
  262. }
  263. }
  264. static int request_frame(AVFilterLink *outlink)
  265. {
  266. TestSourceContext *test = outlink->src->priv;
  267. AVFilterBufferRef *picref;
  268. if (test->max_pts >= 0 && test->pts > test->max_pts)
  269. return AVERROR_EOF;
  270. picref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE,
  271. test->w, test->h);
  272. picref->pts = test->pts++;
  273. test->nb_frame++;
  274. fill_picture(test, picref);
  275. avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
  276. avfilter_draw_slice(outlink, 0, picref->video->h, 1);
  277. avfilter_end_frame(outlink);
  278. avfilter_unref_buffer(picref);
  279. return 0;
  280. }
  281. static int query_formats(AVFilterContext *ctx)
  282. {
  283. static const enum PixelFormat pix_fmts[] = {
  284. PIX_FMT_RGB24, PIX_FMT_NONE
  285. };
  286. avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
  287. return 0;
  288. }
  289. AVFilter avfilter_vsrc_testsrc = {
  290. .name = "testsrc",
  291. .description = NULL_IF_CONFIG_SMALL("Generate test pattern."),
  292. .priv_size = sizeof(TestSourceContext),
  293. .init = init,
  294. .query_formats = query_formats,
  295. .inputs = (AVFilterPad[]) {{ .name = NULL}},
  296. .outputs = (AVFilterPad[]) {{ .name = "default",
  297. .type = AVMEDIA_TYPE_VIDEO,
  298. .request_frame = request_frame,
  299. .config_props = config_props, },
  300. { .name = NULL }},
  301. };