video: add vf_fingerprint and a skip-logo script
skip-logo.lua is just what I wanted to have. Explanations are on the top
of that file. As usual, all documentation threatens to remove this stuff
all the time, since this stuff is just for me, and unlike a normal user
I can afford the luxuary of hacking the shit directly into the player.
vf_fingerprint is needed to support this script. It needs to scale down
video frames as part of its operation. For that, it uses zimg. zimg is
much faster than libswscale and generates more correct output. (The
filter includes a runtime fallback, but it doesn't even work because
libswscale fucks up and can't do YUV->Gray with range adjustment.)
Note on the algorithm: seems almost too simple, but was suggested to me.
It seems to be pretty effective, although long time experience with
false positives is missing. At first I wanted to use dHash [1][2], which
is also pretty simple and effective, but might actually be worse than
the implemented mechanism. dHash has the advantage that the fingerprint
is smaller. But exact matching is too unreliable, and you'd still need
to determine the number of different bits for fuzzier comparison. So
there wasn't really a reason to use it.
[1] https://pypi.org/project/dhash/
[2] http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
2019-07-15 01:20:40 +00:00
|
|
|
/*
|
|
|
|
* This file is part of mpv.
|
|
|
|
*
|
|
|
|
* mpv 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.
|
|
|
|
*
|
|
|
|
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
sws_utils, zimg: destroy vo_x11 and vo_drm performance
Raise swscale and zimg default parameters. This restores screenshot
quality settings (maybe) unset in the commit before. Also expose some
more libswscale and zimg options.
Since these options are also used for VOs like x11 and drm, this will
make x11/drm/etc. much slower. For compensation, provide a profile that
sets the old option values: sw-fast. I'm also enabling zimg here, just
as an experiment.
The core problem is that we have a single set of command line options
which control the settings used for most swscale/zimg uses. This was
done in the previous commit. It cannot differentiate between the VOs,
which need to be realtime and may accept/require lower quality options,
and things like screenshots or vo_image, which can be slower, but should
not sacrifice quality by default.
Should this have two sets of options or something similar to do the
right thing depending on the code which calls libswscale? Maybe. Or
should I just ignore the problem, make it someone else's problem (users
who want to use software conversion VOs), provide a sub-optimal
solution, and call it a day? Definitely, sounds good, pushing to master,
goodbye.
2019-10-31 15:45:28 +00:00
|
|
|
#include <math.h>
|
|
|
|
|
video: add vf_fingerprint and a skip-logo script
skip-logo.lua is just what I wanted to have. Explanations are on the top
of that file. As usual, all documentation threatens to remove this stuff
all the time, since this stuff is just for me, and unlike a normal user
I can afford the luxuary of hacking the shit directly into the player.
vf_fingerprint is needed to support this script. It needs to scale down
video frames as part of its operation. For that, it uses zimg. zimg is
much faster than libswscale and generates more correct output. (The
filter includes a runtime fallback, but it doesn't even work because
libswscale fucks up and can't do YUV->Gray with range adjustment.)
Note on the algorithm: seems almost too simple, but was suggested to me.
It seems to be pretty effective, although long time experience with
false positives is missing. At first I wanted to use dHash [1][2], which
is also pretty simple and effective, but might actually be worse than
the implemented mechanism. dHash has the advantage that the fingerprint
is smaller. But exact matching is too unreliable, and you'd still need
to determine the number of different bits for fuzzier comparison. So
there wasn't really a reason to use it.
[1] https://pypi.org/project/dhash/
[2] http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
2019-07-15 01:20:40 +00:00
|
|
|
#include "common/common.h"
|
|
|
|
#include "common/tags.h"
|
|
|
|
#include "filters/filter.h"
|
|
|
|
#include "filters/filter_internal.h"
|
|
|
|
#include "filters/user_filters.h"
|
|
|
|
#include "options/m_option.h"
|
|
|
|
#include "video/img_format.h"
|
|
|
|
#include "video/sws_utils.h"
|
2019-10-20 13:11:07 +00:00
|
|
|
#include "video/zimg.h"
|
video: add vf_fingerprint and a skip-logo script
skip-logo.lua is just what I wanted to have. Explanations are on the top
of that file. As usual, all documentation threatens to remove this stuff
all the time, since this stuff is just for me, and unlike a normal user
I can afford the luxuary of hacking the shit directly into the player.
vf_fingerprint is needed to support this script. It needs to scale down
video frames as part of its operation. For that, it uses zimg. zimg is
much faster than libswscale and generates more correct output. (The
filter includes a runtime fallback, but it doesn't even work because
libswscale fucks up and can't do YUV->Gray with range adjustment.)
Note on the algorithm: seems almost too simple, but was suggested to me.
It seems to be pretty effective, although long time experience with
false positives is missing. At first I wanted to use dHash [1][2], which
is also pretty simple and effective, but might actually be worse than
the implemented mechanism. dHash has the advantage that the fingerprint
is smaller. But exact matching is too unreliable, and you'd still need
to determine the number of different bits for fuzzier comparison. So
there wasn't really a reason to use it.
[1] https://pypi.org/project/dhash/
[2] http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
2019-07-15 01:20:40 +00:00
|
|
|
|
|
|
|
#include "osdep/timer.h"
|
|
|
|
|
|
|
|
#define PRINT_ENTRY_NUM 10
|
|
|
|
|
|
|
|
struct f_opts {
|
|
|
|
int type;
|
|
|
|
int clear;
|
|
|
|
int print;
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct m_opt_choice_alternatives type_names[] = {
|
|
|
|
{"gray-hex-8x8", 8},
|
|
|
|
{"gray-hex-16x16", 16},
|
|
|
|
{0}
|
|
|
|
};
|
|
|
|
|
|
|
|
#define OPT_BASE_STRUCT struct f_opts
|
|
|
|
static const struct m_option f_opts_list[] = {
|
options: change option macros and all option declarations
Change all OPT_* macros such that they don't define the entire m_option
initializer, and instead expand only to a part of it, which sets certain
fields. This requires changing almost every option declaration, because
they all use these macros. A declaration now always starts with
{"name", ...
followed by designated initializers only (possibly wrapped in macros).
The OPT_* macros now initialize the .offset and .type fields only,
sometimes also .priv and others.
I think this change makes the option macros less tricky. The old code
had to stuff everything into macro arguments (and attempted to allow
setting arbitrary fields by letting the user pass designated
initializers in the vararg parts). Some of this was made messy due to
C99 and C11 not allowing 0-sized varargs with ',' removal. It's also
possible that this change is pointless, other than cosmetic preferences.
Not too happy about some things. For example, the OPT_CHOICE()
indentation I applied looks a bit ugly.
Much of this change was done with regex search&replace, but some places
required manual editing. In particular, code in "obscure" areas (which I
didn't include in compilation) might be broken now.
In wayland_common.c the author of some option declarations confused the
flags parameter with the default value (though the default value was
also properly set below). I fixed this with this change.
2020-03-14 20:28:01 +00:00
|
|
|
{"type", OPT_CHOICE_C(type, type_names)},
|
|
|
|
{"clear-on-query", OPT_FLAG(clear)},
|
|
|
|
{"print", OPT_FLAG(print)},
|
video: add vf_fingerprint and a skip-logo script
skip-logo.lua is just what I wanted to have. Explanations are on the top
of that file. As usual, all documentation threatens to remove this stuff
all the time, since this stuff is just for me, and unlike a normal user
I can afford the luxuary of hacking the shit directly into the player.
vf_fingerprint is needed to support this script. It needs to scale down
video frames as part of its operation. For that, it uses zimg. zimg is
much faster than libswscale and generates more correct output. (The
filter includes a runtime fallback, but it doesn't even work because
libswscale fucks up and can't do YUV->Gray with range adjustment.)
Note on the algorithm: seems almost too simple, but was suggested to me.
It seems to be pretty effective, although long time experience with
false positives is missing. At first I wanted to use dHash [1][2], which
is also pretty simple and effective, but might actually be worse than
the implemented mechanism. dHash has the advantage that the fingerprint
is smaller. But exact matching is too unreliable, and you'd still need
to determine the number of different bits for fuzzier comparison. So
there wasn't really a reason to use it.
[1] https://pypi.org/project/dhash/
[2] http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
2019-07-15 01:20:40 +00:00
|
|
|
{0}
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct f_opts f_opts_def = {
|
|
|
|
.type = 16,
|
|
|
|
.clear = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct print_entry {
|
|
|
|
double pts;
|
|
|
|
char *print;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct priv {
|
|
|
|
struct f_opts *opts;
|
|
|
|
struct mp_image *scaled;
|
|
|
|
struct mp_sws_context *sws;
|
2019-10-20 13:11:07 +00:00
|
|
|
struct mp_zimg_context *zimg;
|
video: add vf_fingerprint and a skip-logo script
skip-logo.lua is just what I wanted to have. Explanations are on the top
of that file. As usual, all documentation threatens to remove this stuff
all the time, since this stuff is just for me, and unlike a normal user
I can afford the luxuary of hacking the shit directly into the player.
vf_fingerprint is needed to support this script. It needs to scale down
video frames as part of its operation. For that, it uses zimg. zimg is
much faster than libswscale and generates more correct output. (The
filter includes a runtime fallback, but it doesn't even work because
libswscale fucks up and can't do YUV->Gray with range adjustment.)
Note on the algorithm: seems almost too simple, but was suggested to me.
It seems to be pretty effective, although long time experience with
false positives is missing. At first I wanted to use dHash [1][2], which
is also pretty simple and effective, but might actually be worse than
the implemented mechanism. dHash has the advantage that the fingerprint
is smaller. But exact matching is too unreliable, and you'd still need
to determine the number of different bits for fuzzier comparison. So
there wasn't really a reason to use it.
[1] https://pypi.org/project/dhash/
[2] http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
2019-07-15 01:20:40 +00:00
|
|
|
struct print_entry entries[PRINT_ENTRY_NUM];
|
|
|
|
int num_entries;
|
2019-10-20 13:11:07 +00:00
|
|
|
bool fallback_warning;
|
video: add vf_fingerprint and a skip-logo script
skip-logo.lua is just what I wanted to have. Explanations are on the top
of that file. As usual, all documentation threatens to remove this stuff
all the time, since this stuff is just for me, and unlike a normal user
I can afford the luxuary of hacking the shit directly into the player.
vf_fingerprint is needed to support this script. It needs to scale down
video frames as part of its operation. For that, it uses zimg. zimg is
much faster than libswscale and generates more correct output. (The
filter includes a runtime fallback, but it doesn't even work because
libswscale fucks up and can't do YUV->Gray with range adjustment.)
Note on the algorithm: seems almost too simple, but was suggested to me.
It seems to be pretty effective, although long time experience with
false positives is missing. At first I wanted to use dHash [1][2], which
is also pretty simple and effective, but might actually be worse than
the implemented mechanism. dHash has the advantage that the fingerprint
is smaller. But exact matching is too unreliable, and you'd still need
to determine the number of different bits for fuzzier comparison. So
there wasn't really a reason to use it.
[1] https://pypi.org/project/dhash/
[2] http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
2019-07-15 01:20:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// (Other code internal to this filter also calls this to reset the frame list.)
|
|
|
|
static void f_reset(struct mp_filter *f)
|
|
|
|
{
|
|
|
|
struct priv *p = f->priv;
|
|
|
|
|
|
|
|
for (int n = 0; n < p->num_entries; n++)
|
|
|
|
talloc_free(p->entries[n].print);
|
|
|
|
p->num_entries = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void f_process(struct mp_filter *f)
|
|
|
|
{
|
|
|
|
struct priv *p = f->priv;
|
|
|
|
|
|
|
|
if (!mp_pin_can_transfer_data(f->ppins[1], f->ppins[0]))
|
|
|
|
return;
|
|
|
|
|
|
|
|
struct mp_frame frame = mp_pin_out_read(f->ppins[0]);
|
|
|
|
|
|
|
|
if (mp_frame_is_signaling(frame)) {
|
|
|
|
mp_pin_in_write(f->ppins[1], frame);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame.type != MP_FRAME_VIDEO)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
struct mp_image *mpi = frame.data;
|
|
|
|
|
2019-10-20 13:11:07 +00:00
|
|
|
// Try to achieve minimum conversion, even if it makes the fingerprints less
|
|
|
|
// "portable" across source video.
|
|
|
|
p->scaled->params.color = mpi->params.color;
|
|
|
|
// Make output always full range; no reason to lose precision.
|
|
|
|
p->scaled->params.color.levels = MP_CSP_LEVELS_PC;
|
|
|
|
|
2019-10-20 13:17:33 +00:00
|
|
|
if (!mp_zimg_convert(p->zimg, p->scaled, mpi)) {
|
2019-10-20 13:11:07 +00:00
|
|
|
if (!p->fallback_warning) {
|
|
|
|
MP_WARN(f, "Falling back to libswscale.\n");
|
|
|
|
p->fallback_warning = true;
|
|
|
|
}
|
video: add vf_fingerprint and a skip-logo script
skip-logo.lua is just what I wanted to have. Explanations are on the top
of that file. As usual, all documentation threatens to remove this stuff
all the time, since this stuff is just for me, and unlike a normal user
I can afford the luxuary of hacking the shit directly into the player.
vf_fingerprint is needed to support this script. It needs to scale down
video frames as part of its operation. For that, it uses zimg. zimg is
much faster than libswscale and generates more correct output. (The
filter includes a runtime fallback, but it doesn't even work because
libswscale fucks up and can't do YUV->Gray with range adjustment.)
Note on the algorithm: seems almost too simple, but was suggested to me.
It seems to be pretty effective, although long time experience with
false positives is missing. At first I wanted to use dHash [1][2], which
is also pretty simple and effective, but might actually be worse than
the implemented mechanism. dHash has the advantage that the fingerprint
is smaller. But exact matching is too unreliable, and you'd still need
to determine the number of different bits for fuzzier comparison. So
there wasn't really a reason to use it.
[1] https://pypi.org/project/dhash/
[2] http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
2019-07-15 01:20:40 +00:00
|
|
|
if (mp_sws_scale(p->sws, p->scaled, mpi) < 0)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->num_entries >= PRINT_ENTRY_NUM) {
|
|
|
|
talloc_free(p->entries[0].print);
|
|
|
|
MP_TARRAY_REMOVE_AT(p->entries, p->num_entries, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int size = p->scaled->w;
|
|
|
|
|
|
|
|
struct print_entry *e = &p->entries[p->num_entries++];
|
|
|
|
e->pts = mpi->pts;
|
|
|
|
e->print = talloc_array(p, char, size * size * 2 + 1);
|
|
|
|
|
|
|
|
for (int y = 0; y < size; y++) {
|
|
|
|
for (int x = 0; x < size; x++) {
|
|
|
|
char *offs = &e->print[(y * size + x) * 2];
|
|
|
|
uint8_t v = p->scaled->planes[0][y * p->scaled->stride[0] + x];
|
|
|
|
snprintf(offs, 3, "%02x", v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->opts->print)
|
|
|
|
MP_INFO(f, "%f: %s\n", e->pts, e->print);
|
|
|
|
|
|
|
|
mp_pin_in_write(f->ppins[1], frame);
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
MP_ERR(f, "unsupported video format\n");
|
|
|
|
mp_pin_in_write(f->ppins[1], frame);
|
|
|
|
mp_filter_internal_mark_failed(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool f_command(struct mp_filter *f, struct mp_filter_command *cmd)
|
|
|
|
{
|
|
|
|
struct priv *p = f->priv;
|
|
|
|
|
|
|
|
switch (cmd->type) {
|
|
|
|
case MP_FILTER_COMMAND_GET_META: {
|
|
|
|
struct mp_tags *t = talloc_zero(NULL, struct mp_tags);
|
|
|
|
|
|
|
|
for (int n = 0; n < p->num_entries; n++) {
|
|
|
|
struct print_entry *e = &p->entries[n];
|
|
|
|
|
|
|
|
if (e->pts != MP_NOPTS_VALUE) {
|
|
|
|
mp_tags_set_str(t, mp_tprintf(80, "fp%d.pts", n),
|
|
|
|
mp_tprintf(80, "%f", e->pts));
|
|
|
|
}
|
|
|
|
mp_tags_set_str(t, mp_tprintf(80, "fp%d.hex", n), e->print);
|
|
|
|
}
|
|
|
|
|
|
|
|
mp_tags_set_str(t, "type", m_opt_choice_str(type_names, p->opts->type));
|
|
|
|
|
|
|
|
if (p->opts->clear)
|
|
|
|
f_reset(f);
|
|
|
|
|
|
|
|
*(struct mp_tags **)cmd->res = t;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct mp_filter_info filter = {
|
|
|
|
.name = "fingerprint",
|
|
|
|
.process = f_process,
|
|
|
|
.command = f_command,
|
|
|
|
.reset = f_reset,
|
|
|
|
.priv_size = sizeof(struct priv),
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct mp_filter *f_create(struct mp_filter *parent, void *options)
|
|
|
|
{
|
|
|
|
struct mp_filter *f = mp_filter_create(parent, &filter);
|
|
|
|
if (!f) {
|
|
|
|
talloc_free(options);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
mp_filter_add_pin(f, MP_PIN_IN, "in");
|
|
|
|
mp_filter_add_pin(f, MP_PIN_OUT, "out");
|
|
|
|
|
|
|
|
struct priv *p = f->priv;
|
|
|
|
p->opts = talloc_steal(p, options);
|
|
|
|
int size = p->opts->type;
|
|
|
|
p->scaled = mp_image_alloc(IMGFMT_Y8, size, size);
|
|
|
|
MP_HANDLE_OOM(p->scaled);
|
|
|
|
talloc_steal(p, p->scaled);
|
|
|
|
p->sws = mp_sws_alloc(p);
|
|
|
|
MP_HANDLE_OOM(p->sws);
|
2019-10-20 13:11:07 +00:00
|
|
|
p->zimg = mp_zimg_alloc();
|
|
|
|
talloc_steal(p, p->zimg);
|
sws_utils, zimg: destroy vo_x11 and vo_drm performance
Raise swscale and zimg default parameters. This restores screenshot
quality settings (maybe) unset in the commit before. Also expose some
more libswscale and zimg options.
Since these options are also used for VOs like x11 and drm, this will
make x11/drm/etc. much slower. For compensation, provide a profile that
sets the old option values: sw-fast. I'm also enabling zimg here, just
as an experiment.
The core problem is that we have a single set of command line options
which control the settings used for most swscale/zimg uses. This was
done in the previous commit. It cannot differentiate between the VOs,
which need to be realtime and may accept/require lower quality options,
and things like screenshots or vo_image, which can be slower, but should
not sacrifice quality by default.
Should this have two sets of options or something similar to do the
right thing depending on the code which calls libswscale? Maybe. Or
should I just ignore the problem, make it someone else's problem (users
who want to use software conversion VOs), provide a sub-optimal
solution, and call it a day? Definitely, sounds good, pushing to master,
goodbye.
2019-10-31 15:45:28 +00:00
|
|
|
p->zimg->opts = (struct zimg_opts){
|
|
|
|
.scaler = ZIMG_RESIZE_BILINEAR,
|
|
|
|
.scaler_params = {NAN, NAN},
|
|
|
|
.scaler_chroma_params = {NAN, NAN},
|
|
|
|
.scaler_chroma = ZIMG_RESIZE_BILINEAR,
|
|
|
|
.dither = ZIMG_DITHER_NONE,
|
|
|
|
.fast = 1,
|
|
|
|
};
|
video: add vf_fingerprint and a skip-logo script
skip-logo.lua is just what I wanted to have. Explanations are on the top
of that file. As usual, all documentation threatens to remove this stuff
all the time, since this stuff is just for me, and unlike a normal user
I can afford the luxuary of hacking the shit directly into the player.
vf_fingerprint is needed to support this script. It needs to scale down
video frames as part of its operation. For that, it uses zimg. zimg is
much faster than libswscale and generates more correct output. (The
filter includes a runtime fallback, but it doesn't even work because
libswscale fucks up and can't do YUV->Gray with range adjustment.)
Note on the algorithm: seems almost too simple, but was suggested to me.
It seems to be pretty effective, although long time experience with
false positives is missing. At first I wanted to use dHash [1][2], which
is also pretty simple and effective, but might actually be worse than
the implemented mechanism. dHash has the advantage that the fingerprint
is smaller. But exact matching is too unreliable, and you'd still need
to determine the number of different bits for fuzzier comparison. So
there wasn't really a reason to use it.
[1] https://pypi.org/project/dhash/
[2] http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
2019-07-15 01:20:40 +00:00
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct mp_user_filter_entry vf_fingerprint = {
|
|
|
|
.desc = {
|
2019-09-21 20:46:02 +00:00
|
|
|
.description = "Compute video frame fingerprints",
|
video: add vf_fingerprint and a skip-logo script
skip-logo.lua is just what I wanted to have. Explanations are on the top
of that file. As usual, all documentation threatens to remove this stuff
all the time, since this stuff is just for me, and unlike a normal user
I can afford the luxuary of hacking the shit directly into the player.
vf_fingerprint is needed to support this script. It needs to scale down
video frames as part of its operation. For that, it uses zimg. zimg is
much faster than libswscale and generates more correct output. (The
filter includes a runtime fallback, but it doesn't even work because
libswscale fucks up and can't do YUV->Gray with range adjustment.)
Note on the algorithm: seems almost too simple, but was suggested to me.
It seems to be pretty effective, although long time experience with
false positives is missing. At first I wanted to use dHash [1][2], which
is also pretty simple and effective, but might actually be worse than
the implemented mechanism. dHash has the advantage that the fingerprint
is smaller. But exact matching is too unreliable, and you'd still need
to determine the number of different bits for fuzzier comparison. So
there wasn't really a reason to use it.
[1] https://pypi.org/project/dhash/
[2] http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
2019-07-15 01:20:40 +00:00
|
|
|
.name = "fingerprint",
|
|
|
|
.priv_size = sizeof(OPT_BASE_STRUCT),
|
|
|
|
.priv_defaults = &f_opts_def,
|
|
|
|
.options = f_opts_list,
|
|
|
|
},
|
|
|
|
.create = f_create,
|
|
|
|
};
|