Merge commit '559a400ac36e75a8d73ba263fd7fa6736df1c2da' into wm4-commits--merge-edition

This bumps libmpv version to 1.103
This commit is contained in:
Anton Kindestam
2018-12-05 19:02:03 +01:00
98 changed files with 3972 additions and 3183 deletions

View File

@@ -55,19 +55,42 @@ static const union m_option_value default_value;
// For use with m_config_cache.
struct m_config_shadow {
pthread_mutex_t lock;
struct m_config *root;
char *data;
pthread_mutex_t lock;
// -- protected by lock
struct m_config_data *data; // protected shadow copy of the option data
struct m_config_cache **listeners;
int num_listeners;
};
// Represents a sub-struct (OPT_SUBSTRUCT()).
struct m_config_group {
const struct m_sub_options *group; // or NULL for top-level options
int parent_group; // index of parent group in m_config.groups
void *opts; // pointer to group user option struct
atomic_llong ts; // incremented on every write access
const struct m_sub_options *group;
int group_count; // 1 + number of all sub groups owned by this (so
// m_config.groups[idx..idx+group_count] is used by the
// entire tree of sub groups included by this group)
int parent_group; // index of parent group into m_config.groups[], or
// -1 for group 0
int parent_ptr; // ptr offset in the parent group's data, or -1 if
// none
int co_index; // index of the first group opt into m_config.opts[]
int co_end_index; // index of the last group opt + 1 (i.e. exclusive)
};
// A copy of option data. Used for the main option struct, the shadow data,
// and copies for m_config_cache.
struct m_config_data {
struct m_config *root; // root config (with up-to-date data)
int group_index; // start index into m_config.groups[]
struct m_group_data *gdata; // user struct allocation (our copy of data)
int num_gdata; // (group_index+num_gdata = end index)
atomic_llong ts; // last change timestamp we've seen
};
// Per m_config_data state for each m_config_group.
struct m_group_data {
char *udata; // pointer to group user option struct
long long ts; // incremented on every write access
};
struct m_profile {
@@ -86,6 +109,20 @@ struct m_opt_backup {
void *backup;
};
static void add_sub_group(struct m_config *config, const char *name_prefix,
int parent_group_index, int parent_ptr,
const struct m_sub_options *subopts);
static struct m_group_data *m_config_gdata(struct m_config_data *data,
int group_index)
{
if (group_index < data->group_index ||
group_index >= data->group_index + data->num_gdata)
return NULL;
return &data->gdata[group_index - data->group_index];
}
static int show_profile(struct m_config *config, bstr param)
{
struct m_profile *p;
@@ -140,30 +177,129 @@ static void substruct_write_ptr(void *ptr, void *val)
memcpy(ptr, &src, sizeof(src));
}
static void add_options(struct m_config *config,
struct m_config_option *parent,
void *optstruct,
const void *optstruct_def,
const struct m_option *defs);
// Initialize a field with a given value. In case this is dynamic data, it has
// to be allocated and copied. src can alias dst.
static void init_opt_inplace(const struct m_option *opt, void *dst,
const void *src)
{
// The option will use dynamic memory allocation iff it has a free callback.
if (opt->type->free) {
union m_option_value temp;
memcpy(&temp, src, opt->type->size);
memset(dst, 0, opt->type->size);
m_option_copy(opt, dst, &temp);
} else if (src != dst) {
memcpy(dst, src, opt->type->size);
}
}
static void alloc_group(struct m_config_data *data, int group_index,
struct m_config_data *copy)
{
assert(group_index == data->group_index + data->num_gdata);
assert(group_index < data->root->num_groups);
struct m_config_group *group = &data->root->groups[group_index];
const struct m_sub_options *opts = group->group;
MP_TARRAY_GROW(data, data->gdata, data->num_gdata);
struct m_group_data *gdata = &data->gdata[data->num_gdata++];
struct m_group_data *copy_gdata =
copy ? m_config_gdata(copy, group_index) : NULL;
*gdata = (struct m_group_data){
.udata = talloc_zero_size(data, opts->size),
.ts = copy_gdata ? copy_gdata->ts : 0,
};
if (opts->defaults)
memcpy(gdata->udata, opts->defaults, opts->size);
char *copy_src = copy_gdata ? copy_gdata->udata : NULL;
for (int n = group->co_index; n < group->co_end_index; n++) {
assert(n >= 0 && n < data->root->num_opts);
struct m_config_option *co = &data->root->opts[n];
if (co->opt->offset < 0 || co->opt->type->size == 0)
continue;
void *dst = gdata->udata + co->opt->offset;
const void *defptr = co->opt->defval ? co->opt->defval : dst;
if (copy_src)
defptr = copy_src + co->opt->offset;
init_opt_inplace(co->opt, dst, defptr);
}
// If there's a parent, update its pointer to the new struct.
if (group->parent_group >= data->group_index && group->parent_ptr >= 0) {
struct m_group_data *parent_gdata =
m_config_gdata(data, group->parent_group);
assert(parent_gdata);
substruct_write_ptr(parent_gdata->udata + group->parent_ptr, gdata->udata);
}
}
static void free_option_data(void *p)
{
struct m_config_data *data = p;
for (int i = 0; i < data->num_gdata; i++) {
struct m_group_data *gdata = &data->gdata[i];
struct m_config_group *group = &data->root->groups[data->group_index + i];
for (int n = group->co_index; n < group->co_end_index; n++) {
struct m_config_option *co = &data->root->opts[n];
if (co->opt->offset >= 0 && co->opt->type->size > 0)
m_option_free(co->opt, gdata->udata + co->opt->offset);
}
}
}
// Allocate data using the option description in root, starting at group_index
// (index into m_config.groups[]).
// If copy is not NULL, copy all data from there (for groups which are in both
// m_config_data instances), in all other cases init the data with the defaults.
static struct m_config_data *allocate_option_data(void *ta_parent,
struct m_config *root,
int group_index,
struct m_config_data *copy)
{
assert(group_index >= 0 && group_index < root->num_groups);
struct m_config_data *data = talloc_zero(ta_parent, struct m_config_data);
talloc_set_destructor(data, free_option_data);
data->root = root;
data->group_index = group_index;
struct m_config_group *root_group = &root->groups[group_index];
assert(root_group->group_count > 0);
for (int n = group_index; n < group_index + root_group->group_count; n++)
alloc_group(data, n, copy);
if (copy)
data->ts = copy->ts;
return data;
}
static void config_destroy(void *p)
{
struct m_config *config = p;
m_config_restore_backups(config);
for (int n = 0; n < config->num_opts; n++) {
struct m_config_option *co = &config->opts[n];
m_option_free(co->opt, co->data);
if (config->shadow && co->shadow_offset >= 0)
m_option_free(co->opt, config->shadow->data + co->shadow_offset);
}
if (config->shadow) {
// must all have been unregistered
assert(config->shadow->num_listeners == 0);
pthread_mutex_destroy(&config->shadow->lock);
talloc_free(config->shadow);
}
talloc_free(config->data);
}
struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log,
@@ -172,25 +308,29 @@ struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log,
{
struct m_config *config = talloc(talloc_ctx, struct m_config);
talloc_set_destructor(config, config_destroy);
*config = (struct m_config)
{.log = log, .size = size, .defaults = defaults, .options = options};
*config = (struct m_config){.log = log,};
// size==0 means a dummy object is created
if (size) {
config->optstruct = talloc_zero_size(config, size);
if (defaults)
memcpy(config->optstruct, defaults, size);
struct m_sub_options *subopts = talloc_ptrtype(config, subopts);
*subopts = (struct m_sub_options){
.opts = options,
.size = size,
.defaults = defaults,
};
add_sub_group(config, NULL, -1, -1, subopts);
if (!size)
return config;
config->data = allocate_option_data(config, config, 0, NULL);
config->optstruct = config->data->gdata[0].udata;
for (int n = 0; n < config->num_opts; n++) {
struct m_config_option *co = &config->opts[n];
struct m_group_data *gdata = m_config_gdata(config->data, co->group_index);
if (gdata && co->opt->offset >= 0)
co->data = gdata->udata + co->opt->offset;
}
config->num_groups = 1;
MP_TARRAY_GROW(config, config->groups, 1);
config->groups[0] = (struct m_config_group){
.parent_group = -1,
.opts = config->optstruct,
};
if (options)
add_options(config, NULL, config->optstruct, defaults, options);
return config;
}
@@ -216,14 +356,14 @@ struct m_config *m_config_from_obj_desc_noalloc(void *talloc_ctx,
return m_config_new(talloc_ctx, log, 0, desc->priv_defaults, desc->options);
}
static struct m_config_group *find_group(struct mpv_global *global,
const struct m_option *cfg)
static const struct m_config_group *find_group(struct mpv_global *global,
const struct m_option *cfg)
{
struct m_config_shadow *shadow = global->config;
struct m_config *root = shadow->root;
for (int n = 0; n < root->num_groups; n++) {
if (cfg && root->groups[n].group && root->groups[n].group->opts == cfg)
if (root->groups[n].group->opts == cfg)
return &root->groups[n];
}
@@ -238,7 +378,7 @@ static struct m_config_group *find_group(struct mpv_global *global,
void *m_config_group_from_desc(void *ta_parent, struct mp_log *log,
struct mpv_global *global, struct m_obj_desc *desc, const char *name)
{
struct m_config_group *group = find_group(global, desc->options);
const struct m_config_group *group = find_group(global, desc->options);
if (group) {
return mp_get_config_group(ta_parent, global, group->group);
} else {
@@ -335,211 +475,109 @@ void m_config_backup_all_opts(struct m_config *config)
ensure_backup(config, &config->opts[n]);
}
static void m_config_add_option(struct m_config *config,
struct m_config_option *parent,
void *optstruct,
const void *optstruct_def,
const struct m_option *arg);
static void add_options(struct m_config *config,
struct m_config_option *parent,
void *optstruct,
const void *optstruct_def,
const struct m_option *defs)
static void init_obj_settings_list(struct m_config *config,
int parent_group_index,
const struct m_obj_list *list)
{
for (int i = 0; defs && defs[i].name; i++)
m_config_add_option(config, parent, optstruct, optstruct_def, &defs[i]);
struct m_obj_desc desc;
for (int n = 0; ; n++) {
if (!list->get_desc(&desc, n))
break;
if (desc.global_opts) {
add_sub_group(config, NULL, parent_group_index, -1,
desc.global_opts);
}
if (list->use_global_options && desc.options) {
struct m_sub_options *conf = talloc_ptrtype(config, conf);
*conf = (struct m_sub_options){
.prefix = desc.options_prefix,
.opts = desc.options,
.defaults = desc.priv_defaults,
.size = desc.priv_size,
};
add_sub_group(config, NULL, parent_group_index, -1, conf);
}
}
}
static void add_sub_options(struct m_config *config,
struct m_config_option *parent,
const struct m_sub_options *subopts)
static const char *concat_name(void *ta_parent, const char *a, const char *b)
{
// Can't be used multiple times.
assert(a);
assert(b);
if (!a[0])
return b;
if (!b[0])
return a;
return talloc_asprintf(ta_parent, "%s-%s", a, b);
}
static void add_sub_group(struct m_config *config, const char *name_prefix,
int parent_group_index, int parent_ptr,
const struct m_sub_options *subopts)
{
// Can't be used multiple times.
for (int n = 0; n < config->num_groups; n++)
assert(config->groups[n].group != subopts);
// You can only use UPDATE_ flags here.
assert(!(subopts->change_flags & ~(unsigned)UPDATE_OPTS_MASK));
void *new_optstruct = NULL;
if (config->optstruct) { // only if not noalloc
new_optstruct = talloc_zero_size(config, subopts->size);
if (subopts->defaults)
memcpy(new_optstruct, subopts->defaults, subopts->size);
}
if (parent && parent->data)
substruct_write_ptr(parent->data, new_optstruct);
assert(parent_group_index >= -1 && parent_group_index < config->num_groups);
const void *new_optstruct_def = NULL;
if (parent && parent->default_data)
new_optstruct_def = substruct_read_ptr(parent->default_data);
if (!new_optstruct_def)
new_optstruct_def = subopts->defaults;
int group = config->num_groups++;
MP_TARRAY_GROW(config, config->groups, group);
config->groups[group] = (struct m_config_group){
int group_index = config->num_groups++;
MP_TARRAY_GROW(config, config->groups, group_index);
config->groups[group_index] = (struct m_config_group){
.group = subopts,
.parent_group = parent ? parent->group : 0,
.opts = new_optstruct,
.parent_group = parent_group_index,
.parent_ptr = parent_ptr,
.co_index = config->num_opts,
};
struct m_config_option next = {
.name = "",
.group = group,
};
if (parent && parent->name && parent->name[0])
next.name = parent->name;
if (subopts->prefix && subopts->prefix[0]) {
assert(next.name);
next.name = subopts->prefix;
}
add_options(config, &next, new_optstruct, new_optstruct_def, subopts->opts);
}
if (subopts->prefix && subopts->prefix[0])
name_prefix = subopts->prefix;
if (!name_prefix)
name_prefix = "";
#define MAX_VO_AO 16
for (int i = 0; subopts->opts && subopts->opts[i].name; i++) {
const struct m_option *opt = &subopts->opts[i];
struct group_entry {
const struct m_obj_list *entry;
struct m_sub_options subs[MAX_VO_AO];
bool initialized;
};
static struct group_entry g_groups[2]; // limited by max. m_obj_list overall
static int g_num_groups = 0;
static pthread_mutex_t g_group_mutex = PTHREAD_MUTEX_INITIALIZER;
static const struct m_sub_options *get_cached_group(const struct m_obj_list *list,
int n, struct m_sub_options *v)
{
pthread_mutex_lock(&g_group_mutex);
struct group_entry *group = NULL;
for (int i = 0; i < g_num_groups; i++) {
if (g_groups[i].entry == list) {
group = &g_groups[i];
break;
}
}
if (!group) {
assert(g_num_groups < MP_ARRAY_SIZE(g_groups));
group = &g_groups[g_num_groups++];
group->entry = list;
}
if (!group->initialized) {
if (!v) {
n = -1;
group->initialized = true;
} else {
assert(n < MAX_VO_AO); // simply increase this if it fails
group->subs[n] = *v;
}
}
pthread_mutex_unlock(&g_group_mutex);
return n >= 0 ? &group->subs[n] : NULL;
}
static void init_obj_settings_list(struct m_config *config,
const struct m_obj_list *list)
{
struct m_obj_desc desc;
for (int n = 0; ; n++) {
if (!list->get_desc(&desc, n)) {
if (list->use_global_options)
get_cached_group(list, n, NULL);
break;
}
if (desc.global_opts)
add_sub_options(config, NULL, desc.global_opts);
if (list->use_global_options && desc.options) {
struct m_sub_options conf = {
.prefix = desc.options_prefix,
.opts = desc.options,
.defaults = desc.priv_defaults,
.size = desc.priv_size,
};
add_sub_options(config, NULL, get_cached_group(list, n, &conf));
}
}
}
// Initialize a field with a given value. In case this is dynamic data, it has
// to be allocated and copied. src can alias dst, also can be NULL.
static void init_opt_inplace(const struct m_option *opt, void *dst,
const void *src)
{
union m_option_value temp = {0};
if (src)
memcpy(&temp, src, opt->type->size);
memset(dst, 0, opt->type->size);
m_option_copy(opt, dst, &temp);
}
static void m_config_add_option(struct m_config *config,
struct m_config_option *parent,
void *optstruct,
const void *optstruct_def,
const struct m_option *arg)
{
assert(config != NULL);
assert(arg != NULL);
const char *parent_name = parent ? parent->name : "";
struct m_config_option co = {
.opt = arg,
.name = arg->name,
.shadow_offset = -1,
.group = parent ? parent->group : 0,
.default_data = &default_value,
.is_hidden = !!arg->deprecation_message,
};
if (arg->offset >= 0) {
if (optstruct)
co.data = (char *)optstruct + arg->offset;
if (optstruct_def)
co.default_data = (char *)optstruct_def + arg->offset;
}
if (arg->defval)
co.default_data = arg->defval;
// Fill in the full name
if (!co.name[0]) {
co.name = parent_name;
} else if (parent_name[0]) {
co.name = talloc_asprintf(config, "%s-%s", parent_name, co.name);
}
if (arg->type == &m_option_type_subconfig) {
const struct m_sub_options *subopts = arg->priv;
add_sub_options(config, &co, subopts);
} else {
int size = arg->type->size;
if (optstruct && size) {
// The required alignment is unknown, so go with the maximum C
// could require. Slightly wasteful, but not that much.
int align = (size - config->shadow_size % size) % size;
int offset = config->shadow_size + align;
assert(offset <= INT16_MAX);
co.shadow_offset = offset;
config->shadow_size = co.shadow_offset + size;
}
// Initialize options
if (co.data && co.default_data)
init_opt_inplace(arg, co.data, co.default_data);
if (opt->type == &m_option_type_subconfig)
continue;
struct m_config_option co = {
.name = concat_name(config, name_prefix, opt->name),
.opt = opt,
.group_index = group_index,
.is_hidden = !!opt->deprecation_message,
};
MP_TARRAY_APPEND(config, config->opts, config->num_opts, co);
if (arg->type == &m_option_type_obj_settings_list)
init_obj_settings_list(config, (const struct m_obj_list *)arg->priv);
}
config->groups[group_index].co_end_index = config->num_opts;
// Initialize sub-structs. These have to come after, because co_index and
// co_end_index must strictly be for a single struct only.
for (int i = 0; subopts->opts && subopts->opts[i].name; i++) {
const struct m_option *opt = &subopts->opts[i];
if (opt->type == &m_option_type_subconfig) {
const struct m_sub_options *new_subopts = opt->priv;
// Providing default structs in-place is not allowed.
if (opt->offset >= 0 && subopts->defaults) {
void *ptr = (char *)subopts->defaults + opt->offset;
assert(!substruct_read_ptr(ptr));
}
const char *prefix = concat_name(config, name_prefix, opt->name);
add_sub_group(config, prefix, group_index, opt->offset, new_subopts);
} else if (opt->type == &m_option_type_obj_settings_list) {
const struct m_obj_list *objlist = opt->priv;
init_obj_settings_list(config, group_index, objlist);
}
}
config->groups[group_index].group_count = config->num_groups - group_index;
}
struct m_config_option *m_config_get_co_raw(const struct m_config *config,
@@ -627,6 +665,19 @@ struct m_config_option *m_config_get_co_index(struct m_config *config, int index
return &config->opts[index];
}
const void *m_config_get_co_default(const struct m_config *config,
struct m_config_option *co)
{
if (co->opt->defval)
return co->opt->defval;
const struct m_sub_options *subopt = config->groups[co->group_index].group;
if (co->opt->offset >= 0 && subopt->defaults)
return (char *)subopt->defaults + co->opt->offset;
return NULL;
}
const char *m_config_get_positional_option(const struct m_config *config, int p)
{
int pos = 0;
@@ -753,7 +804,6 @@ static int m_config_handle_special_options(struct m_config *config,
return M_OPT_UNKNOWN;
}
// Unlike m_config_set_option_raw() this does not go through the property layer
// via config.option_set_callback.
int m_config_set_option_raw_direct(struct m_config *config,
@@ -1033,8 +1083,11 @@ void m_config_print_option_list(const struct m_config *config, const char *name)
MP_INFO(config, " (%s to %s)", min, max);
}
char *def = NULL;
if (co->default_data)
def = m_option_pretty_print(opt, co->default_data);
const void *defptr = m_config_get_co_default(config, co);
if (!defptr)
defptr = &default_value;
if (defptr)
def = m_option_pretty_print(opt, defptr);
if (def) {
MP_INFO(config, " (default: %s)", def);
talloc_free(def);
@@ -1188,36 +1241,16 @@ struct mpv_node m_config_get_profiles(struct m_config *config)
void m_config_create_shadow(struct m_config *config)
{
assert(config->global && config->options && config->size);
assert(config->global);
assert(!config->shadow && !config->global->config);
config->shadow = talloc_zero(config, struct m_config_shadow);
config->shadow->data = talloc_zero_size(config->shadow, config->shadow_size);
config->shadow = talloc_zero(NULL, struct m_config_shadow);
config->shadow->data =
allocate_option_data(config->shadow, config, 0, config->data);
config->shadow->root = config;
pthread_mutex_init(&config->shadow->lock, NULL);
config->global->config = config->shadow;
for (int n = 0; n < config->num_opts; n++) {
struct m_config_option *co = &config->opts[n];
if (co->shadow_offset < 0)
continue;
m_option_copy(co->opt, config->shadow->data + co->shadow_offset, co->data);
}
}
// Return whether parent is a parent of group. Also returns true if they're equal.
static bool is_group_included(struct m_config *config, int group, int parent)
{
for (;;) {
if (group == parent)
return true;
if (group < 0)
break;
group = config->groups[group].parent_group;
}
return false;
}
static void cache_destroy(void *p)
@@ -1236,58 +1269,64 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
{
struct m_config_shadow *shadow = global->config;
struct m_config *root = shadow->root;
int group_index = -1;
struct m_config_cache *cache = talloc_zero(ta_parent, struct m_config_cache);
talloc_set_destructor(cache, cache_destroy);
cache->shadow = shadow;
cache->shadow_config = m_config_new(cache, mp_null_log, root->size,
root->defaults, root->options);
struct m_config *config = cache->shadow_config;
assert(config->num_opts == root->num_opts);
for (int n = 0; n < root->num_opts; n++) {
assert(config->opts[n].opt->type == root->opts[n].opt->type);
assert(config->opts[n].shadow_offset == root->opts[n].shadow_offset);
}
cache->ts = -1;
cache->group = -1;
for (int n = 0; n < config->num_groups; n++) {
if (config->groups[n].group == group) {
cache->opts = config->groups[n].opts;
cache->group = n;
for (int n = 0; n < root->num_groups; n++) {
// group==NULL is special cased to root group.
if (root->groups[n].group == group || (!group && !n)) {
group_index = n;
break;
}
}
assert(cache->group >= 0);
assert(cache->opts);
assert(group_index >= 0); // invalid group (or not in option tree)
// If we're not on the top-level, restrict set of options to the sub-group
// to reduce update costs. (It would be better not to add them in the first
// place.)
if (cache->group > 0) {
int num_opts = config->num_opts;
config->num_opts = 0;
for (int n = 0; n < num_opts; n++) {
struct m_config_option *co = &config->opts[n];
if (is_group_included(config, co->group, cache->group)) {
config->opts[config->num_opts++] = *co;
} else {
m_option_free(co->opt, co->data);
struct m_config_cache *cache = talloc_zero(ta_parent, struct m_config_cache);
talloc_set_destructor(cache, cache_destroy);
cache->shadow = shadow;
pthread_mutex_lock(&shadow->lock);
cache->data = allocate_option_data(cache, root, group_index, shadow->data);
pthread_mutex_unlock(&shadow->lock);
cache->opts = cache->data->gdata[0].udata;
return cache;
}
static bool update_options(struct m_config_data *dst, struct m_config_data *src)
{
assert(dst->root == src->root);
bool res = false;
dst->ts = src->ts;
// Must be from same root, but they can have arbitrary overlap.
int group_s = MPMAX(dst->group_index, src->group_index);
int group_e = MPMIN(dst->group_index + dst->num_gdata,
src->group_index + src->num_gdata);
assert(group_s >= 0 && group_e <= dst->root->num_groups);
for (int n = group_s; n < group_e; n++) {
struct m_config_group *g = &dst->root->groups[n];
struct m_group_data *gsrc = m_config_gdata(src, n);
struct m_group_data *gdst = m_config_gdata(dst, n);
assert(gsrc && gdst);
if (gdst->ts >= gsrc->ts)
continue;
gdst->ts = gsrc->ts;
res = true;
for (int i = g->co_index; i < g->co_end_index; i++) {
struct m_config_option *co = &dst->root->opts[i];
if (co->opt->offset >= 0 && co->opt->type->size) {
m_option_copy(co->opt, gdst->udata + co->opt->offset,
gsrc->udata + co->opt->offset);
}
}
for (int n = 0; n < config->num_groups; n++) {
if (!is_group_included(config, n, cache->group))
TA_FREEP(&config->groups[n].opts);
}
}
m_config_cache_update(cache);
return cache;
return res;
}
bool m_config_cache_update(struct m_config_cache *cache)
@@ -1296,51 +1335,48 @@ bool m_config_cache_update(struct m_config_cache *cache)
// Using atomics and checking outside of the lock - it's unknown whether
// this makes it faster or slower. Just cargo culting it.
if (atomic_load(&shadow->root->groups[cache->group].ts) <= cache->ts)
if (atomic_load_explicit(&cache->data->ts, memory_order_relaxed) >=
atomic_load(&shadow->data->ts))
return false;
pthread_mutex_lock(&shadow->lock);
cache->ts = atomic_load(&shadow->root->groups[cache->group].ts);
for (int n = 0; n < cache->shadow_config->num_opts; n++) {
struct m_config_option *co = &cache->shadow_config->opts[n];
if (co->shadow_offset >= 0)
m_option_copy(co->opt, co->data, shadow->data + co->shadow_offset);
}
bool res = update_options(cache->data, shadow->data);
pthread_mutex_unlock(&shadow->lock);
return true;
return res;
}
void m_config_notify_change_co(struct m_config *config,
struct m_config_option *co)
{
struct m_config_shadow *shadow = config->shadow;
assert(co->data);
if (shadow) {
pthread_mutex_lock(&shadow->lock);
if (co->shadow_offset >= 0)
m_option_copy(co->opt, shadow->data + co->shadow_offset, co->data);
struct m_config_data *data = shadow->data;
struct m_group_data *gdata = m_config_gdata(data, co->group_index);
assert(gdata);
gdata->ts = atomic_fetch_add(&data->ts, 1) + 1;
m_option_copy(co->opt, gdata->udata + co->opt->offset, co->data);
for (int n = 0; n < shadow->num_listeners; n++) {
struct m_config_cache *cache = shadow->listeners[n];
if (cache->wakeup_cb && m_config_gdata(cache->data, co->group_index))
cache->wakeup_cb(cache->wakeup_cb_ctx);
}
pthread_mutex_unlock(&shadow->lock);
}
int changed = co->opt->flags & UPDATE_OPTS_MASK;
int group = co->group;
while (group >= 0) {
struct m_config_group *g = &config->groups[group];
atomic_fetch_add(&g->ts, 1);
if (g->group)
changed |= g->group->change_flags;
group = g->parent_group;
}
if (shadow) {
pthread_mutex_lock(&shadow->lock);
for (int n = 0; n < shadow->num_listeners; n++) {
struct m_config_cache *cache = shadow->listeners[n];
if (cache->wakeup_cb)
cache->wakeup_cb(cache->wakeup_cb_ctx);
}
pthread_mutex_unlock(&shadow->lock);
int group_index = co->group_index;
while (group_index >= 0) {
struct m_config_group *g = &config->groups[group_index];
changed |= g->group->change_flags;
group_index = g->parent_group;
}
if (config->option_change_callback) {
@@ -1441,11 +1477,14 @@ void mp_read_option_raw(struct mpv_global *global, const char *name,
struct m_config_shadow *shadow = global->config;
struct m_config_option *co = m_config_get_co_raw(shadow->root, bstr0(name));
assert(co);
assert(co->shadow_offset >= 0);
assert(co->opt->offset >= 0);
assert(co->opt->type == type);
struct m_group_data *gdata = m_config_gdata(shadow->data, co->group_index);
assert(gdata);
memset(dst, 0, co->opt->type->size);
m_option_copy(co->opt, dst, shadow->data + co->shadow_offset);
m_option_copy(co->opt, dst, gdata->udata + co->opt->offset);
}
struct m_config *mp_get_root_config(struct mpv_global *global)

View File

@@ -43,12 +43,10 @@ struct m_config_option {
bool is_set_from_config : 1; // Set by a config file
bool is_set_locally : 1; // Has a backup entry
bool warning_was_printed : 1;
int16_t shadow_offset; // Offset into m_config_shadow.data
int16_t group; // Index into m_config.groups
int16_t group_index; // Index into m_config.groups
const char *name; // Full name (ie option-subopt)
const struct m_option *opt; // Option description
void *data; // Raw value of the option
const void *default_data; // Raw default value
};
// Config object
@@ -61,11 +59,6 @@ typedef struct m_config {
struct m_config_option *opts; // all options, even suboptions
int num_opts;
// Creation parameters
size_t size;
const void *defaults;
const struct m_option *options;
// List of defined profiles.
struct m_profile *profiles;
// Depth when recursively including profiles.
@@ -94,14 +87,17 @@ typedef struct m_config {
void *optstruct; // struct mpopts or other
int shadow_size;
// List of m_sub_options instances.
// Private. List of m_sub_options instances.
// Index 0 is the top-level and is always present.
// Immutable after init.
// Invariant: a parent is always at a lower index than any of its children.
struct m_config_group *groups;
int num_groups;
// Thread-safe shadow memory; only set for the main m_config.
// Private. Non-NULL if data was allocated. m_config_option.data uses it.
struct m_config_data *data;
// Private. Thread-safe shadow memory; only set for the main m_config.
struct m_config_shadow *shadow;
} m_config_t;
@@ -182,6 +178,8 @@ struct m_config_option *m_config_get_co(const struct m_config *config,
int m_config_get_co_count(struct m_config *config);
struct m_config_option *m_config_get_co_index(struct m_config *config, int index);
const void *m_config_get_co_default(const struct m_config *config,
struct m_config_option *co);
// Return the n-th option by position. n==0 is the first option. If there are
// less than (n + 1) options, return NULL.
@@ -264,14 +262,13 @@ struct mpv_node m_config_get_profiles(struct m_config *config);
// the cache itself is allowed.
struct m_config_cache {
// The struct as indicated by m_config_cache_alloc's group parameter.
// (Internally the same as data->gdata[0]->udata.)
void *opts;
// Internal.
struct m_config_shadow *shadow;
struct m_config *shadow_config;
long long ts;
int group;
bool in_list;
struct m_config_shadow *shadow; // real data
struct m_config_data *data; // copy for the cache user
bool in_list; // registered as listener with root config
// --- Implicitly synchronized by setting/unsetting wakeup_cb.
struct mp_dispatch_queue *wakeup_dispatch_queue;
void (*wakeup_dispatch_cb)(void *ctx);
@@ -281,15 +278,17 @@ struct m_config_cache {
void *wakeup_cb_ctx;
};
#define GLOBAL_CONFIG NULL
// Create a mirror copy from the global options.
// Keep in mind that a m_config_cache object is not thread-safe; it merely
// provides thread-safe access to the global options. All API functions for
// the same m_config_cache object must synchronized, unless otherwise noted.
// ta_parent: parent for the returned allocation
// global: option data source
// group: the option group to return. This can be NULL for the global option
// struct (MPOpts), or m_sub_options used in a certain OPT_SUBSTRUCT()
// item.
// group: the option group to return. This can be GLOBAL_CONFIG for the global
// option struct (MPOpts), or m_sub_options used in a certain
// OPT_SUBSTRUCT() item.
struct m_config_cache *m_config_cache_alloc(void *ta_parent,
struct mpv_global *global,
const struct m_sub_options *group);
@@ -320,6 +319,7 @@ bool m_config_cache_update(struct m_config_cache *cache);
// Like m_config_cache_alloc(), but return the struct (m_config_cache->opts)
// directly, with no way to update the config. Basically this returns a copy
// with a snapshot of the current option values.
// group==GLOBAL_CONFIG is a special case, and always returns the root group.
void *mp_get_config_group(void *ta_parent, struct mpv_global *global,
const struct m_sub_options *group);

View File

@@ -564,6 +564,7 @@ extern const char m_option_path_separator;
#define OPTDEF_STR(s) .defval = (void *)&(char * const){s}
#define OPTDEF_INT(i) .defval = (void *)&(const int){i}
#define OPTDEF_INT64(i) .defval = (void *)&(const int64_t){i}
#define OPTDEF_FLOAT(f) .defval = (void *)&(const float){f}
#define OPTDEF_DOUBLE(d) .defval = (void *)&(const double){d}

View File

@@ -59,7 +59,6 @@ extern const struct m_sub_options tv_params_conf;
extern const struct m_sub_options stream_cdda_conf;
extern const struct m_sub_options stream_dvb_conf;
extern const struct m_sub_options stream_lavf_conf;
extern const struct m_sub_options stream_cache_conf;
extern const struct m_sub_options sws_conf;
extern const struct m_sub_options drm_conf;
extern const struct m_sub_options demux_rawaudio_conf;
@@ -78,7 +77,8 @@ extern const struct m_sub_options demux_conf;
extern const struct m_obj_list vf_obj_list;
extern const struct m_obj_list af_obj_list;
extern const struct m_obj_list vo_obj_list;
extern const struct m_obj_list ao_obj_list;
extern const struct m_sub_options ao_conf;
extern const struct m_sub_options opengl_conf;
extern const struct m_sub_options vulkan_conf;
@@ -386,8 +386,6 @@ const m_option_t mp_opts[] = {
// ------------------------- stream options --------------------
OPT_SUBSTRUCT("", stream_cache, stream_cache_conf, 0),
#if HAVE_DVDREAD || HAVE_DVDNAV
OPT_SUBSTRUCT("", dvd_opts, dvd_conf, 0),
#endif /* HAVE_DVDREAD */
@@ -461,6 +459,7 @@ const m_option_t mp_opts[] = {
OPT_STRING("audio-demuxer", audio_demuxer_name, 0),
OPT_STRING("sub-demuxer", sub_demuxer_name, 0),
OPT_FLAG("demuxer-thread", demuxer_thread, 0),
OPT_DOUBLE("demuxer-termination-timeout", demux_termination_timeout, 0),
OPT_FLAG("prefetch-playlist", prefetch_open, 0),
OPT_FLAG("cache-pause", cache_pause, 0),
OPT_FLAG("cache-pause-initial", cache_pause_initial, 0),
@@ -509,18 +508,13 @@ const m_option_t mp_opts[] = {
OPT_STRING("audio-spdif", audio_spdif, 0),
OPT_STRING_VALIDATE("hwdec", hwdec_api, M_OPT_OPTIONAL_PARAM,
hwdec_validate_opt),
OPT_STRING("hwdec-codecs", hwdec_codecs, 0),
OPT_IMAGEFORMAT("hwdec-image-format", hwdec_image_format, 0, .min = -1),
// -1 means auto aspect (prefer container size until aspect change)
// 0 means square pixels
OPT_ASPECT("video-aspect", movie_aspect, UPDATE_IMGPAR, -1.0, 10.0),
OPT_CHOICE("video-aspect-method", aspect_method, UPDATE_IMGPAR,
({"bitstream", 1}, {"container", 2})),
OPT_SUBSTRUCT("vd-lavc", vd_lavc_params, vd_lavc_conf, 0),
OPT_SUBSTRUCT("", vd_lavc_params, vd_lavc_conf, 0),
OPT_SUBSTRUCT("ad-lavc", ad_lavc_params, ad_lavc_conf, 0),
OPT_SUBSTRUCT("", demux_lavf, demux_lavf_conf, 0),
@@ -548,10 +542,8 @@ const m_option_t mp_opts[] = {
OPT_FLAG("osd-bar", osd_bar_visible, UPDATE_OSD),
//---------------------- libao/libvo options ------------------------
OPT_SETTINGSLIST("ao", audio_driver_list, 0, &ao_obj_list, ),
OPT_STRING("audio-device", audio_device, UPDATE_AUDIO),
OPT_SUBSTRUCT("", ao_opts, ao_conf, 0),
OPT_FLAG("audio-exclusive", audio_exclusive, UPDATE_AUDIO),
OPT_STRING("audio-client-name", audio_client_name, UPDATE_AUDIO),
OPT_FLAG("audio-fallback-to-null", ao_null_fallback, 0),
OPT_FLAG("audio-stream-silence", audio_stream_silence, 0),
OPT_FLOATRANGE("audio-wait-open", audio_wait_open, 0, 0, 60),
@@ -576,8 +568,6 @@ const m_option_t mp_opts[] = {
({"no", 0},
{"yes", 1},
{"weak", -1})),
OPT_DOUBLE("audio-buffer", audio_buffer, M_OPT_MIN | M_OPT_MAX,
.min = 0, .max = 10),
OPT_STRING("title", wintitle, 0),
OPT_STRING("force-media-title", media_title, 0),
@@ -878,16 +868,12 @@ const m_option_t mp_opts[] = {
const struct MPOpts mp_default_opts = {
.use_terminal = 1,
.msg_color = 1,
.audio_driver_list = NULL,
.audio_decoders = NULL,
.video_decoders = NULL,
.softvol_max = 130,
.softvol_volume = 100,
.softvol_mute = 0,
.gapless_audio = -1,
.audio_buffer = 0.2,
.audio_device = "auto",
.audio_client_name = "mpv",
.wintitle = "${?media-title:${media-title}}${!media-title:No file} - mpv",
.stop_screensaver = 1,
.cursor_autohide_delay = 1000,
@@ -915,6 +901,7 @@ const struct MPOpts mp_default_opts = {
.position_resume = 1,
.autoload_files = 1,
.demuxer_thread = 1,
.demux_termination_timeout = 0.1,
.hls_bitrate = INT_MAX,
.cache_pause = 1,
.cache_pause_wait = 1.0,
@@ -952,9 +939,6 @@ const struct MPOpts mp_default_opts = {
.osd_bar_visible = 1,
.screenshot_template = "mpv-shot%n",
.hwdec_api = HAVE_RPI ? "mmal" : "no",
.hwdec_codecs = "h264,vc1,wmv3,hevc,mpeg2video,vp9",
.audio_output_channels = {
.set = 1,
.auto_safe = 1,

View File

@@ -59,16 +59,6 @@ typedef struct mp_vo_opts {
struct drm_opts *drm_opts;
} mp_vo_opts;
struct mp_cache_opts {
int size;
int def_size;
int initial;
int seek_min;
int back_buffer;
char *file;
int file_max;
};
// Subtitle options needed by the subtitle decoders/renderers.
struct mp_subtitle_opts {
int sub_visibility;
@@ -144,10 +134,7 @@ typedef struct MPOpts {
int auto_load_scripts;
struct m_obj_settings *audio_driver_list;
char *audio_device;
int audio_exclusive;
char *audio_client_name;
int ao_null_fallback;
int audio_stream_silence;
float audio_wait_open;
@@ -160,9 +147,9 @@ typedef struct MPOpts {
int softvol_mute;
float softvol_max;
int gapless_audio;
double audio_buffer;
mp_vo_opts *vo;
struct ao_opts *ao_opts;
char *wintitle;
char *media_title;
@@ -207,7 +194,6 @@ typedef struct MPOpts {
char *force_configdir;
int use_filedir_conf;
int hls_bitrate;
struct mp_cache_opts *stream_cache;
int chapterrange[2];
int edition_id;
int correct_pts;
@@ -261,6 +247,7 @@ typedef struct MPOpts {
char **audio_files;
char *demuxer_name;
int demuxer_thread;
double demux_termination_timeout;
int prefetch_open;
char *audio_demuxer_name;
char *sub_demuxer_name;
@@ -295,10 +282,6 @@ typedef struct MPOpts {
int audiofile_auto;
int osd_bar_visible;
char *hwdec_api;
char *hwdec_codecs;
int hwdec_image_format;
int w32_priority;
struct tv_params *tv_params;
@@ -364,14 +347,10 @@ struct filter_opts {
extern const m_option_t mp_opts[];
extern const struct MPOpts mp_default_opts;
extern const struct m_sub_options vo_sub_opts;
extern const struct m_sub_options stream_cache_conf;
extern const struct m_sub_options dvd_conf;
extern const struct m_sub_options mp_subtitle_sub_opts;
extern const struct m_sub_options mp_osd_render_sub_opts;
extern const struct m_sub_options filter_conf;
extern const struct m_sub_options resample_conf;
int hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param);
#endif

View File

@@ -199,7 +199,7 @@ int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files,
if (bstrcmp0(p.arg, "playlist") == 0) {
// append the playlist to the local args
char *param0 = bstrdup0(NULL, p.param);
struct playlist *pl = playlist_parse_file(param0, global);
struct playlist *pl = playlist_parse_file(param0, NULL, global);
talloc_free(param0);
if (!pl) {
MP_FATAL(config, "Error reading playlist '%.*s'\n",
@@ -281,10 +281,8 @@ err_out:
* during normal options parsing.
*/
void m_config_preparse_command_line(m_config_t *config, struct mpv_global *global,
char **argv)
int *verbose, char **argv)
{
struct MPOpts *opts = global->opts;
struct parse_state p = {config, argv};
while (split_opt_silent(&p) == 0) {
if (p.is_opt) {
@@ -293,7 +291,7 @@ void m_config_preparse_command_line(m_config_t *config, struct mpv_global *globa
int flags = M_SETOPT_FROM_CMDLINE | M_SETOPT_PRE_PARSE_ONLY;
m_config_set_option_cli(config, p.arg, p.param, flags);
if (bstrcmp0(p.arg, "v") == 0)
opts->verbose++;
(*verbose)++;
}
}

View File

@@ -27,6 +27,6 @@ struct mpv_global;
int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files,
struct mpv_global *global, char **argv);
void m_config_preparse_command_line(m_config_t *config, struct mpv_global *global,
char **argv);
int *verbose, char **argv);
#endif /* MPLAYER_PARSER_MPCMD_H */

View File

@@ -61,6 +61,19 @@ static const char *const config_dirs[] = {
"global",
};
void mp_init_paths(struct mpv_global *global, struct MPOpts *opts)
{
TA_FREEP(&global->configdir);
const char *force_configdir = getenv("MPV_HOME");
if (opts->force_configdir && opts->force_configdir[0])
force_configdir = opts->force_configdir;
if (!opts->load_config)
force_configdir = "";
global->configdir = talloc_strdup(global, force_configdir);
}
// Return a platform specific path using a path type as defined in osdep/path.h.
// Keep in mind that the only way to free the return value is freeing talloc_ctx
// (or its children), as this function can return a statically allocated string.
@@ -70,15 +83,10 @@ static const char *mp_get_platform_path(void *talloc_ctx,
{
assert(talloc_ctx);
const char *force_configdir = getenv("MPV_HOME");
if (global->opts->force_configdir && global->opts->force_configdir[0])
force_configdir = global->opts->force_configdir;
if (!global->opts->load_config)
force_configdir = "";
if (force_configdir) {
if (global->configdir) {
for (int n = 0; n < MP_ARRAY_SIZE(config_dirs); n++) {
if (strcmp(config_dirs[n], type) == 0)
return (n == 0 && force_configdir[0]) ? force_configdir : NULL;
return (n == 0 && global->configdir[0]) ? global->configdir : NULL;
}
}

View File

@@ -24,6 +24,9 @@
#include "misc/bstr.h"
struct mpv_global;
struct MPOpts;
void mp_init_paths(struct mpv_global *global, struct MPOpts *opts);
// Search for the input filename in several paths. These include user and global
// config locations by default. Some platforms may implement additional platform