00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00026 #include "libavutil/pixdesc.h"
00027 #include "avfilter.h"
00028 #include "drawutils.h"
00029
00030 typedef struct {
00031 unsigned w, h;
00032 unsigned current;
00033 FFDrawContext draw;
00034 FFDrawColor blank;
00035 } TileContext;
00036
00037 #define REASONABLE_SIZE 1024
00038
00039 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
00040 {
00041 TileContext *tile = ctx->priv;
00042 int r;
00043 char dummy;
00044
00045 if (!args)
00046 args = "6x5";
00047 r = sscanf(args, "%ux%u%c", &tile->w, &tile->h, &dummy);
00048 if (r != 2 || !tile->w || !tile->h)
00049 return AVERROR(EINVAL);
00050 if (tile->w > REASONABLE_SIZE || tile->h > REASONABLE_SIZE) {
00051 av_log(ctx, AV_LOG_ERROR, "Tile size %ux%u is insane.\n",
00052 tile->w, tile->h);
00053 return AVERROR(EINVAL);
00054 }
00055 return 0;
00056 }
00057
00058 static int query_formats(AVFilterContext *ctx)
00059 {
00060 avfilter_set_common_pixel_formats(ctx, ff_draw_supported_pixel_formats(0));
00061 return 0;
00062 }
00063
00064 static int config_props(AVFilterLink *outlink)
00065 {
00066 AVFilterContext *ctx = outlink->src;
00067 TileContext *tile = ctx->priv;
00068 AVFilterLink *inlink = ctx->inputs[0];
00069
00070 if (inlink->w > INT_MAX / tile->w) {
00071 av_log(ctx, AV_LOG_ERROR, "Total width %ux%u is too much.\n",
00072 tile->w, inlink->w);
00073 return AVERROR(EINVAL);
00074 }
00075 if (inlink->h > INT_MAX / tile->h) {
00076 av_log(ctx, AV_LOG_ERROR, "Total height %ux%u is too much.\n",
00077 tile->h, inlink->h);
00078 return AVERROR(EINVAL);
00079 }
00080 outlink->w = tile->w * inlink->w;
00081 outlink->h = tile->h * inlink->h;
00082 outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
00083 ff_draw_init(&tile->draw, inlink->format, 0);
00084
00085 ff_draw_color(&tile->draw, &tile->blank, (uint8_t[]){ 0, 0, 0, -1 });
00086
00087 return 0;
00088 }
00089
00090
00091
00092
00093
00094 static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
00095 {
00096 AVFilterContext *ctx = inlink->dst;
00097 TileContext *tile = ctx->priv;
00098 AVFilterLink *outlink = ctx->outputs[0];
00099
00100 if (tile->current)
00101 return;
00102 outlink->out_buf = avfilter_get_video_buffer(outlink, AV_PERM_WRITE,
00103 outlink->w, outlink->h);
00104 avfilter_copy_buffer_ref_props(outlink->out_buf, picref);
00105 outlink->out_buf->video->w = outlink->w;
00106 outlink->out_buf->video->h = outlink->h;
00107 avfilter_start_frame(outlink, outlink->out_buf);
00108 }
00109
00110 static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
00111 {
00112 AVFilterContext *ctx = inlink->dst;
00113 TileContext *tile = ctx->priv;
00114 AVFilterLink *outlink = ctx->outputs[0];
00115 unsigned x0 = inlink->w * (tile->current % tile->w);
00116 unsigned y0 = inlink->h * (tile->current / tile->w);
00117
00118 ff_copy_rectangle2(&tile->draw,
00119 outlink->out_buf->data, outlink->out_buf->linesize,
00120 inlink ->cur_buf->data, inlink ->cur_buf->linesize,
00121 x0, y0 + y, 0, y, inlink->cur_buf->video->w, h);
00122
00123
00124 }
00125
00126 static void draw_blank_frame(AVFilterContext *ctx)
00127 {
00128 TileContext *tile = ctx->priv;
00129 AVFilterLink *inlink = ctx->inputs[0];
00130 AVFilterLink *outlink = ctx->outputs[0];
00131 unsigned x0 = inlink->w * (tile->current % tile->w);
00132 unsigned y0 = inlink->h * (tile->current / tile->w);
00133
00134 ff_fill_rectangle(&tile->draw, &tile->blank,
00135 outlink->out_buf->data, outlink->out_buf->linesize,
00136 x0, y0, inlink->w, inlink->h);
00137 tile->current++;
00138 }
00139 static void end_last_frame(AVFilterContext *ctx)
00140 {
00141 TileContext *tile = ctx->priv;
00142 AVFilterLink *outlink = ctx->outputs[0];
00143
00144 while (tile->current < tile->w * tile->h)
00145 draw_blank_frame(ctx);
00146 avfilter_draw_slice(outlink, 0, outlink->out_buf->video->h, 1);
00147 avfilter_end_frame(outlink);
00148 tile->current = 0;
00149 }
00150
00151 static void end_frame(AVFilterLink *inlink)
00152 {
00153 AVFilterContext *ctx = inlink->dst;
00154 TileContext *tile = ctx->priv;
00155
00156 avfilter_unref_buffer(inlink->cur_buf);
00157 if (++tile->current == tile->w * tile->h)
00158 end_last_frame(ctx);
00159 }
00160
00161 static int request_frame(AVFilterLink *outlink)
00162 {
00163 AVFilterContext *ctx = outlink->src;
00164 TileContext *tile = ctx->priv;
00165 AVFilterLink *inlink = ctx->inputs[0];
00166 int r;
00167
00168 while (1) {
00169 r = avfilter_request_frame(inlink);
00170 if (r < 0) {
00171 if (r == AVERROR_EOF && tile->current)
00172 end_last_frame(ctx);
00173 else
00174 return r;
00175 break;
00176 }
00177 if (!tile->current)
00178 break;
00179 }
00180 return 0;
00181 }
00182
00183
00184 AVFilter avfilter_vf_tile = {
00185 .name = "tile",
00186 .description = NULL_IF_CONFIG_SMALL("Tile several successive frames together."),
00187 .init = init,
00188 .query_formats = query_formats,
00189 .priv_size = sizeof(TileContext),
00190 .inputs = (const AVFilterPad[]) {
00191 { .name = "default",
00192 .type = AVMEDIA_TYPE_VIDEO,
00193 .start_frame = start_frame,
00194 .draw_slice = draw_slice,
00195 .end_frame = end_frame,
00196 .min_perms = AV_PERM_READ, },
00197 { .name = NULL }
00198 },
00199 .outputs = (const AVFilterPad[]) {
00200 { .name = "default",
00201 .type = AVMEDIA_TYPE_VIDEO,
00202 .config_props = config_props,
00203 .request_frame = request_frame },
00204 { .name = NULL }
00205 },
00206 };