1
mirror of https://github.com/mpv-player/mpv synced 2025-01-01 04:36:24 +01:00

options: change option parsing to use bstr

Using bstr allows simpler parsing code, especially because it avoids
the need to modify or copy strings just to terminate extracted
substrings.
This commit is contained in:
Uoti Urpala 2011-07-28 11:07:47 +03:00
parent d8374376c0
commit e873d703e9
17 changed files with 698 additions and 703 deletions

49
bstr.c
View File

@ -20,6 +20,7 @@
#include <libavutil/avutil.h>
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include "talloc.h"
@ -71,6 +72,15 @@ int bstrrchr(struct bstr str, int c)
return -1;
}
int bstrcspn(struct bstr str, const char *reject)
{
int i;
for (i = 0; i < str.len; i++)
if (strchr(reject, str.start[i]))
break;
return i;
}
int bstr_find(struct bstr haystack, struct bstr needle)
{
for (int i = 0; i < haystack.len; i++)
@ -96,21 +106,18 @@ struct bstr bstr_strip(struct bstr str)
return str;
}
struct bstr bstr_split(struct bstr str, char *sep, struct bstr *rest)
struct bstr bstr_split(struct bstr str, const char *sep, struct bstr *rest)
{
int start, end;
int start;
for (start = 0; start < str.len; start++)
if (!strchr(sep, str.start[start]))
break;
for (end = start; end < str.len; end++)
if (strchr(sep, str.start[end]))
break;
str = bstr_cut(str, start);
int end = bstrcspn(str, sep);
if (rest) {
*rest = bstr_cut(str, end);
}
str.start += start;
str.len = end - start;
return str;
return bstr_splice(str, 0, end);
}
@ -131,6 +138,7 @@ struct bstr bstr_splice(struct bstr str, int start, int end)
long long bstrtoll(struct bstr str, struct bstr *rest, int base)
{
str = bstr_lstrip(str);
char buf[51];
int len = FFMIN(str.len, 50);
memcpy(buf, str.start, len);
@ -142,6 +150,20 @@ long long bstrtoll(struct bstr str, struct bstr *rest, int base)
return r;
}
double bstrtod(struct bstr str, struct bstr *rest)
{
str = bstr_lstrip(str);
char buf[101];
int len = FFMIN(str.len, 100);
memcpy(buf, str.start, len);
buf[len] = 0;
char *endptr;
double r = strtod(buf, &endptr);
if (rest)
*rest = bstr_cut(str, endptr - buf);
return r;
}
struct bstr *bstr_splitlines(void *talloc_ctx, struct bstr str)
{
if (str.len == 0)
@ -169,3 +191,14 @@ void bstr_lower(struct bstr str)
for (int i = 0; i < str.len; i++)
str.start[i] = tolower(str.start[i]);
}
int bstr_sscanf(struct bstr str, const char *format, ...)
{
char *ptr = bstrdup0(NULL, str);
va_list va;
va_start(va, format);
int ret = vsscanf(ptr, format, va);
va_end(va);
talloc_free(ptr);
return ret;
}

89
bstr.h
View File

