... | ... |
@@ -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 |
+}; |