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