@ -34,32 +34,6 @@ struct bstr {
size_t len;
};
int bstrcmp(struct bstr str1, struct bstr str2);
int bstrcasecmp(struct bstr str1, struct bstr str2);
int bstrchr(struct bstr str, int c);
int bstrrchr(struct bstr str, int c);
int bstr_find(struct bstr haystack, struct bstr needle);
struct bstr *bstr_splitlines(void *talloc_ctx, struct bstr str);
struct bstr bstr_lstrip(struct bstr str);
struct bstr bstr_strip(struct bstr str);
struct bstr bstr_split(struct bstr str, char *sep, struct bstr *rest);
struct bstr bstr_splice(struct bstr str, int start, int end);
long long bstrtoll(struct bstr str, struct bstr *rest, int base);
void bstr_lower(struct bstr str);
static inline struct bstr bstr_cut(struct bstr str, int n)
{
return (struct bstr){str.start + n, str.len - n};
}
static inline bool bstr_startswith(struct bstr str, struct bstr prefix)
{
if (str.len < prefix.len)
return false;
return !memcmp(str.start, prefix.start, prefix.len);
}
// demux_rtp.cpp (live555) C++ compilation workaround
#ifndef __cplusplus
static inline char *bstrdup0(void *talloc_ctx, struct bstr str)
@ -78,6 +52,69 @@ static inline struct bstr bstr(const unsigned char *s)
return (struct bstr){(unsigned char *)s, s ? strlen(s) : 0};
}
int bstrcmp(struct bstr str1, struct bstr str2);
int bstrcasecmp(struct bstr str1, struct bstr str2);
int bstrchr(struct bstr str, int c);
int bstrrchr(struct bstr str, int c);
int bstrcspn(struct bstr str, const char *reject);
int bstr_find(struct bstr haystack, struct bstr needle);
struct bstr *bstr_splitlines(void *talloc_ctx, struct bstr str);
struct bstr bstr_lstrip(struct bstr str);
struct bstr bstr_strip(struct bstr str);
struct bstr bstr_split(struct bstr str, const char *sep, struct bstr *rest);
struct bstr bstr_splice(struct bstr str, int start, int end);
long long bstrtoll(struct bstr str, struct bstr *rest, int base);
double bstrtod(struct bstr str, struct bstr *rest);
void bstr_lower(struct bstr str);
int bstr_sscanf(struct bstr str, const char *format, ...);
static inline struct bstr bstr_cut(struct bstr str, int n)
{
if (n > str.len)
n = str.len;
return (struct bstr){str.start + n, str.len - n};
}
static inline bool bstr_startswith(struct bstr str, struct bstr prefix)
{
if (str.len < prefix.len)
return false;
return !memcmp(str.start, prefix.start, prefix.len);
}
static inline bool bstr_startswith0(struct bstr str, const char *prefix)
{
return bstr_startswith(str, bstr(prefix));
}
static inline bool bstr_endswith(struct bstr str, struct bstr suffix)
{
if (str.len < suffix.len)
return false;
return !memcmp(str.start + str.len - suffix.len, suffix.start, suffix.len);
}
static inline bool bstr_endswith0(struct bstr str, const char *suffix)
{
return bstr_endswith(str, bstr(suffix));
}
static inline int bstrcmp0(struct bstr str1, const char *str2)
{
return bstrcmp(str1, bstr(str2));
}
static inline int bstrcasecmp0(struct bstr str1, const char *str2)
{
return bstrcasecmp(str1, bstr(str2));
}
static inline int bstr_find0(struct bstr haystack, const char *needle)
{
return bstr_find(haystack, bstr(needle));
}
#endif
// create a pair (not single value!) for "%.*s" printf syntax

View File

