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
786a2daa
Commit
786a2daa
authored
Dec 17, 2019
by
Paul B Mahol
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
avfilter/vf_readeia608: rewrite processing, make extracting more robust
Lots of options are now obsolete.
parent
dbb05176
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
229 additions
and
117 deletions
+229
-117
filters.texi
doc/filters.texi
+2
-28
vf_readeia608.c
libavfilter/vf_readeia608.c
+227
-89
No files found.
doc/filters.texi
View file @
786a2daa
...
...
@@ -15348,42 +15348,16 @@ Set the line to start scanning for EIA-608 data. Default is @code{0}.
@item scan_max
Set the line to end scanning for EIA-608 data. Default is @code{29}.
@item mac
Set minimal acceptable amplitude change for sync codes detection.
Default is @code{0.2}. Allowed range is @code{[0.001 - 1]}.
@item spw
Set the ratio of width reserved for sync code detection.
Default is @code{0.27}. Allowed range is @code{[0.01 - 0.7]}.
@item mhd
Set the max peaks height difference for sync code detection.
Default is @code{0.1}. Allowed range is @code{[0.0 - 0.5]}.
@item mpd
Set max peaks period difference for sync code detection.
Default is @code{0.1}. Allowed range is @code{[0.0 - 0.5]}.
@item msd
Set the first two max start code bits differences.
Default is @code{0.02}. Allowed range is @code{[0.0 - 0.5]}.
@item bhd
Set the minimum ratio of bits height compared to 3rd start code bit.
Default is @code{0.75}. Allowed range is @code{[0.01 - 1]}.
@item th_w
Set the white color threshold. Default is @code{0.35}. Allowed range is @code{[0.1 - 1]}.
@item th_b
Set the black color threshold. Default is @code{0.15}. Allowed range is @code{[0.0 - 0.5]}.
Default is @code{0.27}. Allowed range is @code{[0.1 - 0.7]}.
@item chp
Enable checking the parity bit. In the event of a parity error, the filter will output
@code{0x00} for that character. Default is false.
@item lp
Lowpass lines prior to further processing. Default is
dis
abled.
Lowpass lines prior to further processing. Default is
en
abled.
@end table
@subsection Examples
...
...
libavfilter/vf_readeia608.c
View file @
786a2daa
...
...
@@ -36,41 +36,45 @@
#include "internal.h"
#include "video.h"
#define FALL 0
#define RISE 1
#define LAG 25
#define SYNC_MIN 12.f
#define SYNC_MAX 15.f
typedef
struct
CodeItem
{
uint8_t
bit
;
int
size
;
}
CodeItem
;
typedef
struct
ReadEIA608Context
{
const
AVClass
*
class
;
int
start
,
end
;
int
min_range
;
int
max_peak_diff
;
int
max_period_diff
;
int
max_start_diff
;
int
nb_found
;
int
white
;
int
black
;
float
mpd
,
mhd
,
msd
,
mac
,
spw
,
bhd
,
wth
,
bth
;
float
spw
;
int
chp
;
int
lp
;
uint64_t
histogram
[
256
];
uint8_t
*
temp
;
uint8_t
*
signal
;
CodeItem
*
code
;
float
*
unfiltered
;
float
*
filtered
;
float
*
avg_filter
;
float
*
std_filter
;
}
ReadEIA608Context
;
#define OFFSET(x) offsetof(ReadEIA608Context, x)
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static
const
AVOption
readeia608_options
[]
=
{
{
"scan_min"
,
"set from which line to scan for codes"
,
OFFSET
(
start
),
AV_OPT_TYPE_INT
,
{.
i64
=
0
},
0
,
INT_MAX
,
FLAGS
},
{
"scan_max"
,
"set to which line to scan for codes"
,
OFFSET
(
end
),
AV_OPT_TYPE_INT
,
{.
i64
=
29
},
0
,
INT_MAX
,
FLAGS
},
{
"mac"
,
"set minimal acceptable amplitude change for sync codes detection"
,
OFFSET
(
mac
),
AV_OPT_TYPE_FLOAT
,
{.
dbl
=
.
2
},
0
.
001
,
1
,
FLAGS
},
{
"spw"
,
"set ratio of width reserved for sync code detection"
,
OFFSET
(
spw
),
AV_OPT_TYPE_FLOAT
,
{.
dbl
=
.
27
},
0
.
1
,
0
.
7
,
FLAGS
},
{
"mhd"
,
"set max peaks height difference for sync code detection"
,
OFFSET
(
mhd
),
AV_OPT_TYPE_FLOAT
,
{.
dbl
=
.
1
},
0
,
0
.
5
,
FLAGS
},
{
"mpd"
,
"set max peaks period difference for sync code detection"
,
OFFSET
(
mpd
),
AV_OPT_TYPE_FLOAT
,
{.
dbl
=
.
1
},
0
,
0
.
5
,
FLAGS
},
{
"msd"
,
"set first two max start code bits differences"
,
OFFSET
(
msd
),
AV_OPT_TYPE_FLOAT
,
{.
dbl
=
.
02
},
0
,
0
.
5
,
FLAGS
},
{
"bhd"
,
"set min ratio of bits height compared to 3rd start code bit"
,
OFFSET
(
bhd
),
AV_OPT_TYPE_FLOAT
,
{.
dbl
=
.
75
},
0
.
01
,
1
,
FLAGS
},
{
"th_w"
,
"set white color threshold"
,
OFFSET
(
wth
),
AV_OPT_TYPE_FLOAT
,
{.
dbl
=
.
35
},
0
.
1
,
1
,
FLAGS
},
{
"th_b"
,
"set black color threshold"
,
OFFSET
(
bth
),
AV_OPT_TYPE_FLOAT
,
{.
dbl
=
.
15
},
0
,
0
.
5
,
FLAGS
},
{
"chp"
,
"check and apply parity bit"
,
OFFSET
(
chp
),
AV_OPT_TYPE_BOOL
,
{.
i64
=
0
},
0
,
1
,
FLAGS
},
{
"lp"
,
"lowpass line prior to processing"
,
OFFSET
(
lp
),
AV_OPT_TYPE_BOOL
,
{.
i64
=
0
},
0
,
1
,
FLAGS
},
{
"scan_min"
,
"set from which line to scan for codes"
,
OFFSET
(
start
),
AV_OPT_TYPE_INT
,
{.
i64
=
0
},
0
,
INT_MAX
,
FLAGS
},
{
"scan_max"
,
"set to which line to scan for codes"
,
OFFSET
(
end
),
AV_OPT_TYPE_INT
,
{.
i64
=
29
},
0
,
INT_MAX
,
FLAGS
},
{
"spw"
,
"set ratio of width reserved for sync code detection"
,
OFFSET
(
spw
),
AV_OPT_TYPE_FLOAT
,
{.
dbl
=
.
27
},
0
.
1
,
0
.
7
,
FLAGS
},
{
"chp"
,
"check and apply parity bit"
,
OFFSET
(
chp
),
AV_OPT_TYPE_BOOL
,
{.
i64
=
0
},
0
,
1
,
FLAGS
},
{
"lp"
,
"lowpass line prior to processing"
,
OFFSET
(
lp
),
AV_OPT_TYPE_BOOL
,
{.
i64
=
1
},
0
,
1
,
FLAGS
},
{
NULL
}
};
...
...
@@ -96,10 +100,9 @@ static int query_formats(AVFilterContext *ctx)
static
int
config_input
(
AVFilterLink
*
inlink
)
{
const
AVPixFmtDescriptor
*
desc
=
av_pix_fmt_desc_get
(
inlink
->
format
);
AVFilterContext
*
ctx
=
inlink
->
dst
;
ReadEIA608Context
*
s
=
ctx
->
priv
;
int
depth
=
desc
->
comp
[
0
].
depth
;
int
size
=
inlink
->
w
+
LAG
;
if
(
s
->
end
>=
inlink
->
h
)
{
av_log
(
ctx
,
AV_LOG_WARNING
,
"Last line to scan too large, clipping.
\n
"
);
...
...
@@ -111,34 +114,177 @@ static int config_input(AVFilterLink *inlink)
return
AVERROR
(
EINVAL
);
}
s
->
min_range
=
s
->
mac
*
((
1
<<
depth
)
-
1
);
s
->
max_peak_diff
=
s
->
mhd
*
((
1
<<
depth
)
-
1
);
s
->
max_period_diff
=
s
->
mpd
*
((
1
<<
depth
)
-
1
);
s
->
max_start_diff
=
s
->
msd
*
((
1
<<
depth
)
-
1
);
s
->
white
=
s
->
wth
*
((
1
<<
depth
)
-
1
);
s
->
black
=
s
->
bth
*
((
1
<<
depth
)
-
1
);
s
->
temp
=
av_calloc
(
inlink
->
w
,
sizeof
(
*
s
->
temp
));
s
->
unfiltered
=
av_calloc
(
size
,
sizeof
(
*
s
->
unfiltered
)
);
s
->
filtered
=
av_calloc
(
size
,
sizeof
(
*
s
->
filtered
)
);
s
->
avg_filter
=
av_calloc
(
size
,
sizeof
(
*
s
->
avg_filter
)
);
s
->
std_filter
=
av_calloc
(
size
,
sizeof
(
*
s
->
std_filter
)
);
s
->
signal
=
av_calloc
(
size
,
sizeof
(
*
s
->
signal
)
);
s
->
code
=
av_calloc
(
size
,
sizeof
(
*
s
->
code
)
);
s
->
temp
=
av_calloc
(
size
,
sizeof
(
*
s
->
temp
));
if
(
!
s
->
temp
)
return
AVERROR
(
ENOMEM
);
return
0
;
}
static
void
build_histogram
(
ReadEIA608Context
*
s
,
const
uint8_t
*
src
,
int
len
)
{
memset
(
s
->
histogram
,
0
,
sizeof
(
s
->
histogram
));
for
(
int
i
=
0
;
i
<
len
;
i
++
)
s
->
histogram
[
src
[
i
]]
++
;
}
static
void
find_black_and_white
(
ReadEIA608Context
*
s
)
{
int
start
=
0
,
end
=
0
,
middle
;
int
black
=
0
,
white
=
0
;
int
cnt
;
for
(
int
i
=
0
;
i
<
256
;
i
++
)
{
if
(
s
->
histogram
[
i
])
{
start
=
i
;
break
;
}
}
for
(
int
i
=
255
;
i
>=
0
;
i
--
)
{
if
(
s
->
histogram
[
i
])
{
end
=
i
;
break
;
}
}
middle
=
start
+
(
end
-
start
)
/
2
;
cnt
=
0
;
for
(
int
i
=
start
;
i
<=
middle
;
i
++
)
{
if
(
s
->
histogram
[
i
]
>
cnt
)
{
cnt
=
s
->
histogram
[
i
];
black
=
i
;
}
}
cnt
=
0
;
for
(
int
i
=
end
;
i
>=
middle
;
i
--
)
{
if
(
s
->
histogram
[
i
]
>
cnt
)
{
cnt
=
s
->
histogram
[
i
];
white
=
i
;
}
}
s
->
black
=
black
;
s
->
white
=
white
;
}
static
float
meanf
(
float
*
data
,
int
len
)
{
float
sum
=
0
.
0
,
mean
=
0
.
0
;
for
(
int
i
=
0
;
i
<
len
;
i
++
)
sum
+=
data
[
i
];
mean
=
sum
/
len
;
return
mean
;
}
static
float
stddevf
(
float
*
data
,
int
len
)
{
float
m
=
meanf
(
data
,
len
);
float
standard_deviation
=
0
.
f
;
for
(
int
i
=
0
;
i
<
len
;
i
++
)
standard_deviation
+=
(
data
[
i
]
-
m
)
*
(
data
[
i
]
-
m
);
return
sqrtf
(
standard_deviation
/
(
len
-
1
));
}
static
void
thresholding
(
ReadEIA608Context
*
s
,
const
uint8_t
*
y
,
uint8_t
*
signal
,
float
*
unfiltered
,
float
*
filtered
,
float
*
avg_filter
,
float
*
std_filter
,
int
lag
,
float
threshold
,
float
influence
,
int
len
)
{
for
(
int
i
=
lag
;
i
<
len
+
lag
;
i
++
)
{
unfiltered
[
i
]
=
y
[
i
-
lag
]
/
255
.
f
;
filtered
[
i
]
=
unfiltered
[
i
];
}
for
(
int
i
=
0
;
i
<
lag
;
i
++
)
{
unfiltered
[
i
]
=
meanf
(
unfiltered
,
len
*
s
->
spw
);
filtered
[
i
]
=
unfiltered
[
i
];
}
memset
(
signal
,
0
,
len
);
avg_filter
[
lag
-
1
]
=
meanf
(
unfiltered
,
lag
);
std_filter
[
lag
-
1
]
=
stddevf
(
unfiltered
,
lag
);
for
(
int
i
=
lag
;
i
<
len
+
lag
;
i
++
)
{
if
(
fabsf
(
unfiltered
[
i
]
-
avg_filter
[
i
-
1
])
>
threshold
*
std_filter
[
i
-
1
])
{
if
(
unfiltered
[
i
]
>
avg_filter
[
i
-
1
])
{
signal
[
i
-
lag
]
=
255
;
}
else
{
signal
[
i
-
lag
]
=
0
;
}
filtered
[
i
]
=
influence
*
unfiltered
[
i
]
+
(
1
.
f
-
influence
)
*
filtered
[
i
-
1
];
}
else
{
int
distance_from_black
,
distance_from_white
;
distance_from_black
=
FFABS
(
y
[
i
-
lag
]
-
s
->
black
);
distance_from_white
=
FFABS
(
y
[
i
-
lag
]
-
s
->
white
);
signal
[
i
-
lag
]
=
distance_from_black
<=
distance_from_white
?
0
:
255
;
}
avg_filter
[
i
]
=
meanf
(
filtered
+
i
-
lag
,
lag
);
std_filter
[
i
]
=
stddevf
(
filtered
+
i
-
lag
,
lag
);
}
}
static
int
periods
(
const
uint8_t
*
signal
,
CodeItem
*
code
,
int
len
)
{
int
hold
=
signal
[
0
],
cnt
=
0
;
int
last
=
0
;
memset
(
code
,
0
,
len
*
sizeof
(
*
code
));
for
(
int
i
=
1
;
i
<
len
;
i
++
)
{
if
(
signal
[
i
]
!=
hold
)
{
code
[
cnt
].
size
=
i
-
last
;
code
[
cnt
].
bit
=
hold
;
hold
=
signal
[
i
];
last
=
i
;
cnt
++
;
}
}
code
[
cnt
].
size
=
len
-
last
;
code
[
cnt
].
bit
=
hold
;
return
cnt
+
1
;
}
static
void
dump_code
(
AVFilterContext
*
ctx
,
int
len
,
int
item
)
{
ReadEIA608Context
*
s
=
ctx
->
priv
;
av_log
(
ctx
,
AV_LOG_DEBUG
,
"%d:"
,
item
);
for
(
int
i
=
0
;
i
<
len
;
i
++
)
{
av_log
(
ctx
,
AV_LOG_DEBUG
,
" %03d"
,
s
->
code
[
i
].
size
);
}
av_log
(
ctx
,
AV_LOG_DEBUG
,
"
\n
"
);
}
static
void
extract_line
(
AVFilterContext
*
ctx
,
AVFilterLink
*
inlink
,
AVFrame
*
in
,
int
line
)
{
ReadEIA608Context
*
s
=
ctx
->
priv
;
int
max
=
0
,
min
=
INT_MAX
;
int
i
,
ch
,
range
=
0
;
int
i
,
j
,
ch
,
len
;
const
uint8_t
*
src
;
uint16_t
clock
[
8
][
2
]
=
{
{
0
}
};
const
int
sync_width
=
s
->
spw
*
in
->
width
;
int
last
=
0
,
peaks
=
0
,
max_peak_diff
=
0
,
dir
=
RISE
;
const
int
width_per_bit
=
(
in
->
width
-
sync_width
)
/
19
;
uint8_t
byte
[
2
]
=
{
0
};
int
s1
,
s2
,
s3
,
parity
;
uint8_t
codes
[
19
]
=
{
0
};
float
bit_size
=
0
.
f
;
int
parity
;
src
=
&
in
->
data
[
0
][
line
*
in
->
linesize
[
0
]];
if
(
s
->
lp
)
{
uint8_t
*
dst
=
s
->
temp
;
int
w
=
inlink
->
w
-
1
;
...
...
@@ -157,78 +303,60 @@ static void extract_line(AVFilterContext *ctx, AVFilterLink *inlink, AVFrame *in
src
=
s
->
temp
;
}
for
(
i
=
0
;
i
<
sync_width
;
i
++
)
{
max
=
FFMAX
(
max
,
src
[
i
]);
min
=
FFMIN
(
min
,
src
[
i
]);
}
range
=
max
-
min
;
if
(
range
<
s
->
min_range
)
build_histogram
(
s
,
src
,
inlink
->
w
);
find_black_and_white
(
s
);
if
(
s
->
white
-
s
->
black
<
5
)
return
;
for
(
i
=
0
;
i
<
sync_width
;
i
++
)
{
int
Y
=
src
[
i
];
if
(
dir
==
RISE
)
{
if
(
Y
<
last
)
{
dir
=
FALL
;
if
(
last
>=
s
->
white
)
{
clock
[
peaks
][
0
]
=
last
;
clock
[
peaks
][
1
]
=
i
;
peaks
++
;
if
(
peaks
>
7
)
break
;
}
}
}
else
if
(
dir
==
FALL
)
{
if
(
Y
>
last
&&
last
<=
s
->
black
)
{
dir
=
RISE
;
}
}
last
=
Y
;
}
if
(
peaks
!=
7
)
{
av_log
(
ctx
,
AV_LOG_DEBUG
,
"peaks: %d != 7
\n
"
,
peaks
);
thresholding
(
s
,
src
,
s
->
signal
,
s
->
unfiltered
,
s
->
filtered
,
s
->
avg_filter
,
s
->
std_filter
,
LAG
,
1
,
0
,
inlink
->
w
);
//memcpy(&in->data[0][line * in->linesize[0]], s->signal, inlink->w);
len
=
periods
(
s
->
signal
,
s
->
code
,
inlink
->
w
);
dump_code
(
ctx
,
len
,
line
);
if
(
len
<
15
||
s
->
code
[
14
].
bit
!=
0
||
inlink
->
w
/
(
float
)
s
->
code
[
14
].
size
<
SYNC_MIN
||
inlink
->
w
/
(
float
)
s
->
code
[
14
].
size
>
SYNC_MAX
)
{
return
;
}
for
(
i
=
1
;
i
<
7
;
i
++
)
max_peak_diff
=
FFMAX
(
max_peak_diff
,
FFABS
(
clock
[
i
][
0
]
-
clock
[
i
-
1
][
0
]));
if
(
max_peak_diff
>
s
->
max_peak_diff
)
{
av_log
(
ctx
,
AV_LOG_DEBUG
,
"mhd: %d > %d
\n
"
,
max_peak_diff
,
s
->
max_peak_diff
);
return
;
for
(
i
=
14
;
i
<
len
;
i
++
)
{
bit_size
+=
s
->
code
[
i
].
size
;
}
max
=
0
;
min
=
INT_MAX
;
for
(
i
=
1
;
i
<
7
;
i
++
)
{
max
=
FFMAX
(
max
,
FFABS
(
clock
[
i
][
1
]
-
clock
[
i
-
1
][
1
]));
min
=
FFMIN
(
min
,
FFABS
(
clock
[
i
][
1
]
-
clock
[
i
-
1
][
1
]));
bit_size
/=
19
.
f
;
for
(
i
=
1
;
i
<
14
;
i
++
)
{
if
(
s
->
code
[
i
].
size
>
bit_size
*
1
.
5
f
)
{
return
;
}
}
range
=
max
-
min
;
if
(
range
>
s
->
max_period_diff
)
{
av_log
(
ctx
,
AV_LOG_DEBUG
,
"mpd: %d > %d
\n
"
,
range
,
s
->
max_period_diff
);
if
(
s
->
code
[
15
].
size
/
bit_size
<
0
.
45
f
)
{
return
;
}
s1
=
src
[
sync_width
+
width_per_bit
*
0
+
width_per_bit
/
2
];
s2
=
src
[
sync_width
+
width_per_bit
*
1
+
width_per_bit
/
2
];
s3
=
src
[
sync_width
+
width_per_bit
*
2
+
width_per_bit
/
2
];
for
(
j
=
0
,
i
=
14
;
i
<
len
;
i
++
)
{
int
run
,
bit
;
if
(
FFABS
(
s1
-
s2
)
>
s
->
max_start_diff
||
s1
>
s
->
black
||
s2
>
s
->
black
||
s3
<
s
->
white
)
{
av_log
(
ctx
,
AV_LOG_DEBUG
,
"msd: %d > %d
\n
"
,
FFABS
(
s1
-
s2
),
s
->
max_start_diff
);
return
;
run
=
lrintf
(
s
->
code
[
i
].
size
/
bit_size
);
bit
=
s
->
code
[
i
].
bit
;
for
(
int
k
=
0
;
j
<
19
&&
k
<
run
;
k
++
)
{
codes
[
j
++
]
=
bit
;
}
if
(
j
>=
19
)
break
;
}
for
(
ch
=
0
;
ch
<
2
;
ch
++
)
{
for
(
parity
=
0
,
i
=
0
;
i
<
8
;
i
++
)
{
int
b
=
src
[
sync_width
+
width_per_bit
*
(
i
+
3
+
8
*
ch
)
+
width_per_bit
/
2
];
int
b
=
codes
[
3
+
ch
*
8
+
i
];
if
(
b
-
s1
>
(
s3
-
s1
)
*
s
->
bhd
)
{
b
=
1
;
if
(
b
==
255
)
{
parity
++
;
b
=
1
;
}
else
{
b
=
0
;
}
...
...
@@ -245,6 +373,10 @@ static void extract_line(AVFilterContext *ctx, AVFilterLink *inlink, AVFrame *in
{
uint8_t
key
[
128
],
value
[
128
];
//snprintf(key, sizeof(key), "lavfi.readeia608.%d.bits", s->nb_found);
//snprintf(value, sizeof(value), "0b%d%d%d%d%d%d%d%d 0b%d%d%d%d%d%d%d%d", codes[3]==255,codes[4]==255,codes[5]==255,codes[6]==255,codes[7]==255,codes[8]==255,codes[9]==255,codes[10]==255,codes[11]==255,codes[12]==255,codes[13]==255,codes[14]==255,codes[15]==255,codes[16]==255,codes[17]==255,codes[18]==255);
//av_dict_set(&in->metadata, key, value, 0);
snprintf
(
key
,
sizeof
(
key
),
"lavfi.readeia608.%d.cc"
,
s
->
nb_found
);
snprintf
(
value
,
sizeof
(
value
),
"0x%02X%02X"
,
byte
[
0
],
byte
[
1
]);
av_dict_set
(
&
in
->
metadata
,
key
,
value
,
0
);
...
...
@@ -276,6 +408,12 @@ static av_cold void uninit(AVFilterContext *ctx)
ReadEIA608Context
*
s
=
ctx
->
priv
;
av_freep
(
&
s
->
temp
);
av_freep
(
&
s
->
code
);
av_freep
(
&
s
->
signal
);
av_freep
(
&
s
->
unfiltered
);
av_freep
(
&
s
->
filtered
);
av_freep
(
&
s
->
avg_filter
);
av_freep
(
&
s
->
std_filter
);
}
static
const
AVFilterPad
readeia608_inputs
[]
=
{
...
...
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