vf_tile.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * Copyright (c) 2012 Nicolas George
  3. *
  4. * This file is part of FFmpeg.
  5. *
  6. * FFmpeg is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * FFmpeg is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with FFmpeg; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. */
  20. /**
  21. * @file
  22. * tile video filter
  23. */
  24. #include "libavutil/pixdesc.h"
  25. #include "avfilter.h"
  26. #include "drawutils.h"
  27. #include "formats.h"
  28. #include "video.h"
  29. #include "internal.h"
  30. typedef struct {
  31. unsigned w, h;
  32. unsigned current;
  33. FFDrawContext draw;
  34. FFDrawColor blank;
  35. } TileContext;
  36. #define REASONABLE_SIZE 1024
  37. static av_cold int init(AVFilterContext *ctx, const char *args)
  38. {
  39. TileContext *tile = ctx->priv;
  40. int r;
  41. char dummy;
  42. if (!args)
  43. args = "6x5";
  44. r = sscanf(args, "%ux%u%c", &tile->w, &tile->h, &dummy);
  45. if (r != 2 || !tile->w || !tile->h)
  46. return AVERROR(EINVAL);
  47. if (tile->w > REASONABLE_SIZE || tile->h > REASONABLE_SIZE) {
  48. av_log(ctx, AV_LOG_ERROR, "Tile size %ux%u is insane.\n",
  49. tile->w, tile->h);
  50. return AVERROR(EINVAL);
  51. }
  52. return 0;
  53. }
  54. static int query_formats(AVFilterContext *ctx)
  55. {
  56. ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
  57. return 0;
  58. }
  59. static int config_props(AVFilterLink *outlink)
  60. {
  61. AVFilterContext *ctx = outlink->src;
  62. TileContext *tile = ctx->priv;
  63. AVFilterLink *inlink = ctx->inputs[0];
  64. if (inlink->w > INT_MAX / tile->w) {
  65. av_log(ctx, AV_LOG_ERROR, "Total width %ux%u is too much.\n",
  66. tile->w, inlink->w);
  67. return AVERROR(EINVAL);
  68. }
  69. if (inlink->h > INT_MAX / tile->h) {
  70. av_log(ctx, AV_LOG_ERROR, "Total height %ux%u is too much.\n",
  71. tile->h, inlink->h);
  72. return AVERROR(EINVAL);
  73. }
  74. outlink->w = tile->w * inlink->w;
  75. outlink->h = tile->h * inlink->h;
  76. outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
  77. outlink->frame_rate = av_mul_q(inlink->frame_rate,
  78. (AVRational){ 1, tile->w * tile->h });
  79. ff_draw_init(&tile->draw, inlink->format, 0);
  80. /* TODO make the color an option, or find an unified way of choosing it */
  81. ff_draw_color(&tile->draw, &tile->blank, (uint8_t[]){ 0, 0, 0, -1 });
  82. return 0;
  83. }
  84. /* Note: direct rendering is not possible since there is no guarantee that
  85. * buffers are fed to start_frame in the order they were obtained from
  86. * get_buffer (think B-frames). */
  87. static int start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
  88. {
  89. AVFilterContext *ctx = inlink->dst;
  90. TileContext *tile = ctx->priv;
  91. AVFilterLink *outlink = ctx->outputs[0];
  92. if (tile->current)
  93. return 0;
  94. outlink->out_buf = ff_get_video_buffer(outlink, AV_PERM_WRITE,
  95. outlink->w, outlink->h);
  96. avfilter_copy_buffer_ref_props(outlink->out_buf, picref);
  97. outlink->out_buf->video->w = outlink->w;
  98. outlink->out_buf->video->h = outlink->h;
  99. return 0;
  100. }
  101. static int draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
  102. {
  103. AVFilterContext *ctx = inlink->dst;
  104. TileContext *tile = ctx->priv;
  105. AVFilterLink *outlink = ctx->outputs[0];
  106. unsigned x0 = inlink->w * (tile->current % tile->w);
  107. unsigned y0 = inlink->h * (tile->current / tile->w);
  108. ff_copy_rectangle2(&tile->draw,
  109. outlink->out_buf->data, outlink->out_buf->linesize,
  110. inlink ->cur_buf->data, inlink ->cur_buf->linesize,
  111. x0, y0 + y, 0, y, inlink->cur_buf->video->w, h);
  112. /* TODO if tile->w == 1 && slice_dir is always 1, we could draw_slice
  113. * immediately. */
  114. return 0;
  115. }
  116. static void draw_blank_frame(AVFilterContext *ctx, AVFilterBufferRef *out_buf)
  117. {
  118. TileContext *tile = ctx->priv;
  119. AVFilterLink *inlink = ctx->inputs[0];
  120. unsigned x0 = inlink->w * (tile->current % tile->w);
  121. unsigned y0 = inlink->h * (tile->current / tile->w);
  122. ff_fill_rectangle(&tile->draw, &tile->blank,
  123. out_buf->data, out_buf->linesize,
  124. x0, y0, inlink->w, inlink->h);
  125. tile->current++;
  126. }
  127. static void end_last_frame(AVFilterContext *ctx)
  128. {
  129. TileContext *tile = ctx->priv;
  130. AVFilterLink *outlink = ctx->outputs[0];
  131. AVFilterBufferRef *out_buf = outlink->out_buf;
  132. outlink->out_buf = NULL;
  133. ff_start_frame(outlink, out_buf);
  134. while (tile->current < tile->w * tile->h)
  135. draw_blank_frame(ctx, out_buf);
  136. ff_draw_slice(outlink, 0, out_buf->video->h, 1);
  137. ff_end_frame(outlink);
  138. tile->current = 0;
  139. }
  140. static int end_frame(AVFilterLink *inlink)
  141. {
  142. AVFilterContext *ctx = inlink->dst;
  143. TileContext *tile = ctx->priv;
  144. avfilter_unref_bufferp(&inlink->cur_buf);
  145. if (++tile->current == tile->w * tile->h)
  146. end_last_frame(ctx);
  147. return 0;
  148. }
  149. static int request_frame(AVFilterLink *outlink)
  150. {
  151. AVFilterContext *ctx = outlink->src;
  152. TileContext *tile = ctx->priv;
  153. AVFilterLink *inlink = ctx->inputs[0];
  154. int r;
  155. while (1) {
  156. r = ff_request_frame(inlink);
  157. if (r < 0) {
  158. if (r == AVERROR_EOF && tile->current)
  159. end_last_frame(ctx);
  160. else
  161. return r;
  162. break;
  163. }
  164. if (!tile->current) /* done */
  165. break;
  166. }
  167. return 0;
  168. }
  169. AVFilter avfilter_vf_tile = {
  170. .name = "tile",
  171. .description = NULL_IF_CONFIG_SMALL("Tile several successive frames together."),
  172. .init = init,
  173. .query_formats = query_formats,
  174. .priv_size = sizeof(TileContext),
  175. .inputs = (const AVFilterPad[]) {
  176. { .name = "default",
  177. .type = AVMEDIA_TYPE_VIDEO,
  178. .start_frame = start_frame,
  179. .draw_slice = draw_slice,
  180. .end_frame = end_frame,
  181. .min_perms = AV_PERM_READ, },
  182. { .name = NULL }
  183. },
  184. .outputs = (const AVFilterPad[]) {
  185. { .name = "default",
  186. .type = AVMEDIA_TYPE_VIDEO,
  187. .config_props = config_props,
  188. .request_frame = request_frame },
  189. { .name = NULL }
  190. },
  191. };