FFmpeg
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)
double random_fill_ratio
Definition: vsrc_cellauto.c:52
This structure describes decoded (raw) audio or video data.
Definition: frame.h:295
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:1093
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:388
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 i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
#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:144
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:569
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 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:114
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
#define s(width, name)
Definition: cbs_vp9.c:257
static const AVFilterPad outputs[]
Definition: af_acontrast.c:203
#define FLAGS
Definition: vsrc_cellauto.c:61
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_WB32 unsigned int_TMPL AV_WB24 unsigned int_TMPL AV_WB16 unsigned int_TMPL byte
Definition: bytestream.h:87
int linesize[AV_NUM_DATA_POINTERS]
For video, size in bytes of each picture line.
Definition: frame.h:326
AVRational sample_aspect_ratio
Sample aspect ratio for the video frame, 0/1 if unknown/unspecified.
Definition: frame.h:383
static unsigned int av_lfg_get(AVLFG *c)
Get the next random unsigned 32-bit number using an ALFG.
Definition: lfg.h:47
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several inputs
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:275
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:309
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:76
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
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
AVPixelFormat
Pixel format.
Definition: pixfmt.h:64
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)
Definition: mem.c:191