[PATCH 1/2] Create life source.
Stefano Sabatini
stefano.sabatini-lala
Tue Jun 15 19:47:50 CEST 2010
---
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/vsrc_life.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 271 insertions(+), 0 deletions(-)
create mode 100644 libavfilter/vsrc_life.c
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index d2a40c4..b70c630 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -33,6 +33,7 @@ OBJS-$(CONFIG_VFLIP_FILTER) += vf_vflip.o
OBJS-$(CONFIG_BUFFER_FILTER) += vsrc_buffer.o
OBJS-$(CONFIG_COLOR_FILTER) += vf_pad.o
+OBJS-$(CONFIG_LIFE_FILTER) += vsrc_life.o
OBJS-$(CONFIG_NULLSRC_FILTER) += vsrc_nullsrc.o
OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 27442f6..6cb0628 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -53,6 +53,7 @@ void avfilter_register_all(void)
REGISTER_FILTER (BUFFER, buffer, vsrc);
REGISTER_FILTER (COLOR, color, vsrc);
+ REGISTER_FILTER (LIFE, life, vsrc);
REGISTER_FILTER (NULLSRC, nullsrc, vsrc);
REGISTER_FILTER (NULLSINK, nullsink, vsink);
diff --git a/libavfilter/vsrc_life.c b/libavfilter/vsrc_life.c
new file mode 100644
index 0000000..db1ebb1
--- /dev/null
+++ b/libavfilter/vsrc_life.c
@@ -0,0 +1,269 @@
+/*
+ * copyright (c) Stefano Sabatini 2010
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * life video source
+ */
+
+#include "libavcore/parseutils.h"
+#include "avfilter.h"
+#include "parseutils.h"
+
+typedef struct {
+ int w, h;
+ char *buf[2];
+ uint8_t buf_idx;
+ unsigned int rule;
+ uint64_t pts;
+ AVRational time_base;
+} LifeContext;
+
+static void show_life_grid(AVFilterContext *ctx)
+{
+ LifeContext *life = ctx->priv;
+ int i, j;
+
+ char *line = av_malloc(life->w + 1);
+ if (!line)
+ return;
+ for (i = 0; i < life->h; i++) {
+ for (j = 0; j < life->w; j++)
+ line[j] = life->buf[life->buf_idx][i*life->w + j] ? '@' : ' ';
+ line[j] = 0;
+ av_log(ctx, AV_LOG_DEBUG, "%3d: %s\n", i, line);
+ }
+ av_free(line);
+}
+
+static int init_pattern_from_file(AVFilterContext *ctx, const char *filename)
+{
+ LifeContext *life = ctx->priv;
+ char *p, *buf;
+ size_t size;
+ int i, i0, j, j0, h = 0, w, max_w = 0;
+ FILE *f = fopen(filename, "rb");
+
+ if (!f) {
+ av_log(ctx, AV_LOG_ERROR, "Cannot read file '%s': %s\n", filename, strerror(errno));
+ return AVERROR(errno);
+ }
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ buf = av_malloc(size + 1);
+ if (!buf) {
+ fclose(f);
+ return AVERROR(ENOMEM);
+ }
+ fread(buf, 1, size, f);
+ buf[size++] = 0;
+ fclose(f);
+
+ /* prescan file to get the number of lines and the maximum width */
+ w = 0;
+ for (i = 0; i < size; i++) {
+ if (buf[i] == '\n') {
+ h++; max_w = FFMAX(w, max_w); w = 0;
+ } else {
+ w++;
+ }
+ }
+ av_log(ctx, AV_LOG_DEBUG, "h:%d max_w:%d\n", h, max_w);
+
+ if (max_w > life->w || h > life->h) {
+ av_log(ctx, AV_LOG_ERROR, "Grid max size is %dx%d but the provided file has a size of %dx%d\n",
+ life->w, life->h, max_w, h);
+ return AVERROR(EINVAL);
+ }
+ if (!(life->buf[0] = av_mallocz(sizeof(char) * life->h * life->w)) ||
+ !(life->buf[1] = av_mallocz(sizeof(char) * life->h * life->w))) {
+ av_free(life->buf[0]);
+ av_free(life->buf[1]);
+ return AVERROR(ENOMEM);
+ }
+
+ /* fill buf[0] */
+ p = buf;
+ for (i0 = 0, i = (life->h - h)/2; i0 < h; i0++, i++) {
+ for (j0 = 0, j = (life->w - max_w)/2; j0 < max_w; j0++, j++) {
+ av_log(ctx, AV_LOG_DEBUG, "%d:%d %c\n", i, j, *p == '\n' ? 'N' : *p);
+ if (*p == '\n') {
+ p++; break;
+ } else
+ life->buf[0][i*life->w + j] = !!isprint(*(p++));
+ }
+ }
+ life->buf_idx = 0;
+
+ return 0;
+}
+
+static int init(AVFilterContext *ctx, const char *args, void *opaque)
+{
+ LifeContext *life = ctx->priv;
+ char filename [128] = "";
+ char frame_size[128] = "qcif";
+ char frame_rate[128] = "25";
+ AVRational frame_rate_q;
+ int ret;
+ life->rule = 42;
+
+ if (args)
+ sscanf(args, "%127[^:]:%127[^:]:%127[^:]:%d", filename, frame_size, frame_rate, &life->rule);
+
+ if ((ret = av_parse_video_size(&life->w, &life->h, frame_size)) < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid frame size: %s\n", frame_size);
+ return ret;
+ }
+
+ if ((ret = av_parse_video_rate(&frame_rate_q, frame_rate)) < 0 ||
+ frame_rate_q.den <= 0 || frame_rate_q.num <= 0) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: %s\n", frame_rate);
+ return AVERROR(EINVAL);
+ }
+
+ if (life->rule > 255) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid life rule %u, it cannot be > 255\n", life->rule);
+ return AVERROR(EINVAL);
+ }
+
+ life->time_base.num = frame_rate_q.den;
+ life->time_base.den = frame_rate_q.num;
+
+ if (life->w <= 0 || life->h <= 0) {
+ av_log(ctx, AV_LOG_ERROR, "Non-positive size value %dx%d is not acceptable\n", life->w, life->h);
+ return AVERROR(EINVAL);
+ }
+
+ return init_pattern_from_file(ctx, filename);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ LifeContext *life = ctx->priv;
+
+ av_freep(&life->buf[0]);
+ av_freep(&life->buf[1]);
+}
+
+static int config_props(AVFilterLink *outlink)
+{
+ LifeContext *life = outlink->src->priv;
+
+ outlink->w = life->w;
+ outlink->h = life->h;
+
+ av_log(outlink->src, AV_LOG_INFO, "s%dx%d rule:%d\n", life->w, life->h, life->rule);
+ return 0;
+}
+
+static void evolve(AVFilterContext *ctx)
+{
+ LifeContext *life = ctx->priv;
+ int i, j, k, v;
+ uint8_t *oldbuf = life->buf[ life->buf_idx];
+ uint8_t *newbuf = life->buf[!life->buf_idx];
+
+ /* evolve the grid */
+ for (i = 0; i < life->h; i++) {
+ for (k = 0, v = 0, j = 0; j < life->w; j++) {
+ /* compute the number of filled cells near the cell (i j) */
+ int n =
+ ((i-1)<0 || (j-1)<0 ? 0 : oldbuf[(i-1)*life->w +j-1]) +
+ ((i-1)<0 ? 0 : oldbuf[(i-1)*life->w +j ]) +
+ ((i-1)<0 || (j+1)==life->w ? 0 : oldbuf[(i-1)*life->w +j+1]) +
+ ( (j-1)<0 ? 0 : oldbuf[ i *life->w +j-1]) +
+ ( oldbuf[ i *life->w +j ]) +
+ ( (j+1)==life->w ? 0 : oldbuf[ i *life->w +j+1]) +
+ ((i+1)==life->h || (j-1)<0 ? 0 : oldbuf[(i+1)*life->w +j-1]) +
+ ((i+1)==life->h ? 0 : oldbuf[(i+1)*life->w +j ]) +
+ ((i+1)==life->h || (j+1)==life->w ? 0 : oldbuf[(i+1)*life->w +j+1]);
+ v = !!(1<<n & life->rule);
+ av_log(ctx, AV_LOG_DEBUG, "i:%d j:%d k:%d, n:%d v:%d\n", i, j, k, n, v);
+ newbuf[i*life->w+j] = v;
+ }
+ }
+
+ life->buf_idx = !life->buf_idx;
+}
+
+static void fill_picture(AVFilterContext *ctx, AVFilterBufferRef *picref)
+{
+ LifeContext *life = ctx->priv;
+ uint8_t *buf = life->buf[life->buf_idx];
+ int i, j, k;
+
+ /* fill the output picture with the old grid buffer */
+ for (i = 0; i < life->h; i++) {
+ uint8_t byte = 0;
+ uint8_t *p = picref->data[0] + i * picref->linesize[0];
+ for (k = 0, j = 0; j < life->w; j++) {
+ byte |= buf[i*life->w+j]<<(7-k++);
+ if (k==8 || j == life->w-1) {
+ k = 0;
+ *p++ = byte;
+ byte = 0;
+ }
+ }
+ }
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+ LifeContext *life = outlink->src->priv;
+ AVFilterBufferRef *picref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, life->w, life->h);
+ picref->pts = av_rescale_q(life->pts++, life->time_base, AV_TIME_BASE_Q);
+ picref->video->pixel_aspect = (AVRational) {1, 1};
+ picref->pos = -1;
+ avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
+ fill_picture(outlink->src, picref);
+ show_life_grid(outlink->src);
+ evolve(outlink->src);
+ avfilter_draw_slice(outlink, 0, life->h, 1);
+ avfilter_end_frame(outlink);
+ avfilter_unref_buffer(picref);
+
+ return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+ static const enum PixelFormat pix_fmts[] = { PIX_FMT_MONOBLACK, PIX_FMT_NONE };
+ avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
+ return 0;
+}
+
+AVFilter avfilter_vsrc_life = {
+ .name = "life",
+ .description = NULL_IF_CONFIG_SMALL("Create life."),
+
+ .priv_size = sizeof(LifeContext),
+ .init = init,
+ .uninit = uninit,
+ .query_formats = query_formats,
+
+ .inputs = (AVFilterPad[]) {{ .name = NULL}},
+
+ .outputs = (AVFilterPad[]) {{ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .request_frame = request_frame,
+ .config_props = config_props },
+ { .name = NULL}},
+};
--
1.7.1
--MGYHOYXEY6WxJCY8--
More information about the ffmpeg-devel
mailing list