| ... | ... |
@@ -2604,6 +2604,121 @@ this example corresponds to: |
| 2604 | 2604 |
buffer=320:240:6:1:24:1:1 |
| 2605 | 2605 |
@end example |
| 2606 | 2606 |
|
| 2607 |
+@section cellauto |
|
| 2608 |
+ |
|
| 2609 |
+Create a pattern generated by an elementary cellular automaton. |
|
| 2610 |
+ |
|
| 2611 |
+The initial state of the cellular automaton can be defined through the |
|
| 2612 |
+@option{filename}, and @option{pattern} options. If such options are
|
|
| 2613 |
+not specified an initial state is created randomly. |
|
| 2614 |
+ |
|
| 2615 |
+At each new frame a new row in the video is filled with the result of |
|
| 2616 |
+the cellular automaton next generation. The behavior when the whole |
|
| 2617 |
+frame is filled is defined by the @option{scroll} option.
|
|
| 2618 |
+ |
|
| 2619 |
+This source accepts a list of options in the form of |
|
| 2620 |
+@var{key}=@var{value} pairs separated by ":". A description of the
|
|
| 2621 |
+accepted options follows. |
|
| 2622 |
+ |
|
| 2623 |
+@table @option |
|
| 2624 |
+@item filename, f |
|
| 2625 |
+Read the initial cellular automaton state, i.e. the starting row, from |
|
| 2626 |
+the specified file. |
|
| 2627 |
+In the file, each non-whitespace character is considered an alive |
|
| 2628 |
+cell, a newline will terminate the row, and further characters in the |
|
| 2629 |
+file will be ignored. |
|
| 2630 |
+ |
|
| 2631 |
+@item pattern, p |
|
| 2632 |
+Read the initial cellular automaton state, i.e. the starting row, from |
|
| 2633 |
+the specified string. |
|
| 2634 |
+ |
|
| 2635 |
+Each non-whitespace character in the string is considered an alive |
|
| 2636 |
+cell, a newline will terminate the row, and further characters in the |
|
| 2637 |
+string will be ignored. |
|
| 2638 |
+ |
|
| 2639 |
+@item rate, r |
|
| 2640 |
+Set the video rate, that is the number of frames generated per second. |
|
| 2641 |
+Default is 25. |
|
| 2642 |
+ |
|
| 2643 |
+@item random_fill_ratio, ratio |
|
| 2644 |
+Set the random fill ratio for the initial cellular automaton row. It |
|
| 2645 |
+is a floating point number value ranging from 0 to 1, defaults to |
|
| 2646 |
+1/PHI. |
|
| 2647 |
+ |
|
| 2648 |
+This option is ignored when a file or a pattern is specified. |
|
| 2649 |
+ |
|
| 2650 |
+@item random_seed, seed |
|
| 2651 |
+Set the seed for filling randomly the initial row, must be an integer |
|
| 2652 |
+included between 0 and UINT32_MAX. If not specified, or if explicitly |
|
| 2653 |
+set to -1, the filter will try to use a good random seed on a best |
|
| 2654 |
+effort basis. |
|
| 2655 |
+ |
|
| 2656 |
+@item rule |
|
| 2657 |
+Set the cellular automaton rule, it is a number ranging from 0 to 255. |
|
| 2658 |
+Default value is 110. |
|
| 2659 |
+ |
|
| 2660 |
+@item size, s |
|
| 2661 |
+Set the size of the output video. |
|
| 2662 |
+ |
|
| 2663 |
+If @option{filename} or @option{pattern} is specified, the size is set
|
|
| 2664 |
+by default to the width of the specified initial state row, and the |
|
| 2665 |
+height is set to @var{width} * PHI.
|
|
| 2666 |
+ |
|
| 2667 |
+If @option{size} is set, it must contain the width of the specified
|
|
| 2668 |
+pattern string, and the specified pattern will be centered in the |
|
| 2669 |
+larger row. |
|
| 2670 |
+ |
|
| 2671 |
+If a filename or a pattern string is not specified, the size value |
|
| 2672 |
+defaults to "320x518" (used for a randomly generated initial state). |
|
| 2673 |
+ |
|
| 2674 |
+@item scroll |
|
| 2675 |
+If set to 1, scroll the output upward when all the rows in the output |
|
| 2676 |
+have been already filled. If set to 0, the new generated row will be |
|
| 2677 |
+written over the top row just after the bottom row is filled. |
|
| 2678 |
+Defaults to 1. |
|
| 2679 |
+ |
|
| 2680 |
+@item start_full, full |
|
| 2681 |
+If set to 1, completely fill the output with generated rows before |
|
| 2682 |
+outputting the first frame. |
|
| 2683 |
+This is the default behavior, for disabling set the value to 0. |
|
| 2684 |
+ |
|
| 2685 |
+@item stitch |
|
| 2686 |
+If set to 1, stitch the left and right row edges together. |
|
| 2687 |
+This is the default behavior, for disabling set the value to 0. |
|
| 2688 |
+@end table |
|
| 2689 |
+ |
|
| 2690 |
+@subsection Examples |
|
| 2691 |
+ |
|
| 2692 |
+@itemize |
|
| 2693 |
+@item |
|
| 2694 |
+Read the initial state from @file{pattern}, and specify an output of
|
|
| 2695 |
+size 200x400. |
|
| 2696 |
+@example |
|
| 2697 |
+cellauto=f=pattern:s=200x400 |
|
| 2698 |
+@end example |
|
| 2699 |
+ |
|
| 2700 |
+@item |
|
| 2701 |
+Generate a random initial row with a width of 200 cells, with a fill |
|
| 2702 |
+ratio of 2/3: |
|
| 2703 |
+@example |
|
| 2704 |
+cellauto=ratio=2/3:s=200x200 |
|
| 2705 |
+@end example |
|
| 2706 |
+ |
|
| 2707 |
+@item |
|
| 2708 |
+Create a pattern generated by rule 18 starting by a single alive cell |
|
| 2709 |
+centered on an initial row with width 100: |
|
| 2710 |
+@example |
|
| 2711 |
+cellauto=p=@@:s=100x400:full=0:rule=18 |
|
| 2712 |
+@end example |
|
| 2713 |
+ |
|
| 2714 |
+@item |
|
| 2715 |
+Specify a more elaborated initial pattern: |
|
| 2716 |
+@example |
|
| 2717 |
+cellauto=p='@@@@ @@ @@@@':s=100x400:full=0:rule=18 |
|
| 2718 |
+@end example |
|
| 2719 |
+ |
|
| 2720 |
+@end itemize |
|
| 2721 |
+ |
|
| 2607 | 2722 |
@section color |
| 2608 | 2723 |
|
| 2609 | 2724 |
Provide an uniformly colored input. |
| ... | ... |
@@ -84,6 +84,7 @@ OBJS-$(CONFIG_VFLIP_FILTER) += vf_vflip.o |
| 84 | 84 |
OBJS-$(CONFIG_YADIF_FILTER) += vf_yadif.o |
| 85 | 85 |
|
| 86 | 86 |
OBJS-$(CONFIG_BUFFER_FILTER) += vsrc_buffer.o |
| 87 |
+OBJS-$(CONFIG_CELLAUTO_FILTER) += vsrc_cellauto.o |
|
| 87 | 88 |
OBJS-$(CONFIG_COLOR_FILTER) += vsrc_color.o |
| 88 | 89 |
OBJS-$(CONFIG_FREI0R_SRC_FILTER) += vf_frei0r.o |
| 89 | 90 |
OBJS-$(CONFIG_LIFE_FILTER) += vsrc_life.o |
| ... | ... |
@@ -95,6 +95,7 @@ void avfilter_register_all(void) |
| 95 | 95 |
REGISTER_FILTER (YADIF, yadif, vf); |
| 96 | 96 |
|
| 97 | 97 |
REGISTER_FILTER (BUFFER, buffer, vsrc); |
| 98 |
+ REGISTER_FILTER (CELLAUTO, cellauto, vsrc); |
|
| 98 | 99 |
REGISTER_FILTER (COLOR, color, vsrc); |
| 99 | 100 |
REGISTER_FILTER (FREI0R, frei0r_src, vsrc); |
| 100 | 101 |
REGISTER_FILTER (LIFE, life, vsrc); |
| ... | ... |
@@ -29,7 +29,7 @@ |
| 29 | 29 |
#include "libavutil/rational.h" |
| 30 | 30 |
|
| 31 | 31 |
#define LIBAVFILTER_VERSION_MAJOR 2 |
| 32 |
-#define LIBAVFILTER_VERSION_MINOR 52 |
|
| 32 |
+#define LIBAVFILTER_VERSION_MINOR 53 |
|
| 33 | 33 |
#define LIBAVFILTER_VERSION_MICRO 0 |
| 34 | 34 |
|
| 35 | 35 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
| 36 | 36 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,370 @@ |
| 0 |
+/* |
|
| 1 |
+ * Copyright (c) Stefano Sabatini 2011 |
|
| 2 |
+ * |
|
| 3 |
+ * This file is part of FFmpeg. |
|
| 4 |
+ * |
|
| 5 |
+ * FFmpeg is free software; you can redistribute it and/or |
|
| 6 |
+ * modify it under the terms of the GNU Lesser General Public |
|
| 7 |
+ * License as published by the Free Software Foundation; either |
|
| 8 |
+ * version 2.1 of the License, or (at your option) any later version. |
|
| 9 |
+ * |
|
| 10 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
| 11 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 12 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
| 13 |
+ * Lesser General Public License for more details. |
|
| 14 |
+ * |
|
| 15 |
+ * You should have received a copy of the GNU Lesser General Public |
|
| 16 |
+ * License along with FFmpeg; if not, write to the Free Software |
|
| 17 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
| 18 |
+ */ |
|
| 19 |
+ |
|
| 20 |
+/** |
|
| 21 |
+ * @file |
|
| 22 |
+ * cellular automaton video source, based on Stephen Wolfram "experimentus crucis" |
|
| 23 |
+ */ |
|
| 24 |
+ |
|
| 25 |
+/* #define DEBUG */ |
|
| 26 |
+ |
|
| 27 |
+#include "libavutil/file.h" |
|
| 28 |
+#include "libavutil/lfg.h" |
|
| 29 |
+#include "libavutil/opt.h" |
|
| 30 |
+#include "libavutil/parseutils.h" |
|
| 31 |
+#include "libavutil/random_seed.h" |
|
| 32 |
+#include "avfilter.h" |
|
| 33 |
+ |
|
| 34 |
+typedef struct {
|
|
| 35 |
+ const AVClass *class; |
|
| 36 |
+ int w, h; |
|
| 37 |
+ char *filename; |
|
| 38 |
+ char *rule_str; |
|
| 39 |
+ uint8_t *file_buf; |
|
| 40 |
+ size_t file_bufsize; |
|
| 41 |
+ uint8_t *buf; |
|
| 42 |
+ int buf_prev_row_idx, buf_row_idx; |
|
| 43 |
+ uint8_t rule; |
|
| 44 |
+ uint64_t pts; |
|
| 45 |
+ AVRational time_base; |
|
| 46 |
+ char *size; ///< video frame size |
|
| 47 |
+ char *rate; ///< video frame rate |
|
| 48 |
+ double random_fill_ratio; |
|
| 49 |
+ uint32_t random_seed; |
|
| 50 |
+ int stitch, scroll, start_full; |
|
| 51 |
+ int64_t generation; ///< the generation number, starting from 0 |
|
| 52 |
+ AVLFG lfg; |
|
| 53 |
+ char *pattern; |
|
| 54 |
+} CellAutoContext; |
|
| 55 |
+ |
|
| 56 |
+#define OFFSET(x) offsetof(CellAutoContext, x) |
|
| 57 |
+ |
|
| 58 |
+static const AVOption cellauto_options[] = {
|
|
| 59 |
+ { "filename", "read initial pattern from file", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
|
|
| 60 |
+ { "f", "read initial pattern from file", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
|
|
| 61 |
+ { "pattern", "set initial pattern", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
|
|
| 62 |
+ { "p", "set initial pattern", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
|
|
| 63 |
+ { "rate", "set video rate", OFFSET(rate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0 },
|
|
| 64 |
+ { "r", "set video rate", OFFSET(rate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0 },
|
|
| 65 |
+ { "size", "set video size", OFFSET(size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
|
|
| 66 |
+ { "s", "set video size", OFFSET(size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
|
|
| 67 |
+ { "rule", "set rule", OFFSET(rule), AV_OPT_TYPE_INT, {.dbl = 110}, 0, 255 },
|
|
| 68 |
+ { "random_fill_ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl = 1/M_PHI}, 0, 1 },
|
|
| 69 |
+ { "ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl = 1/M_PHI}, 0, 1 },
|
|
| 70 |
+ { "random_seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.dbl = -1}, -1, UINT32_MAX },
|
|
| 71 |
+ { "seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.dbl = -1}, -1, UINT32_MAX },
|
|
| 72 |
+ { "scroll", "scroll pattern downward", OFFSET(scroll), AV_OPT_TYPE_INT, {.dbl = 1}, 0, 1 },
|
|
| 73 |
+ { "start_full", "start filling the whole video", OFFSET(start_full), AV_OPT_TYPE_INT, {.dbl = 0}, 0, 1 },
|
|
| 74 |
+ { "full", "start filling the whole video", OFFSET(start_full), AV_OPT_TYPE_INT, {.dbl = 1}, 0, 1 },
|
|
| 75 |
+ { "stitch", "stitch boundaries", OFFSET(stitch), AV_OPT_TYPE_INT, {.dbl = 1}, 0, 1 },
|
|
| 76 |
+ { NULL },
|
|
| 77 |
+}; |
|
| 78 |
+ |
|
| 79 |
+static const char *cellauto_get_name(void *ctx) |
|
| 80 |
+{
|
|
| 81 |
+ return "cellauto"; |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+static const AVClass cellauto_class = {
|
|
| 85 |
+ "CellAutoContext", |
|
| 86 |
+ cellauto_get_name, |
|
| 87 |
+ cellauto_options |
|
| 88 |
+}; |
|
| 89 |
+ |
|
| 90 |
+#ifdef DEBUG |
|
| 91 |
+static void show_cellauto_row(AVFilterContext *ctx) |
|
| 92 |
+{
|
|
| 93 |
+ CellAutoContext *cellauto = ctx->priv; |
|
| 94 |
+ int i; |
|
| 95 |
+ uint8_t *row = cellauto->buf + cellauto->w * cellauto->buf_row_idx; |
|
| 96 |
+ char *line = av_malloc(cellauto->w + 1); |
|
| 97 |
+ if (!line) |
|
| 98 |
+ return; |
|
| 99 |
+ |
|
| 100 |
+ for (i = 0; i < cellauto->w; i++) |
|
| 101 |
+ line[i] = row[i] ? '@' : ' '; |
|
| 102 |
+ line[i] = 0; |
|
| 103 |
+ av_log(ctx, AV_LOG_DEBUG, "generation:%"PRId64" row:%s|\n", cellauto->generation, line); |
|
| 104 |
+ av_free(line); |
|
| 105 |
+} |
|
| 106 |
+#endif |
|
| 107 |
+ |
|
| 108 |
+static int init_pattern_from_string(AVFilterContext *ctx) |
|
| 109 |
+{
|
|
| 110 |
+ CellAutoContext *cellauto = ctx->priv; |
|
| 111 |
+ char *p; |
|
| 112 |
+ int i, w = 0; |
|
| 113 |
+ |
|
| 114 |
+ w = strlen(cellauto->pattern); |
|
| 115 |
+ av_log(ctx, AV_LOG_DEBUG, "w:%d\n", w); |
|
| 116 |
+ |
|
| 117 |
+ if (cellauto->size) {
|
|
| 118 |
+ if (w > cellauto->w) {
|
|
| 119 |
+ av_log(ctx, AV_LOG_ERROR, |
|
| 120 |
+ "The specified width is %d which cannot contain the provided string width of %d\n", |
|
| 121 |
+ cellauto->w, w); |
|
| 122 |
+ return AVERROR(EINVAL); |
|
| 123 |
+ } |
|
| 124 |
+ } else {
|
|
| 125 |
+ /* width was not specified, set it to width of the provided row */ |
|
| 126 |
+ cellauto->w = w; |
|
| 127 |
+ cellauto->h = (double)cellauto->w * M_PHI; |
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ cellauto->buf = av_mallocz(sizeof(uint8_t) * cellauto->w * cellauto->h); |
|
| 131 |
+ if (!cellauto->buf) |
|
| 132 |
+ return AVERROR(ENOMEM); |
|
| 133 |
+ |
|
| 134 |
+ /* fill buf */ |
|
| 135 |
+ p = cellauto->pattern; |
|
| 136 |
+ for (i = (cellauto->w - w)/2;; i++) {
|
|
| 137 |
+ av_log(ctx, AV_LOG_DEBUG, "%d %c\n", i, *p == '\n' ? 'N' : *p); |
|
| 138 |
+ if (*p == '\n' || !*p) |
|
| 139 |
+ break; |
|
| 140 |
+ else |
|
| 141 |
+ cellauto->buf[i] = !!isgraph(*(p++)); |
|
| 142 |
+ } |
|
| 143 |
+ |
|
| 144 |
+ return 0; |
|
| 145 |
+} |
|
| 146 |
+ |
|
| 147 |
+static int init_pattern_from_file(AVFilterContext *ctx) |
|
| 148 |
+{
|
|
| 149 |
+ CellAutoContext *cellauto = ctx->priv; |
|
| 150 |
+ int ret; |
|
| 151 |
+ |
|
| 152 |
+ ret = av_file_map(cellauto->filename, |
|
| 153 |
+ &cellauto->file_buf, &cellauto->file_bufsize, 0, ctx); |
|
| 154 |
+ if (ret < 0) |
|
| 155 |
+ return ret; |
|
| 156 |
+ |
|
| 157 |
+ /* create a string based on the read file */ |
|
| 158 |
+ cellauto->pattern = av_malloc(cellauto->file_bufsize + 1); |
|
| 159 |
+ if (!cellauto->pattern) |
|
| 160 |
+ return AVERROR(ENOMEM); |
|
| 161 |
+ memcpy(cellauto->pattern, cellauto->file_buf, cellauto->file_bufsize); |
|
| 162 |
+ cellauto->pattern[cellauto->file_bufsize] = 0; |
|
| 163 |
+ |
|
| 164 |
+ return init_pattern_from_string(ctx); |
|
| 165 |
+} |
|
| 166 |
+ |
|
| 167 |
+static int init(AVFilterContext *ctx, const char *args, void *opaque) |
|
| 168 |
+{
|
|
| 169 |
+ CellAutoContext *cellauto = ctx->priv; |
|
| 170 |
+ AVRational frame_rate; |
|
| 171 |
+ int ret; |
|
| 172 |
+ |
|
| 173 |
+ cellauto->class = &cellauto_class; |
|
| 174 |
+ av_opt_set_defaults(cellauto); |
|
| 175 |
+ |
|
| 176 |
+ if ((ret = av_set_options_string(cellauto, args, "=", ":")) < 0) {
|
|
| 177 |
+ av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args); |
|
| 178 |
+ return ret; |
|
| 179 |
+ } |
|
| 180 |
+ |
|
| 181 |
+ if ((ret = av_parse_video_rate(&frame_rate, cellauto->rate)) < 0) {
|
|
| 182 |
+ av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: %s\n", cellauto->rate); |
|
| 183 |
+ return AVERROR(EINVAL); |
|
| 184 |
+ } |
|
| 185 |
+ |
|
| 186 |
+ if (!cellauto->size && !cellauto->filename && !cellauto->pattern) |
|
| 187 |
+ av_opt_set(cellauto, "size", "320x518", 0); |
|
| 188 |
+ |
|
| 189 |
+ if (cellauto->size && |
|
| 190 |
+ (ret = av_parse_video_size(&cellauto->w, &cellauto->h, cellauto->size)) < 0) {
|
|
| 191 |
+ av_log(ctx, AV_LOG_ERROR, "Invalid frame size: %s\n", cellauto->size); |
|
| 192 |
+ return ret; |
|
| 193 |
+ } |
|
| 194 |
+ |
|
| 195 |
+ cellauto->time_base.num = frame_rate.den; |
|
| 196 |
+ cellauto->time_base.den = frame_rate.num; |
|
| 197 |
+ |
|
| 198 |
+ if (cellauto->filename && cellauto->pattern) {
|
|
| 199 |
+ av_log(ctx, AV_LOG_ERROR, "Only one of the filename or pattern options can be used\n"); |
|
| 200 |
+ return AVERROR(EINVAL); |
|
| 201 |
+ } |
|
| 202 |
+ |
|
| 203 |
+ if (cellauto->filename) {
|
|
| 204 |
+ if ((ret = init_pattern_from_file(ctx)) < 0) |
|
| 205 |
+ return ret; |
|
| 206 |
+ } else if (cellauto->pattern) {
|
|
| 207 |
+ if ((ret = init_pattern_from_string(ctx)) < 0) |
|
| 208 |
+ return ret; |
|
| 209 |
+ } else {
|
|
| 210 |
+ /* fill the first row randomly */ |
|
| 211 |
+ int i; |
|
| 212 |
+ |
|
| 213 |
+ cellauto->buf = av_mallocz(sizeof(uint8_t) * cellauto->w * cellauto->h); |
|
| 214 |
+ if (!cellauto->buf) |
|
| 215 |
+ return AVERROR(ENOMEM); |
|
| 216 |
+ if (cellauto->random_seed == -1) |
|
| 217 |
+ cellauto->random_seed = av_get_random_seed(); |
|
| 218 |
+ |
|
| 219 |
+ av_lfg_init(&cellauto->lfg, cellauto->random_seed); |
|
| 220 |
+ |
|
| 221 |
+ for (i = 0; i < cellauto->w; i++) {
|
|
| 222 |
+ double r = (double)av_lfg_get(&cellauto->lfg) / UINT32_MAX; |
|
| 223 |
+ if (r <= cellauto->random_fill_ratio) |
|
| 224 |
+ cellauto->buf[i] = 1; |
|
| 225 |
+ } |
|
| 226 |
+ } |
|
| 227 |
+ |
|
| 228 |
+ av_log(ctx, AV_LOG_INFO, |
|
| 229 |
+ "s:%dx%d r:%d/%d rule:%d stitch:%d scroll:%d full:%d seed:%u\n", |
|
| 230 |
+ cellauto->w, cellauto->h, frame_rate.num, frame_rate.den, |
|
| 231 |
+ cellauto->rule, cellauto->stitch, cellauto->scroll, cellauto->start_full, |
|
| 232 |
+ cellauto->random_seed); |
|
| 233 |
+ return 0; |
|
| 234 |
+} |
|
| 235 |
+ |
|
| 236 |
+static av_cold void uninit(AVFilterContext *ctx) |
|
| 237 |
+{
|
|
| 238 |
+ CellAutoContext *cellauto = ctx->priv; |
|
| 239 |
+ |
|
| 240 |
+ av_file_unmap(cellauto->file_buf, cellauto->file_bufsize); |
|
| 241 |
+ av_freep(&cellauto->buf); |
|
| 242 |
+ av_freep(&cellauto->pattern); |
|
| 243 |
+} |
|
| 244 |
+ |
|
| 245 |
+static int config_props(AVFilterLink *outlink) |
|
| 246 |
+{
|
|
| 247 |
+ CellAutoContext *cellauto = outlink->src->priv; |
|
| 248 |
+ |
|
| 249 |
+ outlink->w = cellauto->w; |
|
| 250 |
+ outlink->h = cellauto->h; |
|
| 251 |
+ outlink->time_base = cellauto->time_base; |
|
| 252 |
+ |
|
| 253 |
+ return 0; |
|
| 254 |
+} |
|
| 255 |
+ |
|
| 256 |
+static void evolve(AVFilterContext *ctx) |
|
| 257 |
+{
|
|
| 258 |
+ CellAutoContext *cellauto = ctx->priv; |
|
| 259 |
+ int i, v, pos[3]; |
|
| 260 |
+ uint8_t *row, *prev_row = cellauto->buf + cellauto->buf_row_idx * cellauto->w; |
|
| 261 |
+ enum { NW, N, NE };
|
|
| 262 |
+ |
|
| 263 |
+ cellauto->buf_prev_row_idx = cellauto->buf_row_idx; |
|
| 264 |
+ cellauto->buf_row_idx = cellauto->buf_row_idx == cellauto->h-1 ? 0 : cellauto->buf_row_idx+1; |
|
| 265 |
+ row = cellauto->buf + cellauto->w * cellauto->buf_row_idx; |
|
| 266 |
+ |
|
| 267 |
+ for (i = 0; i < cellauto->w; i++) {
|
|
| 268 |
+ if (cellauto->stitch) {
|
|
| 269 |
+ pos[NW] = i-1 < 0 ? cellauto->w-1 : i-1; |
|
| 270 |
+ pos[N] = i; |
|
| 271 |
+ pos[NE] = i+1 == cellauto->w ? 0 : i+1; |
|
| 272 |
+ v = prev_row[pos[NW]]<<2 | prev_row[pos[N]]<<1 | prev_row[pos[NE]]; |
|
| 273 |
+ } else {
|
|
| 274 |
+ v = 0; |
|
| 275 |
+ v|= i-1 >= 0 ? prev_row[i-1]<<2 : 0; |
|
| 276 |
+ v|= prev_row[i ]<<1 ; |
|
| 277 |
+ v|= i+1 < cellauto->w ? prev_row[i+1] : 0; |
|
| 278 |
+ } |
|
| 279 |
+ row[i] = !!(cellauto->rule & (1<<v)); |
|
| 280 |
+ av_dlog(ctx, "i:%d context:%c%c%c -> cell:%d\n", i, |
|
| 281 |
+ v&4?'@':' ', v&2?'@':' ', v&1?'@':' ', row[i]); |
|
| 282 |
+ } |
|
| 283 |
+ |
|
| 284 |
+ cellauto->generation++; |
|
| 285 |
+} |
|
| 286 |
+ |
|
| 287 |
+static void fill_picture(AVFilterContext *ctx, AVFilterBufferRef *picref) |
|
| 288 |
+{
|
|
| 289 |
+ CellAutoContext *cellauto = ctx->priv; |
|
| 290 |
+ int i, j, k, row_idx = 0; |
|
| 291 |
+ uint8_t *p0 = picref->data[0]; |
|
| 292 |
+ |
|
| 293 |
+ if (cellauto->scroll && cellauto->generation >= cellauto->h) |
|
| 294 |
+ /* show on top the oldest row */ |
|
| 295 |
+ row_idx = (cellauto->buf_row_idx + 1) % cellauto->h; |
|
| 296 |
+ |
|
| 297 |
+ /* fill the output picture with the whole buffer */ |
|
| 298 |
+ for (i = 0; i < cellauto->h; i++) {
|
|
| 299 |
+ uint8_t byte = 0; |
|
| 300 |
+ uint8_t *row = cellauto->buf + row_idx*cellauto->w; |
|
| 301 |
+ uint8_t *p = p0; |
|
| 302 |
+ for (k = 0, j = 0; j < cellauto->w; j++) {
|
|
| 303 |
+ byte |= row[j]<<(7-k++); |
|
| 304 |
+ if (k==8 || j == cellauto->w-1) {
|
|
| 305 |
+ k = 0; |
|
| 306 |
+ *p++ = byte; |
|
| 307 |
+ byte = 0; |
|
| 308 |
+ } |
|
| 309 |
+ } |
|
| 310 |
+ row_idx = (row_idx + 1) % cellauto->h; |
|
| 311 |
+ p0 += picref->linesize[0]; |
|
| 312 |
+ } |
|
| 313 |
+} |
|
| 314 |
+ |
|
| 315 |
+static int request_frame(AVFilterLink *outlink) |
|
| 316 |
+{
|
|
| 317 |
+ CellAutoContext *cellauto = outlink->src->priv; |
|
| 318 |
+ AVFilterBufferRef *picref = |
|
| 319 |
+ avfilter_get_video_buffer(outlink, AV_PERM_WRITE, cellauto->w, cellauto->h); |
|
| 320 |
+ picref->video->sample_aspect_ratio = (AVRational) {1, 1};
|
|
| 321 |
+ if (cellauto->generation == 0 && cellauto->start_full) {
|
|
| 322 |
+ int i; |
|
| 323 |
+ for (i = 0; i < cellauto->h-1; i++) |
|
| 324 |
+ evolve(outlink->src); |
|
| 325 |
+ } |
|
| 326 |
+ fill_picture(outlink->src, picref); |
|
| 327 |
+ evolve(outlink->src); |
|
| 328 |
+ |
|
| 329 |
+ picref->pts = cellauto->pts++; |
|
| 330 |
+ picref->pos = -1; |
|
| 331 |
+ |
|
| 332 |
+#ifdef DEBUG |
|
| 333 |
+ show_cellauto_row(outlink->src); |
|
| 334 |
+#endif |
|
| 335 |
+ |
|
| 336 |
+ avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0)); |
|
| 337 |
+ avfilter_draw_slice(outlink, 0, cellauto->h, 1); |
|
| 338 |
+ avfilter_end_frame(outlink); |
|
| 339 |
+ avfilter_unref_buffer(picref); |
|
| 340 |
+ |
|
| 341 |
+ return 0; |
|
| 342 |
+} |
|
| 343 |
+ |
|
| 344 |
+static int query_formats(AVFilterContext *ctx) |
|
| 345 |
+{
|
|
| 346 |
+ static const enum PixelFormat pix_fmts[] = { PIX_FMT_MONOBLACK, PIX_FMT_NONE };
|
|
| 347 |
+ avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts)); |
|
| 348 |
+ return 0; |
|
| 349 |
+} |
|
| 350 |
+ |
|
| 351 |
+AVFilter avfilter_vsrc_cellauto = {
|
|
| 352 |
+ .name = "cellauto", |
|
| 353 |
+ .description = NULL_IF_CONFIG_SMALL("Create pattern generated by an elementary cellular automaton."),
|
|
| 354 |
+ .priv_size = sizeof(CellAutoContext), |
|
| 355 |
+ .init = init, |
|
| 356 |
+ .uninit = uninit, |
|
| 357 |
+ .query_formats = query_formats, |
|
| 358 |
+ |
|
| 359 |
+ .inputs = (const AVFilterPad[]) {
|
|
| 360 |
+ { .name = NULL}
|
|
| 361 |
+ }, |
|
| 362 |
+ .outputs = (const AVFilterPad[]) {
|
|
| 363 |
+ { .name = "default",
|
|
| 364 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
| 365 |
+ .request_frame = request_frame, |
|
| 366 |
+ .config_props = config_props }, |
|
| 367 |
+ { .name = NULL}
|
|
| 368 |
+ }, |
|
| 369 |
+}; |