Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
F
ffmpeg.wasm-core
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Linshizhi
ffmpeg.wasm-core
Commits
2f8b6e90
Commit
2f8b6e90
authored
Jun 26, 2011
by
Stefano Sabatini
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
lavfi: add life source
parent
6c26fe8b
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
522 additions
and
2 deletions
+522
-2
Changelog
Changelog
+1
-0
filters.texi
doc/filters.texi
+107
-0
Makefile
libavfilter/Makefile
+1
-0
allfilters.c
libavfilter/allfilters.c
+1
-0
avfilter.h
libavfilter/avfilter.h
+2
-2
vsrc_life.c
libavfilter/vsrc_life.c
+410
-0
No files found.
Changelog
View file @
2f8b6e90
...
...
@@ -128,6 +128,7 @@ easier to use. The changes are:
- CRI ADX audio format demuxer
- Playstation Portable PMP format demuxer
- Microsoft Windows ICO demuxer
- life source
version 0.8:
...
...
doc/filters.texi
View file @
2f8b6e90
...
...
@@ -2782,6 +2782,113 @@ Some examples follow:
frei0r_src=200x200:10:partik0l=1234 [overlay]; [in][overlay] overlay
@end example
@section life
Generate a life pattern.
This source is based on a generalization of John Conway's life game.
The sourced input represents a life grid, each pixel represents a cell
which can be in one of two possible states, alive or dead. Every cell
interacts with its eight neighbours, which are the cells that are
horizontally, vertically, or diagonally adjacent.
At each interaction the grid evolves according to the adopted rule,
which specifies the number of neighbor alive cells which will make a
cell stay alive or born. The @option{rule} option allows to specify
the rule to adopt.
This source accepts a list of options in the form of
@var{key}=@var{value} pairs separated by ":". A description of the
accepted options follows.
@table @option
@item filename, f
Set the file from which to read the initial grid state. In the file,
each non-whitespace character is considered an alive cell, and newline
is used to delimit the end of each row.
If this option is not specified, the initial grid is generated
randomly.
@item rate, r
Set the video rate, that is the number of frames generated per second.
Default is 25.
@item random_fill_ratio, ratio
Set the random fill ratio for the initial random grid. It is a
floating point number value ranging from 0 to 1, defaults to 1/PHI.
It is ignored when a file is specified.
@item random_seed, seed
Set the seed for filling the initial random grid, must be an integer
included between 0 and UINT32_MAX. If not specified, or if explicitly
set to -1, the filter will try to use a good random seed on a best
effort basis.
@item rule
Set the life rule.
A rule can be specified with a code of the kind "S@var{NS}/B@var{NB}",
where @var{NS} and @var{NB} are sequences of numbers in the range 0-8,
@var{NS} specifies the number of alive neighbor cells which make a
live cell stay alive, and @var{NB} the number of alive neighbor cells
which make a dead cell to become alive (i.e. to "born").
"s" and "b" can be used in place of "S" and "B", respectively.
Alternatively a rule can be specified by an 18-bits integer. The 9
high order bits are used to encode the next cell state if it is alive
for each number of neighbor alive cells, the low order bits specify
the rule for "borning" new cells. Higher order bits encode for an
higher number of neighbor cells.
For example the number 6153 = @code{(12<<9)+9} specifies a stay alive
rule of 12 and a born rule of 9, which corresponds to "S23/B03".
Default value is "S23/B3", which is the original Conway's game of life
rule, and will keep a cell alive if it has 2 or 3 neighbor alive
cells, and will born a new cell if there are three alive cells around
a dead cell.
@item size, s
Set the size of the output video.
If @option{filename} is specified, the size is set by default to the
same size of the input file. If @option{size} is set, it must contain
the size specified in the input file, and the initial grid defined in
that file is centered in the larger resulting area.
If a filename is not specified, the size value defaults to "320x240"
(used for a randomly generated initial grid).
@item stitch
If set to 1, stitch the left and right grid edges together, and the
top and bottom edges also. Defaults to 1.
@end table
@subsection Examples
@itemize
@item
Read a grid from @file{pattern}, and center it on a grid of size
300x300 pixels:
@example
life=f=pattern:s=300x300
@end example
@item
Generate a random grid of size 200x200, with a fill ratio of 2/3:
@example
life=ratio=2/3:s=200x200
@end example
@item
Specify a custom rule for evolving a randomly generated grid:
@example
life=rule=S14/B34
@end example
@end itemize
@section nullsrc, rgbtestsrc, testsrc
The @code{nullsrc} source returns unprocessed video frames. It is
...
...
libavfilter/Makefile
View file @
2f8b6e90
...
...
@@ -86,6 +86,7 @@ OBJS-$(CONFIG_YADIF_FILTER) += vf_yadif.o
OBJS-$(CONFIG_BUFFER_FILTER)
+=
vsrc_buffer.o
OBJS-$(CONFIG_COLOR_FILTER)
+=
vsrc_color.o
OBJS-$(CONFIG_FREI0R_SRC_FILTER)
+=
vf_frei0r.o
OBJS-$(CONFIG_LIFE_FILTER)
+=
vsrc_life.o
OBJS-$(CONFIG_MANDELBROT_FILTER)
+=
vsrc_mandelbrot.o
OBJS-$(CONFIG_MOVIE_FILTER)
+=
src_movie.o
OBJS-$(CONFIG_MPTESTSRC_FILTER)
+=
vsrc_mptestsrc.o
...
...
libavfilter/allfilters.c
View file @
2f8b6e90
...
...
@@ -97,6 +97,7 @@ void avfilter_register_all(void)
REGISTER_FILTER
(
BUFFER
,
buffer
,
vsrc
);
REGISTER_FILTER
(
COLOR
,
color
,
vsrc
);
REGISTER_FILTER
(
FREI0R
,
frei0r_src
,
vsrc
);
REGISTER_FILTER
(
LIFE
,
life
,
vsrc
);
REGISTER_FILTER
(
MANDELBROT
,
mandelbrot
,
vsrc
);
REGISTER_FILTER
(
MOVIE
,
movie
,
vsrc
);
REGISTER_FILTER
(
MPTESTSRC
,
mptestsrc
,
vsrc
);
...
...
libavfilter/avfilter.h
View file @
2f8b6e90
...
...
@@ -29,8 +29,8 @@
#include "libavutil/rational.h"
#define LIBAVFILTER_VERSION_MAJOR 2
#define LIBAVFILTER_VERSION_MINOR 5
0
#define LIBAVFILTER_VERSION_MICRO
1
#define LIBAVFILTER_VERSION_MINOR 5
1
#define LIBAVFILTER_VERSION_MICRO
0
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
LIBAVFILTER_VERSION_MINOR, \
...
...
libavfilter/vsrc_life.c
0 → 100644
View file @
2f8b6e90
/*
* 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, based on John Conways' Life Game
*/
/* #define DEBUG */
#include "libavutil/file.h"
#include "libavutil/lfg.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/random_seed.h"
#include "avfilter.h"
typedef
struct
{
const
AVClass
*
class
;
int
w
,
h
;
char
*
filename
;
char
*
rule_str
;
uint8_t
*
file_buf
;
size_t
file_bufsize
;
char
*
buf
[
2
];
uint8_t
buf_idx
;
uint16_t
stay_rule
;
///< encode the behavior for filled cells
uint16_t
born_rule
;
///< encode the behavior for empty cells
uint64_t
pts
;
AVRational
time_base
;
char
*
size
;
///< video frame size
char
*
rate
;
///< video frame rate
double
random_fill_ratio
;
uint32_t
random_seed
;
int
stitch
;
AVLFG
lfg
;
}
LifeContext
;
#define OFFSET(x) offsetof(LifeContext, x)
static
const
AVOption
life_options
[]
=
{
{
"filename"
,
"set source file"
,
OFFSET
(
filename
),
AV_OPT_TYPE_STRING
,
{.
str
=
NULL
},
0
,
0
},
{
"f"
,
"set source file"
,
OFFSET
(
filename
),
AV_OPT_TYPE_STRING
,
{.
str
=
NULL
},
0
,
0
},
{
"size"
,
"set video size"
,
OFFSET
(
size
),
AV_OPT_TYPE_STRING
,
{.
str
=
NULL
},
0
,
0
},
{
"s"
,
"set video size"
,
OFFSET
(
size
),
AV_OPT_TYPE_STRING
,
{.
str
=
NULL
},
0
,
0
},
{
"rate"
,
"set video rate"
,
OFFSET
(
rate
),
AV_OPT_TYPE_STRING
,
{.
str
=
"25"
},
0
,
0
},
{
"r"
,
"set video rate"
,
OFFSET
(
rate
),
AV_OPT_TYPE_STRING
,
{.
str
=
"25"
},
0
,
0
},
{
"rule"
,
"set rule"
,
OFFSET
(
rule_str
),
AV_OPT_TYPE_STRING
,
{.
str
=
"B3/S23"
},
CHAR_MIN
,
CHAR_MAX
},
{
"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
},
{
"ratio"
,
"set fill ratio for filling initial grid randomly"
,
OFFSET
(
random_fill_ratio
),
AV_OPT_TYPE_DOUBLE
,
{.
dbl
=
1
/
M_PHI
},
0
,
1
},
{
"random_seed"
,
"set the seed for filling the initial grid randomly"
,
OFFSET
(
random_seed
),
AV_OPT_TYPE_INT
,
{.
dbl
=-
1
},
-
1
,
UINT32_MAX
},
{
"seed"
,
"set the seed for filling the initial grid randomly"
,
OFFSET
(
random_seed
),
AV_OPT_TYPE_INT
,
{.
dbl
=-
1
},
-
1
,
UINT32_MAX
},
{
"stitch"
,
"stitch boundaries"
,
OFFSET
(
stitch
),
AV_OPT_TYPE_INT
,
{.
dbl
=
1
},
0
,
1
},
{
NULL
},
};
static
const
char
*
life_get_name
(
void
*
ctx
)
{
return
"life"
;
}
static
const
AVClass
life_class
=
{
"LifeContext"
,
life_get_name
,
life_options
};
static
int
parse_rule
(
uint16_t
*
born_rule
,
uint16_t
*
stay_rule
,
const
char
*
rule_str
,
void
*
log_ctx
)
{
char
*
tail
;
const
char
*
p
=
rule_str
;
*
born_rule
=
0
;
*
stay_rule
=
0
;
if
(
strchr
(
"bBsS"
,
*
p
))
{
/* parse rule as a Born / Stay Alive code, see
* http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life */
do
{
uint16_t
*
rule
=
(
*
p
==
'b'
||
*
p
==
'B'
)
?
born_rule
:
stay_rule
;
p
++
;
while
(
*
p
>=
'0'
&&
*
p
<=
'8'
)
{
*
rule
+=
1
<<
(
*
p
-
'0'
);
p
++
;
}
if
(
*
p
!=
'/'
)
break
;
p
++
;
}
while
(
strchr
(
"bBsS"
,
*
p
));
if
(
*
p
)
goto
error
;
}
else
{
/* parse the rule as a number, expressed in the form STAY|(BORN<<9),
* where STAY and DEATH encode the corresponding 9-bits rule */
long
int
rule
=
strtol
(
rule_str
,
&
tail
,
10
);
if
(
*
tail
)
goto
error
;
*
born_rule
=
((
1
<<
9
)
-
1
)
&
rule
;
*
stay_rule
=
rule
>>
9
;
}
return
0
;
error:
av_log
(
log_ctx
,
AV_LOG_ERROR
,
"Invalid rule code '%s' provided
\n
"
,
rule_str
);
return
AVERROR
(
EINVAL
);
}
#ifdef DEBUG
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
);
}
#endif
static
int
init_pattern_from_file
(
AVFilterContext
*
ctx
)
{
LifeContext
*
life
=
ctx
->
priv
;
char
*
p
;
int
ret
,
i
,
i0
,
j
,
h
=
0
,
w
,
max_w
=
0
;
if
((
ret
=
av_file_map
(
life
->
filename
,
&
life
->
file_buf
,
&
life
->
file_bufsize
,
0
,
ctx
))
<
0
)
return
ret
;
/* prescan file to get the number of lines and the maximum width */
w
=
0
;
for
(
i
=
0
;
i
<
life
->
file_bufsize
;
i
++
)
{
if
(
life
->
file_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
(
life
->
size
)
{
if
(
max_w
>
life
->
w
||
h
>
life
->
h
)
{
av_log
(
ctx
,
AV_LOG_ERROR
,
"The specified size is %dx%d which cannot contain the provided file size of %dx%d
\n
"
,
life
->
w
,
life
->
h
,
max_w
,
h
);
return
AVERROR
(
EINVAL
);
}
}
else
{
/* size was not specified, set it to size of the grid */
life
->
w
=
max_w
;
life
->
h
=
h
;
}
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
=
life
->
file_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
;
AVRational
frame_rate
;
int
ret
;
life
->
class
=
&
life_class
;
av_opt_set_defaults
(
life
);
if
((
ret
=
av_set_options_string
(
life
,
args
,
"="
,
":"
))
<
0
)
{
av_log
(
ctx
,
AV_LOG_ERROR
,
"Error parsing options string: '%s'
\n
"
,
args
);
return
ret
;
}
if
((
ret
=
av_parse_video_rate
(
&
frame_rate
,
life
->
rate
))
<
0
)
{
av_log
(
ctx
,
AV_LOG_ERROR
,
"Invalid frame rate: %s
\n
"
,
life
->
rate
);
return
AVERROR
(
EINVAL
);
}
if
(
!
life
->
size
&&
!
life
->
filename
)
av_opt_set
(
life
,
"size"
,
"320x240"
,
0
);
if
(
life
->
size
&&
(
ret
=
av_parse_video_size
(
&
life
->
w
,
&
life
->
h
,
life
->
size
))
<
0
)
{
av_log
(
ctx
,
AV_LOG_ERROR
,
"Invalid frame size: %s
\n
"
,
life
->
size
);
return
ret
;
}
if
((
ret
=
parse_rule
(
&
life
->
born_rule
,
&
life
->
stay_rule
,
life
->
rule_str
,
ctx
))
<
0
)
return
ret
;
life
->
time_base
.
num
=
frame_rate
.
den
;
life
->
time_base
.
den
=
frame_rate
.
num
;
if
(
!
life
->
filename
)
{
/* fill the grid randomly */
int
i
;
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
);
}
if
(
life
->
random_seed
==
-
1
)
life
->
random_seed
=
av_get_random_seed
();
av_lfg_init
(
&
life
->
lfg
,
life
->
random_seed
);
for
(
i
=
0
;
i
<
life
->
w
*
life
->
h
;
i
++
)
{
double
r
=
(
double
)
av_lfg_get
(
&
life
->
lfg
)
/
UINT32_MAX
;
if
(
r
<=
life
->
random_fill_ratio
)
life
->
buf
[
0
][
i
]
=
1
;
}
life
->
buf_idx
=
0
;
}
else
{
if
((
ret
=
init_pattern_from_file
(
ctx
))
<
0
)
return
ret
;
}
av_log
(
ctx
,
AV_LOG_INFO
,
"s:%dx%d r:%d/%d rule:%s stay_rule:%d born_rule:%d stitch:%d
\n
"
,
life
->
w
,
life
->
h
,
frame_rate
.
num
,
frame_rate
.
den
,
life
->
rule_str
,
life
->
stay_rule
,
life
->
born_rule
,
life
->
stitch
);
return
0
;
}
static
av_cold
void
uninit
(
AVFilterContext
*
ctx
)
{
LifeContext
*
life
=
ctx
->
priv
;
av_file_unmap
(
life
->
file_buf
,
life
->
file_bufsize
);
av_freep
(
&
life
->
rule_str
);
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
;
outlink
->
time_base
=
life
->
time_base
;
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
];
enum
{
NW
,
N
,
NE
,
W
,
E
,
SW
,
S
,
SE
};
/* evolve the grid */
for
(
i
=
0
;
i
<
life
->
h
;
i
++
)
{
for
(
j
=
0
;
j
<
life
->
w
;
j
++
)
{
int
pos
[
8
][
2
],
n
;
if
(
life
->
stitch
)
{
pos
[
NW
][
0
]
=
(
i
-
1
)
<
0
?
life
->
h
-
1
:
i
-
1
;
pos
[
NW
][
1
]
=
(
j
-
1
)
<
0
?
life
->
w
-
1
:
j
-
1
;
pos
[
N
][
0
]
=
(
i
-
1
)
<
0
?
life
->
h
-
1
:
i
-
1
;
pos
[
N
][
1
]
=
j
;
pos
[
NE
][
0
]
=
(
i
-
1
)
<
0
?
life
->
h
-
1
:
i
-
1
;
pos
[
NE
][
1
]
=
(
j
+
1
)
==
life
->
w
?
0
:
j
+
1
;
pos
[
W
][
0
]
=
i
;
pos
[
W
][
1
]
=
(
j
-
1
)
<
0
?
life
->
w
-
1
:
j
-
1
;
pos
[
E
][
0
]
=
i
;
pos
[
E
][
1
]
=
(
j
+
1
)
==
life
->
w
?
0
:
j
+
1
;
pos
[
SW
][
0
]
=
(
i
+
1
)
==
life
->
h
?
0
:
i
+
1
;
pos
[
SW
][
1
]
=
(
j
-
1
)
<
0
?
life
->
w
-
1
:
j
-
1
;
pos
[
S
][
0
]
=
(
i
+
1
)
==
life
->
h
?
0
:
i
+
1
;
pos
[
S
][
1
]
=
j
;
pos
[
SE
][
0
]
=
(
i
+
1
)
==
life
->
h
?
0
:
i
+
1
;
pos
[
SE
][
1
]
=
(
j
+
1
)
==
life
->
w
?
0
:
j
+
1
;
}
else
{
pos
[
NW
][
0
]
=
(
i
-
1
)
<
0
?
-
1
:
i
-
1
;
pos
[
NW
][
1
]
=
(
j
-
1
)
<
0
?
-
1
:
j
-
1
;
pos
[
N
][
0
]
=
(
i
-
1
)
<
0
?
-
1
:
i
-
1
;
pos
[
N
][
1
]
=
j
;
pos
[
NE
][
0
]
=
(
i
-
1
)
<
0
?
-
1
:
i
-
1
;
pos
[
NE
][
1
]
=
(
j
+
1
)
==
life
->
w
?
-
1
:
j
+
1
;
pos
[
W
][
0
]
=
i
;
pos
[
W
][
1
]
=
(
j
-
1
)
<
0
?
-
1
:
j
-
1
;
pos
[
E
][
0
]
=
i
;
pos
[
E
][
1
]
=
(
j
+
1
)
==
life
->
w
?
-
1
:
j
+
1
;
pos
[
SW
][
0
]
=
(
i
+
1
)
==
life
->
h
?
-
1
:
i
+
1
;
pos
[
SW
][
1
]
=
(
j
-
1
)
<
0
?
-
1
:
j
-
1
;
pos
[
S
][
0
]
=
(
i
+
1
)
==
life
->
h
?
-
1
:
i
+
1
;
pos
[
S
][
1
]
=
j
;
pos
[
SE
][
0
]
=
(
i
+
1
)
==
life
->
h
?
-
1
:
i
+
1
;
pos
[
SE
][
1
]
=
(
j
+
1
)
==
life
->
w
?
-
1
:
j
+
1
;
}
/* compute the number of live neighbor cells */
n
=
(
pos
[
NW
][
0
]
==
-
1
||
pos
[
NW
][
1
]
==
-
1
?
0
:
oldbuf
[
pos
[
NW
][
0
]
*
life
->
w
+
pos
[
NW
][
1
]])
+
(
pos
[
N
][
0
]
==
-
1
||
pos
[
N
][
1
]
==
-
1
?
0
:
oldbuf
[
pos
[
N
][
0
]
*
life
->
w
+
pos
[
N
][
1
]])
+
(
pos
[
NE
][
0
]
==
-
1
||
pos
[
NE
][
1
]
==
-
1
?
0
:
oldbuf
[
pos
[
NE
][
0
]
*
life
->
w
+
pos
[
NE
][
1
]])
+
(
pos
[
W
][
0
]
==
-
1
||
pos
[
W
][
1
]
==
-
1
?
0
:
oldbuf
[
pos
[
W
][
0
]
*
life
->
w
+
pos
[
W
][
1
]])
+
(
pos
[
E
][
0
]
==
-
1
||
pos
[
E
][
1
]
==
-
1
?
0
:
oldbuf
[
pos
[
E
][
0
]
*
life
->
w
+
pos
[
E
][
1
]])
+
(
pos
[
SW
][
0
]
==
-
1
||
pos
[
SW
][
1
]
==
-
1
?
0
:
oldbuf
[
pos
[
SW
][
0
]
*
life
->
w
+
pos
[
SW
][
1
]])
+
(
pos
[
S
][
0
]
==
-
1
||
pos
[
S
][
1
]
==
-
1
?
0
:
oldbuf
[
pos
[
S
][
0
]
*
life
->
w
+
pos
[
S
][
1
]])
+
(
pos
[
SE
][
0
]
==
-
1
||
pos
[
SE
][
1
]
==
-
1
?
0
:
oldbuf
[
pos
[
SE
][
0
]
*
life
->
w
+
pos
[
SE
][
1
]]);
v
=
!!
(
1
<<
n
&
(
oldbuf
[
i
*
life
->
w
+
j
]
?
life
->
stay_rule
:
life
->
born_rule
));
av_dlog
(
ctx
,
"i:%d j:%d live_neighbors:%d cell:%d -> cell:%d
\n
"
,
i
,
j
,
n
,
oldbuf
[
i
*
life
->
w
+
j
],
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
->
video
->
sample_aspect_ratio
=
(
AVRational
)
{
1
,
1
};
picref
->
pts
=
life
->
pts
++
;
picref
->
pos
=
-
1
;
fill_picture
(
outlink
->
src
,
picref
);
evolve
(
outlink
->
src
);
#ifdef DEBUG
show_life_grid
(
outlink
->
src
);
#endif
avfilter_start_frame
(
outlink
,
avfilter_ref_buffer
(
picref
,
~
0
));
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_pixel_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
=
(
const
AVFilterPad
[])
{
{
.
name
=
NULL
}
},
.
outputs
=
(
const
AVFilterPad
[])
{
{
.
name
=
"default"
,
.
type
=
AVMEDIA_TYPE_VIDEO
,
.
request_frame
=
request_frame
,
.
config_props
=
config_props
},
{
.
name
=
NULL
}
},
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment