[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  |  273 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 275 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..550372a
--- /dev/null
+++ b/libavfilter/vsrc_life.c
@@ -0,0 +1,273 @@
+/*
+ * 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
+ */
+
+#define DEBUG
+
+#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;
+    uint8_t live_rule, dead_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, 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 (j = (life->w - max_w)/2;; 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] = !!isgraph(*(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 = (8<<8) + 12;     /* Conway's rule */
+
+    if (args)
+        sscanf(args, "%127[^:]:%127[^:]:%127[^:]:%u", 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 ((unsigned)life->rule > ((1<<16)-1)) {
+        av_log(ctx, AV_LOG_ERROR, "Invalid life rule %d, negative or greater than %d\n", life->rule, (1<<16) -1);
+        return AVERROR(EINVAL);
+    }
+    life->live_rule = ((1<<8)-1) & life->rule;
+    life->dead_rule = life->rule >> 8;
+
+    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 live_rule:%d dead_rule:%d\n", life->w, life->h, life->rule, life->live_rule, life->dead_rule);
+    return 0;
+}
+
+static void evolve(AVFilterContext *ctx)
+{
+    LifeContext *life = ctx->priv;
+    int i, j, 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 (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]) +
+                (                  (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 & (oldbuf[i*life->w + j] ? life->live_rule : life->dead_rule));
+            dprintf(ctx, "i:%d j:%d live:%d n:%d v:%d\n", i, j, oldbuf[i*life->w + j], 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


--fdj2RfSjLxBAspz7--



More information about the ffmpeg-devel mailing list