FFmpeg  4.0
vsrc_cellauto.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) Stefano Sabatini 2011
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /**
22  * @file
23  * cellular automaton video source, based on Stephen Wolfram "experimentus crucis"
24  */
25 
26 /* #define DEBUG */
27 
28 #include "libavutil/file.h"
29 #include "libavutil/internal.h"
30 #include "libavutil/lfg.h"
31 #include "libavutil/opt.h"
32 #include "libavutil/parseutils.h"
33 #include "libavutil/random_seed.h"
34 #include "libavutil/avstring.h"
35 #include "avfilter.h"
36 #include "internal.h"
37 #include "formats.h"
38 #include "video.h"
39 
40 typedef struct CellAutoContext {
41  const AVClass *class;
42  int w, h;
43  char *filename;
44  char *rule_str;
46  size_t file_bufsize;
50  uint64_t pts;
53  uint32_t random_seed;
55  int64_t generation; ///< the generation number, starting from 0
57  char *pattern;
59 
60 #define OFFSET(x) offsetof(CellAutoContext, x)
61 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
62 
63 static const AVOption cellauto_options[] = {
64  { "filename", "read initial pattern from file", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
65  { "f", "read initial pattern from file", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
66  { "pattern", "set initial pattern", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
67  { "p", "set initial pattern", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
68  { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS },
69  { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS },
70  { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, FLAGS },
71  { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, FLAGS },
72  { "rule", "set rule", OFFSET(rule), AV_OPT_TYPE_INT, {.i64 = 110}, 0, 255, FLAGS },
73  { "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, FLAGS },
74  { "ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl = 1/M_PHI}, 0, 1, FLAGS },
75  { "random_seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64 = -1}, -1, UINT32_MAX, FLAGS },
76  { "seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64 = -1}, -1, UINT32_MAX, FLAGS },
77  { "scroll", "scroll pattern downward", OFFSET(scroll), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS },
78  { "start_full", "start filling the whole video", OFFSET(start_full), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS },
79  { "full", "start filling the whole video", OFFSET(start_full), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS },
80  { "stitch", "stitch boundaries", OFFSET(stitch), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS },
81  { NULL }
82 };
83 
84 AVFILTER_DEFINE_CLASS(cellauto);
85 
86 #ifdef DEBUG
87 static void show_cellauto_row(AVFilterContext *ctx)
88 {
89  CellAutoContext *s = ctx->priv;
90  int i;
91  uint8_t *row = s->buf + s->w * s->buf_row_idx;
92  char *line = av_malloc(s->w + 1);
93  if (!line)
94  return;
95 
96  for (i = 0; i < s->w; i++)
97  line[i] = row[i] ? '@' : ' ';
98  line[i] = 0;
99  av_log(ctx, AV_LOG_DEBUG, "generation:%"PRId64" row:%s|\n", s->generation, line);
100  av_free(line);
101 }
102 #endif
103 
105 {
106  CellAutoContext *s = ctx->priv;
107  char *p;
108  int i, w = 0;
109 
110  w = strlen(s->pattern);
111  av_log(ctx, AV_LOG_DEBUG, "w:%d\n", w);
112 
113  if (s->w) {
114  if (w > s->w) {
115  av_log(ctx, AV_LOG_ERROR,
116  "The specified width is %d which cannot contain the provided string width of %d\n",
117  s->w, w);
118  return AVERROR(EINVAL);
119  }
120  } else {
121  /* width was not specified, set it to width of the provided row */
122  s->w = w;
123  s->h = (double)s->w * M_PHI;
124  }
125 
126  s->buf = av_mallocz_array(sizeof(uint8_t) * s->w, s->h);
127  if (!s->buf)
128  return AVERROR(ENOMEM);
129 
130  /* fill buf */
131  p = s->pattern;
132  for (i = (s->w - w)/2;; i++) {
133  av_log(ctx, AV_LOG_DEBUG, "%d %c\n", i, *p == '\n' ? 'N' : *p);
134  if (*p == '\n' || !*p)
135  break;
136  else
137  s->buf[i] = !!av_isgraph(*(p++));
138  }
139 
140  return 0;
141 }
142 
144 {
145  CellAutoContext *s = ctx->priv;
146  int ret;
147 
148  ret = av_file_map(s->filename,
149  &s->file_buf, &s->file_bufsize, 0, ctx);
150  if (ret < 0)
151  return ret;
152 
153  /* create a string based on the read file */
154  s->pattern = av_malloc(s->file_bufsize + 1);
155  if (!s->pattern)
156  return AVERROR(ENOMEM);
157  memcpy(s->pattern, s->file_buf, s->file_bufsize);
158  s->pattern[s->file_bufsize] = 0;
159 
160  return init_pattern_from_string(ctx);
161 }
162 
163 static av_cold int init(AVFilterContext *ctx)
164 {
165  CellAutoContext *s = ctx->priv;
166  int ret;
167 
168  if (!s->w && !s->filename && !s->pattern)
169  av_opt_set(s, "size", "320x518", 0);
170 
171  if (s->filename && s->pattern) {
172  av_log(ctx, AV_LOG_ERROR, "Only one of the filename or pattern options can be used\n");
173  return AVERROR(EINVAL);
174  }
175 
176  if (s->filename) {
177  if ((ret = init_pattern_from_file(ctx)) < 0)
178  return ret;
179  } else if (s->pattern) {
180  if ((ret = init_pattern_from_string(ctx)) < 0)
181  return ret;
182  } else {
183  /* fill the first row randomly */
184  int i;
185 
186  s->buf = av_mallocz_array(sizeof(uint8_t) * s->w, s->h);
187  if (!s->buf)
188  return AVERROR(ENOMEM);
189  if (s->random_seed == -1)
191 
192  av_lfg_init(&s->lfg, s->random_seed);
193 
194  for (i = 0; i < s->w; i++) {
195  double r = (double)av_lfg_get(&s->lfg) / UINT32_MAX;
196  if (r <= s->random_fill_ratio)
197  s->buf[i] = 1;
198  }
199  }
200 
201  av_log(ctx, AV_LOG_VERBOSE,
202  "s:%dx%d r:%d/%d rule:%d stitch:%d scroll:%d full:%d seed:%"PRIu32"\n",
203  s->w, s->h, s->frame_rate.num, s->frame_rate.den,
204  s->rule, s->stitch, s->scroll, s->start_full,
205  s->random_seed);
206  return 0;
207 }
208 
209 static av_cold void uninit(AVFilterContext *ctx)
210 {
211  CellAutoContext *s = ctx->priv;
212 
214  av_freep(&s->buf);
215  av_freep(&s->pattern);
216 }
217 
218 static int config_props(AVFilterLink *outlink)
219 {
220  CellAutoContext *s = outlink->src->priv;
221 
222  outlink->w = s->w;
223  outlink->h = s->h;
224  outlink->time_base = av_inv_q(s->frame_rate);
225 
226  return 0;
227 }
228 
229 static void evolve(AVFilterContext *ctx)
230 {
231  CellAutoContext *s = ctx->priv;
232  int i, v, pos[3];
233  uint8_t *row, *prev_row = s->buf + s->buf_row_idx * s->w;
234  enum { NW, N, NE };
235 
237  s->buf_row_idx = s->buf_row_idx == s->h-1 ? 0 : s->buf_row_idx+1;
238  row = s->buf + s->w * s->buf_row_idx;
239 
240  for (i = 0; i < s->w; i++) {
241  if (s->stitch) {
242  pos[NW] = i-1 < 0 ? s->w-1 : i-1;
243  pos[N] = i;
244  pos[NE] = i+1 == s->w ? 0 : i+1;
245  v = prev_row[pos[NW]]<<2 | prev_row[pos[N]]<<1 | prev_row[pos[NE]];
246  } else {
247  v = 0;
248  v|= i-1 >= 0 ? prev_row[i-1]<<2 : 0;
249  v|= prev_row[i ]<<1 ;
250  v|= i+1 < s->w ? prev_row[i+1] : 0;
251  }
252  row[i] = !!(s->rule & (1<<v));
253  ff_dlog(ctx, "i:%d context:%c%c%c -> cell:%d\n", i,
254  v&4?'@':' ', v&2?'@':' ', v&1?'@':' ', row[i]);
255  }
256 
257  s->generation++;
258 }
259 
260 static void fill_picture(AVFilterContext *ctx, AVFrame *picref)
261 {
262  CellAutoContext *s = ctx->priv;
263  int i, j, k, row_idx = 0;
264  uint8_t *p0 = picref->data[0];
265 
266  if (s->scroll && s->generation >= s->h)
267  /* show on top the oldest row */
268  row_idx = (s->buf_row_idx + 1) % s->h;
269 
270  /* fill the output picture with the whole buffer */
271  for (i = 0; i < s->h; i++) {
272  uint8_t byte = 0;
273  uint8_t *row = s->buf + row_idx*s->w;
274  uint8_t *p = p0;
275  for (k = 0, j = 0; j < s->w; j++) {
276  byte |= row[j]<<(7-k++);
277  if (k==8 || j == s->w-1) {
278  k = 0;
279  *p++ = byte;
280  byte = 0;
281  }
282  }
283  row_idx = (row_idx + 1) % s->h;
284  p0 += picref->linesize[0];
285  }
286 }
287 
288 static int request_frame(AVFilterLink *outlink)
289 {
290  CellAutoContext *s = outlink->src->priv;
291  AVFrame *picref = ff_get_video_buffer(outlink, s->w, s->h);
292  if (!picref)
293  return AVERROR(ENOMEM);
294  picref->sample_aspect_ratio = (AVRational) {1, 1};
295  if (s->generation == 0 && s->start_full) {
296  int i;
297  for (i = 0; i < s->h-1; i++)
298  evolve(outlink->src);
299  }
300  fill_picture(outlink->src, picref);
301  evolve(outlink->src);
302 
303  picref->pts = s->pts++;
304 
305 #ifdef DEBUG
306  show_cellauto_row(outlink->src);
307 #endif
308  return ff_filter_frame(outlink, picref);
309 }
310 
312 {
313  static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_MONOBLACK, AV_PIX_FMT_NONE };
314  AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
315  if (!fmts_list)
316  return AVERROR(ENOMEM);
317  return ff_set_common_formats(ctx, fmts_list);
318 }
319 
320 static const AVFilterPad cellauto_outputs[] = {
321  {
322  .name = "default",
323  .type = AVMEDIA_TYPE_VIDEO,
324  .request_frame = request_frame,
325  .config_props = config_props,
326  },
327  { NULL }
328 };
329 
331  .name = "cellauto",
332  .description = NULL_IF_CONFIG_SMALL("Create pattern generated by an elementary cellular automaton."),
333  .priv_size = sizeof(CellAutoContext),
334  .priv_class = &cellauto_class,
335  .init = init,
336  .uninit = uninit,
338  .inputs = NULL,
339  .outputs = cellauto_outputs,
340 };
Definition: lfg.h:27
#define NULL
Definition: coverity.c:32
static int request_frame(AVFilterLink *outlink)
const char * s
Definition: avisynth_c.h:768
double random_fill_ratio
Definition: vsrc_cellauto.c:52
This structure describes decoded (raw) audio or video data.
Definition: frame.h:218
AVOption.
Definition: opt.h:246
Main libavfilter public API header.
int num
Numerator.
Definition: rational.h:59
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition: video.c:99
#define OFFSET(x)
Definition: vsrc_cellauto.c:60
#define N
Definition: af_mcompand.c:54
AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
Definition: formats.c:283
static av_cold int init(AVFilterContext *ctx)
const char * name
Pad name.
Definition: internal.h:60
AVRational frame_rate
Definition: vsrc_cellauto.c:51
static const AVFilterPad cellauto_outputs[]
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1080
uint8_t
#define av_cold
Definition: attributes.h:82
#define av_malloc(s)
AVOptions.
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition: frame.h:311
Misc file utilities.
#define ff_dlog(a,...)
static int config_props(AVFilterLink *outlink)
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:192
#define av_log(a,...)
A filter pad used for either input or output.
Definition: internal.h:54
uint32_t random_seed
Definition: vsrc_cellauto.c:53
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
void av_file_unmap(uint8_t *bufptr, size_t size)
Unmap or free the buffer bufptr created by av_file_map().
Definition: file.c:133
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
A helper for query_formats() which sets all links to the same list of formats.
Definition: formats.c:568
int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, int log_offset, void *log_ctx)
Read the file with name filename, and put its content in a newly allocated buffer or map it with mmap...
Definition: file.c:53
#define AVERROR(e)
Definition: error.h:43
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:186
const char * r
Definition: vf_curves.c:111
void * priv
private data for use by the filter
Definition: avfilter.h:353
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:197
Definition: graph2dot.c:48
uint8_t * file_buf
Definition: vsrc_cellauto.c:45
static int init_pattern_from_file(AVFilterContext *ctx)
static void fill_picture(AVFilterContext *ctx, AVFrame *picref)
common internal API header
static void evolve(AVFilterContext *ctx)
AVFormatContext * ctx
Definition: movenc.c:48
AVFilter ff_vsrc_cellauto
static const AVFilterPad inputs[]
Definition: af_acontrast.c:193
static const AVFilterPad outputs[]
Definition: af_acontrast.c:203
#define FLAGS
Definition: vsrc_cellauto.c:61
int linesize[AV_NUM_DATA_POINTERS]
For video, size in bytes of each picture line.
Definition: frame.h:249
AVRational sample_aspect_ratio
Sample aspect ratio for the video frame, 0/1 if unknown/unspecified.
Definition: frame.h:306
static unsigned int av_lfg_get(AVLFG *c)
Get the next random unsigned 32-bit number using an ALFG.
Definition: lfg.h:47
Describe the class of an AVClass context structure.
Definition: log.h:67
Filter definition.
Definition: avfilter.h:144
Rational number (pair of numerator and denominator).
Definition: rational.h:58
#define M_PHI
Definition: mathematics.h:49
static int query_formats(AVFilterContext *ctx)
offset must point to AVRational
Definition: opt.h:236
const char * name
Filter name.
Definition: avfilter.h:148
av_cold void av_lfg_init(AVLFG *c, unsigned int seed)
Definition: lfg.c:32
offset must point to two consecutive integers
Definition: opt.h:233
misc parsing utilities
static enum AVPixelFormat pix_fmts[]
Definition: libkvazaar.c:266
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:232
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
Definition: rational.h:159
static av_const int av_isgraph(int c)
Locale-independent conversion of ASCII isgraph.
Definition: avstring.h:214
Y , 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb...
Definition: pixfmt.h:72
static int init_pattern_from_string(AVFilterContext *ctx)
static const AVOption cellauto_options[]
Definition: vsrc_cellauto.c:63
int64_t generation
the generation number, starting from 0
Definition: vsrc_cellauto.c:55
int den
Denominator.
Definition: rational.h:60
static av_cold void uninit(AVFilterContext *ctx)
AVFILTER_DEFINE_CLASS(cellauto)
#define av_free(p)
A list of supported formats for one end of a filter link.
Definition: formats.h:64
An instance of a filter.
Definition: avfilter.h:338
#define av_freep(p)
uint32_t av_get_random_seed(void)
Get a seed to use in conjunction with random functions.
Definition: random_seed.c:120
internal API functions
AVPixelFormat
Pixel format.
Definition: pixfmt.h:60
int av_opt_set(void *obj, const char *name, const char *val, int search_flags)
Definition: opt.c:449
for(j=16;j >0;--j)
void * av_mallocz_array(size_t nmemb, size_t size)
Allocate a memory block for an array with av_mallocz().
Definition: mem.c:191