@ -232,7 +232,8 @@ static int mp_property_generic_option(struct m_option *prop, int action,
void *arg, MPContext *mpctx)
{
char *optname = prop->priv;
const struct m_option *opt = m_config_get_option(mpctx->mconfig, optname);
const struct m_option *opt = m_config_get_option(mpctx->mconfig,
bstr(optname));
void *valptr = m_option_get_ptr(opt, &mpctx->opts);
switch (action) {

View File

@ -208,7 +208,8 @@ static int menu_parse_config(char* buffer, struct m_config *mconfig)
// Setup the attribs
for(i = 0 ; attribs[2*i] ; i++) {
if(strcasecmp(attribs[2*i],"name") == 0) continue;
if(!m_struct_set(&minfo->priv_st,menu_list[menu_count].cfg,attribs[2*i], attribs[2*i+1]))
if (!m_struct_set(&minfo->priv_st, menu_list[menu_count].cfg,
attribs[2*i], bstr(attribs[2*i+1])))
mp_tmsg(MSGT_GLOBAL,MSGL_WARN,"[MENU] bad attribute %s=%s in menu '%s' at line %d\n",attribs[2*i],attribs[2*i+1],
name,parser->line);
}

View File

@ -455,7 +455,7 @@ struct vf_instance *vf_open_plugin_noerr(struct MPOpts *opts,
void* vf_priv = m_struct_alloc(st);
int n;
for(n = 0 ; args && args[2*n] ; n++)
m_struct_set(st,vf_priv,args[2*n],args[2*n+1]);
m_struct_set(st, vf_priv, args[2*n], bstr(args[2*n+1]));
vf->priv = vf_priv;
args = NULL;
} else // Otherwise we should have the '_oldargs_'

View File

@ -36,13 +36,13 @@
#define MAX_PROFILE_DEPTH 20
static int parse_profile(const struct m_option *opt, const char *name,
const char *param, bool ambiguous_param, void *dst)
static int parse_profile(const struct m_option *opt, struct bstr name,
struct bstr param, bool ambiguous_param, void *dst)
{
struct m_config *config = opt->priv;
char **list = NULL;
int i, r;
if (param && !strcmp(param, "help")) {
if (!bstrcmp0(param, "help")) {
struct m_profile *p;
if (!config->profiles) {
mp_tmsg(MSGT_CFGPARSER, MSGL_INFO,
@ -387,36 +387,37 @@ int m_config_register_options(struct m_config *config,
}
static struct m_config_option *m_config_get_co(const struct m_config *config,
char *arg)
struct bstr name)
{
struct m_config_option *co;
for (co = config->opts; co; co = co->next) {
int l = strlen(co->name) - 1;
if ((co->opt->type->flags & M_OPT_TYPE_ALLOW_WILDCARD) &&
(co->name[l] == '*')) {
if (strncasecmp(co->name, arg, l) == 0)
struct bstr coname = bstr(co->name);
if ((co->opt->type->flags & M_OPT_TYPE_ALLOW_WILDCARD)
&& bstr_endswith0(coname, "*")) {
coname.len--;
if (bstrcasecmp(bstr_splice(name, 0, coname.len), coname) == 0)
return co;
} else if (strcasecmp(co->name, arg) == 0)
} else if (bstrcasecmp(coname, name) == 0)
return co;
}
return NULL;
}
static int m_config_parse_option(const struct m_config *config, char *arg,
char *param, bool ambiguous_param, bool set)
static int m_config_parse_option(const struct m_config *config,
struct bstr name, struct bstr param,
bool ambiguous_param, bool set)
{
struct m_config_option *co;
int r = 0;
assert(config != NULL);
assert(config->lvl > 0);
assert(arg != NULL);
assert(name.len != 0);
co = m_config_get_co(config, arg);
if (!co) {
co = m_config_get_co(config, name);
if (!co)
return M_OPT_UNKNOWN;
}
// This is the only mandatory function
assert(co->opt->type->parse);
@ -424,12 +425,14 @@ static int m_config_parse_option(const struct m_config *config, char *arg,
// Check if this option isn't forbidden in the current mode
if ((config->mode == M_CONFIG_FILE) && (co->opt->flags & M_OPT_NOCFG)) {
mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
"The %s option can't be used in a config file.\n", arg);
"The %.*s option can't be used in a config file.\n",
BSTR_P(name));
return M_OPT_INVALID;
}
if ((config->mode == M_COMMAND_LINE) && (co->opt->flags & M_OPT_NOCMD)) {
mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
"The %s option can't be used on the command line.\n", arg);
"The %.*s option can't be used on the command line.\n",
BSTR_P(name));
return M_OPT_INVALID;
}
// During command line preparse set only pre-parse options
@ -445,7 +448,7 @@ static int m_config_parse_option(const struct m_config *config, char *arg,
char **lst = NULL;
int i, sr;
// Parse the child options
r = m_option_parse(co->opt, arg, param, false, &lst);
r = m_option_parse(co->opt, name, param, false, &lst);
// Set them now
if (r >= 0)
for (i = 0; lst && lst[2 * i]; i++) {
@ -454,8 +457,9 @@ static int m_config_parse_option(const struct m_config *config, char *arg,
// Build the full name
char n[l];
sprintf(n, "%s:%s", co->name, lst[2 * i]);
sr = m_config_parse_option(config, n, lst[2 * i + 1],
false, set);
sr = m_config_parse_option(config, bstr(n),
bstr(lst[2 * i + 1]), false,
set);
if (sr < 0) {
if (sr == M_OPT_UNKNOWN) {
mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
@ -476,7 +480,7 @@ static int m_config_parse_option(const struct m_config *config, char *arg,
}
talloc_free(lst);
} else
r = m_option_parse(co->opt, arg, param, ambiguous_param,
r = m_option_parse(co->opt, name, param, ambiguous_param,
set ? co->slots->data : NULL);
// Parsing failed ?
@ -491,22 +495,24 @@ static int m_config_parse_option(const struct m_config *config, char *arg,
return r;
}
int m_config_set_option(struct m_config *config, char *arg,
char *param, bool ambiguous_param)
int m_config_set_option(struct m_config *config, struct bstr name,
struct bstr param, bool ambiguous_param)
{
mp_msg(MSGT_CFGPARSER, MSGL_DBG2, "Setting %s=%s\n", arg, param);
return m_config_parse_option(config, arg, param, ambiguous_param, 1);
mp_msg(MSGT_CFGPARSER, MSGL_DBG2, "Setting %.*s=%.*s\n", BSTR_P(name),
BSTR_P(param));
return m_config_parse_option(config, name, param, ambiguous_param, 1);
}
int m_config_check_option(const struct m_config *config, char *arg,
char *param, bool ambiguous_param)
int m_config_check_option(const struct m_config *config, struct bstr name,
struct bstr param, bool ambiguous_param)
{
int r;
mp_msg(MSGT_CFGPARSER, MSGL_DBG2, "Checking %s=%s\n", arg, param);
r = m_config_parse_option(config, arg, param, ambiguous_param, 0);
mp_msg(MSGT_CFGPARSER, MSGL_DBG2, "Checking %.*s=%.*s\n", BSTR_P(name),
BSTR_P(param));
r = m_config_parse_option(config, name, param, ambiguous_param, 0);
if (r == M_OPT_MISSING_PARAM) {
mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,
"Error: option '%s' must have a parameter!\n", arg);
"Error: option '%.*s' must have a parameter!\n", BSTR_P(name));
return M_OPT_INVALID;
}
return r;
@ -514,15 +520,14 @@ int m_config_check_option(const struct m_config *config, char *arg,
const struct m_option *m_config_get_option(const struct m_config *config,
char *arg)
struct bstr name)
{
struct m_config_option *co;
assert(config != NULL);
assert(config->lvl > 0);
assert(arg != NULL);
co = m_config_get_co(config, arg);
co = m_config_get_co(config, name);
if (co)
return co->opt;
else
@ -597,7 +602,7 @@ void m_profile_set_desc(struct m_profile *p, char *desc)
int m_config_set_profile_option(struct m_config *config, struct m_profile *p,
char *name, char *val)
{
int i = m_config_check_option(config, name, val, false);
int i = m_config_check_option0(config, name, val, false);
if (i < 0)
return i;
p->opts = talloc_realloc(p, p->opts, char *, 2 * (p->num_opts + 2));
@ -620,7 +625,7 @@ void m_config_set_profile(struct m_config *config, struct m_profile *p)
config->mode = M_CONFIG_FILE;
config->profile_depth++;
for (i = 0; i < p->num_opts; i++)
m_config_set_option(config, p->opts[2 * i], p->opts[2 * i + 1], false);
m_config_set_option0(config, p->opts[2 * i], p->opts[2 * i + 1], false);
config->profile_depth--;
config->mode = prev_mode;
}

View File

@ -21,6 +21,7 @@
#include <stdbool.h>
#include "bstr.h"
// m_config provides an API to manipulate the config variables in MPlayer.
// It makes use of the Options API to provide a context stack that
@ -129,28 +130,42 @@ int m_config_register_options(struct m_config *config,
/* Set an option.
* \param config The config object.
* \param arg The option's name.
* \param name The option's name.
* \param param The value of the option, can be NULL.
* \param ambiguous_param: old style cmdline option, "param" may be a
parameter to this option or something entirely unrelated
* \return See \ref OptionParserReturn.
*/
int m_config_set_option(struct m_config *config, char *arg,
char *param, bool ambiguous_param);
int m_config_set_option(struct m_config *config, struct bstr name,
struct bstr param, bool ambiguous_param);
static inline int m_config_set_option0(struct m_config *config,
const char *name, const char *param,
bool ambiguous)
{
return m_config_set_option(config, bstr(name), bstr(param), ambiguous);
}
/* Check if an option setting is valid.
* Same as above m_config_set_option() but doesn't actually set anything.
*/
int m_config_check_option(const struct m_config *config, char *arg,
char *param, bool ambiguous_param);
int m_config_check_option(const struct m_config *config, struct bstr name,
struct bstr param, bool ambiguous_param);
static inline int m_config_check_option0(struct m_config *config,
const char *name, const char *param,
bool ambiguous)
{
return m_config_check_option(config, bstr(name), bstr(param), ambiguous);
}
/* Get the option matching the given name.
* \param config The config object.
* \param arg The option's name.
* \param name The option's name.
*/
const struct m_option *m_config_get_option(const struct m_config *config,
char *arg);
struct bstr name);
/* Print a list of all registered options.
* \param config The config object.

1087
m_option.c

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@
#include <stdbool.h>
#include "config.h"
#include "bstr.h"
// m_option allows to parse, print and copy data of various types.
@ -217,7 +218,7 @@ struct m_option_type {
* \return On error a negative value is returned, on success the number
* of arguments consumed. For details see \ref OptionParserReturn.
*/
int (*parse)(const m_option_t *opt, const char *name, const char *param,
int (*parse)(const m_option_t *opt, struct bstr name, struct bstr param,
bool ambiguous_param, void *dst);
// Print back a value in string form.
@ -440,8 +441,8 @@ static inline void *m_option_get_ptr(const struct m_option *opt,
}
// Helper to parse options, see \ref m_option_type::parse.
static inline int m_option_parse(const m_option_t *opt, const char *name,
const char *param, bool ambiguous_param,
static inline int m_option_parse(const m_option_t *opt, struct bstr name,
struct bstr param, bool ambiguous_param,
void *dst)
{
return opt->type->parse(opt, name, param, ambiguous_param, dst);
@ -475,17 +476,6 @@ static inline void m_option_free(const m_option_t *opt, void *dst)
/*@}*/
/**
* Parse a string as a timestamp.
*
* @param[in] str the string to parse.
* @param[out] time parsed time.
* @param[in] endchar return an error of the next character after the
* timestamp is neither nul nor endchar.
* @return Number of chars in the timestamp.
*/
int parse_timestring(const char *str, double *time, char endchar);
#define OPTION_LIST_SEPARATOR ','
#if HAVE_DOS_PATHS

View File

@ -108,7 +108,8 @@ int m_property_do(const m_option_t *prop_list, const char *name,
if (!arg)
return M_PROPERTY_ERROR;
val = calloc(1, opt->type->size);
if ((r = m_option_parse(opt, opt->name, arg, false, val)) <= 0) {
if ((r = m_option_parse(opt, bstr(opt->name), bstr(arg), false,
val)) <= 0) {
free(val);
return r;
}

View File

@ -67,8 +67,9 @@ m_struct_alloc(const m_struct_t* st) {
return r;
}
int
m_struct_set(const m_struct_t* st, void* obj, const char* field, const char* param) {
int m_struct_set(const m_struct_t *st, void *obj, const char *field,
struct bstr param)
{
const m_option_t* f = m_struct_get_field(st,field);
if(!f) {
@ -77,9 +78,9 @@ m_struct_set(const m_struct_t* st, void* obj, const char* field, const char* par
return 0;
}
if(f->type->parse(f, field, param, false, M_ST_MB_P(obj,f->p)) < 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Struct %s, field %s parsing error: %s\n",
st->name,field,param);
if(f->type->parse(f, bstr(field), param, false, M_ST_MB_P(obj,f->p)) < 0) {
mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Struct %s, field %s parsing error: %.*s\n",
st->name, field, BSTR_P(param));
return 0;
}

View File

@ -19,6 +19,8 @@
#ifndef MPLAYER_M_STRUCT_H
#define MPLAYER_M_STRUCT_H
#include "bstr.h"
/// \defgroup OptionsStruct Options struct
/// \ingroup Options
/// An API to manipulate structs using m_option.
@ -87,8 +89,8 @@ m_struct_alloc(const m_struct_t* st);
* \param param New value of the field.
* \return 0 on error, 1 on success.
*/
int
m_struct_set(const m_struct_t* st, void* obj, const char* field, const char* param);
int m_struct_set(const m_struct_t *st, void *obj, const char *field,
struct bstr param);
/// Reset a field (or all if field == NULL) to defaults.
/** \param st Struct definition.

View File

@ -118,7 +118,7 @@ char myPsnStr[5+10+1+10+1];
myPsnStr[5+10+1+10]=0;
if((argc==2) && !strcmp(myPsnStr, argv[1])) {
m_config_set_option(config, "quiet", NULL, false);
m_config_set_option0(config, "quiet", NULL, false);
InitCursor();
AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP(AppleEventHandlerProc), 0, FALSE);
RunApplicationEventLoop();

View File

@ -233,7 +233,7 @@ int m_config_parse_config_file(m_config_t *config, const char *conffile)
tmp = m_config_set_profile_option(config, profile,
opt, param);
} else
tmp = m_config_set_option(config, opt, param, false);
tmp = m_config_set_option0(config, opt, param, false);
if (tmp < 0) {
PRINT_LINENUM;
if (tmp == M_OPT_UNKNOWN) {

View File

@ -183,15 +183,15 @@ play_tree_t *m_config_parse_mp_command_line(m_config_t *config, int argc,
} else if (mode == LOCAL) // Drop params for empty entry
mode = DROP_LOCAL;
} else if (tmp == 0) { // 'normal' options
mp_opt = m_config_get_option(config, opt);
mp_opt = m_config_get_option(config, bstr(opt));
if (mp_opt != NULL) { // Option exist
if (mode == GLOBAL || (mp_opt->flags & M_OPT_GLOBAL))
tmp = (i + 1 < argc)
? m_config_set_option(config, opt, argv[i + 1],
? m_config_set_option0(config, opt, argv[i + 1],
true)
: m_config_set_option(config, opt, NULL, false);
: m_config_set_option0(config, opt, NULL, false);
else {
tmp = m_config_check_option(config, opt,
tmp = m_config_check_option0(config, opt,
(i + 1 < argc) ? argv[i + 1] : NULL, true);
if (tmp >= 0 && mode != DROP_LOCAL) {
play_tree_t *pt =
@ -257,7 +257,7 @@ play_tree_t *m_config_parse_mp_command_line(m_config_t *config, int argc,
// Lock stdin if it will be used as input
if (strcasecmp(argv[i], "-") == 0)
m_config_set_option(config, "consolecontrols", "no", false);
m_config_set_option0(config, "consolecontrols", "no", false);
add_entry(&last_parent, &last_entry, entry);
mode = LOCAL; // We start entry specific options
}
@ -301,13 +301,13 @@ int m_config_preparse_command_line(m_config_t *config, int argc, char **argv)
break;
arg++;
opt = m_config_get_option(config, arg);
opt = m_config_get_option(config, bstr(arg));
// Ignore invalid option
if (!opt)
continue;
// Set, non-pre-parse options will be ignored
int r = m_config_set_option(config, arg, i+1 < argc ? argv[i+1] : NULL,
true);
int r = m_config_set_option0(config, arg, i+1 < argc ? argv[i+1] : NULL,
true);
if (r < 0)
ret = r;
else

View File

@ -465,8 +465,8 @@ play_tree_iter_push_params(play_tree_iter_t* iter) {
for(n = 0; pt->params[n].name != NULL ; n++) {
int e;
if((e = m_config_set_option(iter->config, pt->params[n].name,
pt->params[n].value, false)) < 0) {
if((e = m_config_set_option0(iter->config, pt->params[n].name,
pt->params[n].value, false)) < 0) {
mp_msg(MSGT_PLAYTREE,MSGL_ERR,"Error %d while setting option '%s' with value '%s'\n",e,
pt->params[n].name,pt->params[n].value);
}

View File

@ -166,7 +166,7 @@ static stream_t *open_stream_plugin(const stream_info_t *sinfo,
if(sinfo->opts_url) {
m_option_t url_opt =
{ "stream url", arg , CONF_TYPE_CUSTOM_URL, 0, 0 ,0, (void *)sinfo->opts };
if (m_option_parse(&url_opt, "stream url", filename, false, arg) < 0) {
if (m_option_parse(&url_opt, bstr("stream url"), bstr(filename), false, arg) < 0) {
mp_tmsg(MSGT_OPEN,MSGL_ERR, "URL parsing failed on url %s\n",filename);
m_struct_free(desc,arg);
return NULL;