static int open_f(stream_t *stream) { stream->fill_buffer = fill_buffer; stream->seek = seek; stream->seekable = true; stream->control = control; stream->read_chunk = 1024 * 1024; struct priv *p = talloc_zero(stream, struct priv); stream->priv = p; // Initial data bstr data = bstr0(stream->url); bool use_hex = bstr_eatstart0(&data, "hex://"); if (!use_hex) bstr_eatstart0(&data, "memory://"); stream_control(stream, STREAM_CTRL_SET_CONTENTS, &data); if (use_hex && !bstr_decode_hex(stream, p->data, &p->data)) { MP_FATAL(stream, "Invalid data.\n"); return STREAM_ERROR; } return STREAM_OK; }
// Select a decoder from the given list for the given codec. The selection // can be influenced by the selection string, which can specify a priority // list of preferred decoders. // This returns a list of decoders to try, with the preferred decoders first. // The selection string corresponds to --vd/--ad directly, and has the // following syntax: // selection = [<entry> ("," <entry>)*] // entry = <family> ":" <decoder> // prefer decoder // entry = <family> ":*" // prefer all decoders // entry = "+" <family> ":" <decoder> // force a decoder // entry = "-" <family> ":" <decoder> // exclude a decoder // entry = "-" // don't add fallback decoders // Forcing a decoder means it's added even if the codec mismatches. struct mp_decoder_list *mp_select_decoders(struct mp_decoder_list *all, const char *codec, const char *selection) { struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list); struct mp_decoder_list *remove = talloc_zero(NULL, struct mp_decoder_list); if (!codec) codec = "unknown"; bool stop = false; bstr sel = bstr0(selection); while (sel.len) { bstr entry; bstr_split_tok(sel, ",", &entry, &sel); if (bstr_equals0(entry, "-")) { stop = true; break; } bool force = bstr_eatstart0(&entry, "+"); bool exclude = !force && bstr_eatstart0(&entry, "-"); struct mp_decoder_list *dest = exclude ? remove : list; bstr family, decoder; if (!bstr_split_tok(entry, ":", &family, &decoder)) { mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Decoders must be specified as " "'family:decoder' for the --ad/--vd options.\n"); break; } if (bstr_equals0(decoder, "*")) { for (int n = 0; n < all->num_entries; n++) { struct mp_decoder_entry *cur = &all->entries[n]; if (bstr_equals0(family, cur->family)) add_new(dest, cur, codec); } } else { add_new(dest, find_decoder(all, family, decoder), force ? NULL : codec); } } if (!stop) { // Add the remaining codecs which haven't been added yet for (int n = 0; n < all->num_entries; n++) add_new(list, &all->entries[n], codec); } for (int n = 0; n < remove->num_entries; n++) { struct mp_decoder_entry *ex = &remove->entries[n]; struct mp_decoder_entry *del = find_decoder(list, bstr0(ex->family), bstr0(ex->decoder)); if (del) { int index = del - &list->entries[0]; MP_TARRAY_REMOVE_AT(list->entries, list->num_entries, index); } } talloc_free(remove); return list; }
static int parse_ref_init(struct pl_parser *p) { bstr line = bstr_strip(pl_get_line(p)); if (!bstr_equals0(line, "[Reference]")) return -1; // ASF http streaming redirection - this is needed because ffmpeg http:// // and mmsh:// can not automatically switch automatically between each // others. Both protocols use http - MMSH requires special http headers // to "activate" it, and will in other cases return this playlist. static const char *const mmsh_types[] = {"audio/x-ms-wax", "audio/x-ms-wma", "video/x-ms-asf", "video/x-ms-afs", "video/x-ms-wmv", "video/x-ms-wma", "application/x-mms-framed", "application/vnd.ms.wms-hdr.asfv1", NULL}; bstr burl = bstr0(p->s->url); if (bstr_eatstart0(&burl, "http://") && check_mimetype(p->s, mmsh_types)) { MP_INFO(p, "Redirecting to mmsh://\n"); playlist_add_file(p->pl, talloc_asprintf(p, "mmsh://%.*s", BSTR_P(burl))); return 0; } while (!pl_eof(p)) { line = bstr_strip(pl_get_line(p)); if (bstr_case_startswith(line, bstr0("Ref"))) { bstr_split_tok(line, "=", &(bstr){0}, &line); if (line.len) pl_add(p, line); } } return 0; }
// Returns 0 if a valid option/file is available, <0 on error, 1 on end of args. static int split_opt_silent(struct parse_state *p) { assert(!p->error); if (p->argc < 1) return 1; p->is_opt = false; p->arg = bstr0(p->argv[0]); p->param = bstr0(NULL); p->argc--; p->argv++; if (p->no_more_opts || !bstr_startswith0(p->arg, "-") || p->arg.len == 1) return 0; if (bstrcmp0(p->arg, "--") == 0) { p->no_more_opts = true; return split_opt_silent(p); } p->is_opt = true; if (!bstr_eatstart0(&p->arg, "--")) bstr_eatstart0(&p->arg, "-"); bool ambiguous = !bstr_split_tok(p->arg, "=", &p->arg, &p->param); int r = m_config_option_requires_param(p->config, p->arg); if (r < 0) return r; if (ambiguous && r > 0) { if (p->argc < 1) return M_OPT_MISSING_PARAM; p->param = bstr0(p->argv[0]); p->argc--; p->argv++; } return 0; }
static int parse_m3u(struct pl_parser *p) { bstr line = bstr_strip(pl_get_line(p)); if (p->probing && !bstr_equals0(line, "#EXTM3U")) { // Last resort: if the file extension is m3u, it might be headerless. if (p->check_level == DEMUX_CHECK_UNSAFE) { char *ext = mp_splitext(p->real_stream->url, NULL); bstr data = stream_peek(p->real_stream, PROBE_SIZE); if (ext && data.len > 10 && maybe_text(data)) { const char *exts[] = {"m3u", "m3u8", NULL}; for (int n = 0; exts[n]; n++) { if (strcasecmp(ext, exts[n]) == 0) goto ok; } } } return -1; } ok: if (p->probing) return 0; char *title = NULL; while (line.len || !pl_eof(p)) { if (bstr_eatstart0(&line, "#EXTINF:")) { bstr duration, btitle; if (bstr_split_tok(line, ",", &duration, &btitle) && btitle.len) { talloc_free(title); title = bstrto0(NULL, btitle); } } else if (bstr_startswith0(line, "#EXT-X-")) { p->format = "hls"; } else if (line.len > 0 && !bstr_startswith0(line, "#")) { char *fn = bstrto0(NULL, line); struct playlist_entry *e = playlist_entry_new(fn); talloc_free(fn); e->title = talloc_steal(e, title); title = NULL; playlist_add(p->pl, e); } line = bstr_strip(pl_get_line(p)); } talloc_free(title); return 0; }
static int open_f(stream_t *stream, int mode, void* opts, int* file_format) { stream->type = STREAMTYPE_MEMORY; stream->fill_buffer = fill_buffer; stream->seek = seek; stream->control = control; stream->read_chunk = 1024 * 1024; struct priv *p = talloc_zero(stream, struct priv); stream->priv = p; // Initial data bstr data = bstr0(stream->url); bstr_eatstart0(&data, "memory://"); stream_control(stream, STREAM_CTRL_SET_CONTENTS, &data); return STREAM_OK; }
// Load options and profiles from from a config file. // conffile: path to the config file // initial_section: default section where to add normal options // flags: M_SETOPT_* bits // returns: 1 on sucess, -1 on error, 0 if file not accessible. int m_config_parse_config_file(m_config_t *config, const char *conffile, char *initial_section, int flags) { #define PRINT_LINENUM MP_ERR(config, "%s:%d: ", conffile, line_num) #define MAX_LINE_LEN 10000 #define MAX_OPT_LEN 1000 #define MAX_PARAM_LEN 1500 FILE *fp = NULL; char *line = NULL; char opt[MAX_OPT_LEN + 1]; char param[MAX_PARAM_LEN + 1]; char c; /* for the "" and '' check */ int tmp; int line_num = 0; int line_pos; /* line pos */ int opt_pos; /* opt pos */ int param_pos; /* param pos */ int ret = 1; int errors = 0; m_profile_t *profile = m_config_add_profile(config, initial_section); flags = flags | M_SETOPT_FROM_CONFIG_FILE; MP_VERBOSE(config, "Reading config file %s\n", conffile); if (config->recursion_depth > MAX_RECURSION_DEPTH) { MP_ERR(config, "Maximum 'include' nesting depth exceeded.\n"); ret = -1; goto out; } if ((line = malloc(MAX_LINE_LEN + 1)) == NULL) { ret = -1; goto out; } else MP_VERBOSE(config, "\n"); if ((fp = fopen(conffile, "r")) == NULL) { MP_VERBOSE(config, "Can't open config file: %s\n", strerror(errno)); ret = 0; goto out; } while (fgets(line, MAX_LINE_LEN, fp)) { if (errors >= 16) { MP_FATAL(config, "too many errors\n"); goto out; } line_num++; line_pos = 0; /* skip BOM */ if (strncmp(line, "\xEF\xBB\xBF", 3) == 0) line_pos += 3; /* skip whitespaces */ while (mp_isspace(line[line_pos])) ++line_pos; /* EOL / comment */ if (line[line_pos] == '\0' || line[line_pos] == '#') continue; /* read option. */ for (opt_pos = 0; mp_isprint(line[line_pos]) && line[line_pos] != ' ' && line[line_pos] != '#' && line[line_pos] != '='; /* NOTHING */) { opt[opt_pos++] = line[line_pos++]; if (opt_pos >= MAX_OPT_LEN) { PRINT_LINENUM; MP_ERR(config, "option name too long\n"); errors++; ret = -1; goto nextline; } } if (opt_pos == 0) { PRINT_LINENUM; MP_ERR(config, "parse error\n"); ret = -1; errors++; continue; } opt[opt_pos] = '\0'; /* Profile declaration */ if (opt_pos > 2 && opt[0] == '[' && opt[opt_pos - 1] == ']') { opt[opt_pos - 1] = '\0'; profile = m_config_add_profile(config, opt + 1); continue; } /* skip whitespaces */ while (mp_isspace(line[line_pos])) ++line_pos; param_pos = 0; bool param_set = false; /* check '=' */ if (line[line_pos] == '=') { line_pos++; param_set = true; /* whitespaces... */ while (mp_isspace(line[line_pos])) ++line_pos; /* read the parameter */ if (line[line_pos] == '"' || line[line_pos] == '\'') { c = line[line_pos]; ++line_pos; for (param_pos = 0; line[line_pos] != c; /* NOTHING */) { param[param_pos++] = line[line_pos++]; if (param_pos >= MAX_PARAM_LEN) { PRINT_LINENUM; MP_ERR(config, "option %s has a too long parameter\n", opt); ret = -1; errors++; goto nextline; } } line_pos++; /* skip the closing " or ' */ goto param_done; } if (line[line_pos] == '%') { char *start = &line[line_pos + 1]; char *end = start; unsigned long len = strtoul(start, &end, 10); if (start != end && end[0] == '%') { if (len >= MAX_PARAM_LEN - 1 || strlen(end + 1) < len) { PRINT_LINENUM; MP_ERR(config, "bogus %% length\n"); ret = -1; errors++; goto nextline; } param_pos = snprintf(param, sizeof(param), "%.*s", (int)len, end + 1); line_pos += 1 + (end - start) + 1 + len; goto param_done; } } for (param_pos = 0; mp_isprint(line[line_pos]) && !mp_isspace(line[line_pos]) && line[line_pos] != '#'; /* NOTHING */) { param[param_pos++] = line[line_pos++]; if (param_pos >= MAX_PARAM_LEN) { PRINT_LINENUM; MP_ERR(config, "too long parameter\n"); ret = -1; errors++; goto nextline; } } param_done: while (mp_isspace(line[line_pos])) ++line_pos; } param[param_pos] = '\0'; /* EOL / comment */ if (line[line_pos] != '\0' && line[line_pos] != '#') { PRINT_LINENUM; MP_ERR(config, "extra characters: %s\n", line + line_pos); ret = -1; } bstr bopt = bstr0(opt); bstr bparam = bstr0(param); if (bopt.len >= 3) bstr_eatstart0(&bopt, "--"); if (profile && bstr_equals0(bopt, "profile-desc")) { m_profile_set_desc(profile, bparam); goto nextline; } bool need_param = m_config_option_requires_param(config, bopt) > 0; if (need_param && !param_set) { PRINT_LINENUM; MP_ERR(config, "error parsing option %.*s=%.*s: %s\n", BSTR_P(bopt), BSTR_P(bparam), m_option_strerror(M_OPT_MISSING_PARAM)); continue; } if (profile) { tmp = m_config_set_profile_option(config, profile, bopt, bparam); } else { tmp = m_config_set_option_ext(config, bopt, bparam, flags); } if (tmp < 0) { PRINT_LINENUM; MP_ERR(config, "setting option %.*s='%.*s' failed.\n", BSTR_P(bopt), BSTR_P(bparam)); continue; /* break */ } nextline: ; } out: free(line); if (fp) fclose(fp); config->recursion_depth -= 1; if (ret < 0) { MP_FATAL(config, "Error loading config file %s.\n", conffile); } return ret; }
int m_config_parse(m_config_t *config, const char *location, bstr data, char *initial_section, int flags) { m_profile_t *profile = m_config_add_profile(config, initial_section); void *tmp = talloc_new(NULL); int line_no = 0; int errors = 0; bstr_eatstart0(&data, "\xEF\xBB\xBF"); // skip BOM while (data.len) { talloc_free_children(tmp); bool ok = false; line_no++; char loc[512]; snprintf(loc, sizeof(loc), "%s:%d:", location, line_no); bstr line = bstr_strip_linebreaks(bstr_getline(data, &data)); if (!skip_ws(&line)) continue; // Profile declaration if (bstr_eatstart0(&line, "[")) { bstr profilename; if (!bstr_split_tok(line, "]", &profilename, &line)) { MP_ERR(config, "%s missing closing ]\n", loc); goto error; } if (skip_ws(&line)) { MP_ERR(config, "%s unparseable extra characters: '%.*s'\n", loc, BSTR_P(line)); goto error; } profile = m_config_add_profile(config, bstrto0(tmp, profilename)); continue; } bstr_eatstart0(&line, "--"); bstr option = line; while (line.len && (mp_isalnum(line.start[0]) || line.start[0] == '_' || line.start[0] == '-')) line = bstr_cut(line, 1); option.len = option.len - line.len; skip_ws(&line); bstr value = {0}; if (bstr_eatstart0(&line, "=")) { skip_ws(&line); if (line.len && (line.start[0] == '"' || line.start[0] == '\'')) { // Simple quoting, like "value" char term[2] = {line.start[0], 0}; line = bstr_cut(line, 1); if (!bstr_split_tok(line, term, &value, &line)) { MP_ERR(config, "%s unterminated quote\n", loc); goto error; } } else if (bstr_eatstart0(&line, "%")) { // Quoting with length, like %5%value bstr rest; long long len = bstrtoll(line, &rest, 10); if (rest.len == line.len || !bstr_eatstart0(&rest, "%") || len > rest.len) { MP_ERR(config, "%s fixed-length quoting expected - put " "\"quotes\" around the option value if you did not " "intend to use this, but your option value starts " "with '%%'\n", loc); goto error; } value = bstr_splice(rest, 0, len); line = bstr_cut(rest, len); } else { // No quoting; take everything until the comment or end of line int end = bstrchr(line, '#'); value = bstr_strip(end < 0 ? line : bstr_splice(line, 0, end)); line.len = 0; } } if (skip_ws(&line)) { MP_ERR(config, "%s unparseable extra characters: '%.*s'\n", loc, BSTR_P(line)); goto error; } int res; if (profile) { if (bstr_equals0(option, "profile-desc")) { m_profile_set_desc(profile, value); res = 0; } else { res = m_config_set_profile_option(config, profile, option, value); } } else { res = m_config_set_option_ext(config, option, value, flags); } if (res < 0) { MP_ERR(config, "%s setting option %.*s='%.*s' failed.\n", loc, BSTR_P(option), BSTR_P(value)); goto error; } ok = true; error: if (!ok) errors++; if (errors > 16) { MP_ERR(config, "%s: too many errors, stopping.\n", location); break; } } talloc_free(tmp); return 1; }
/* Returns a list of parts, or NULL on parse error. * Syntax (without file header or URI prefix): * url ::= <entry> ( (';' | '\n') <entry> )* * entry ::= <param> ( <param> ',' )* * param ::= [<string> '='] (<string> | '%' <number> '%' <bytes>) */ static struct tl_parts *parse_edl(bstr str) { struct tl_parts *tl = talloc_zero(NULL, struct tl_parts); while (str.len) { if (bstr_eatstart0(&str, "#")) bstr_split_tok(str, "\n", &(bstr){0}, &str); if (bstr_eatstart0(&str, "\n") || bstr_eatstart0(&str, ";")) continue; struct tl_part p = { .length = -1 }; int nparam = 0; while (1) { bstr name, val; // Check if it's of the form "name=..." int next = bstrcspn(str, "=%,;\n"); if (next > 0 && next < str.len && str.start[next] == '=') { name = bstr_splice(str, 0, next); str = bstr_cut(str, next + 1); } else { const char *names[] = {"file", "start", "length"}; // implied name name = bstr0(nparam < 3 ? names[nparam] : "-"); } if (bstr_eatstart0(&str, "%")) { int len = bstrtoll(str, &str, 0); if (!bstr_startswith0(str, "%") || (len > str.len - 1)) goto error; val = bstr_splice(str, 1, len + 1); str = bstr_cut(str, len + 1); } else { next = bstrcspn(str, ",;\n"); val = bstr_splice(str, 0, next); str = bstr_cut(str, next); } // Interpret parameters. Explicitly ignore unknown ones. if (bstr_equals0(name, "file")) { p.filename = bstrto0(tl, val); } else if (bstr_equals0(name, "start")) { if (!parse_time(val, &p.offset)) goto error; p.offset_set = true; } else if (bstr_equals0(name, "length")) { if (!parse_time(val, &p.length)) goto error; } else if (bstr_equals0(name, "timestamps")) { if (bstr_equals0(val, "chapters")) p.chapter_ts = true; } nparam++; if (!bstr_eatstart0(&str, ",")) break; } if (!p.filename) goto error; MP_TARRAY_APPEND(tl, tl->parts, tl->num_parts, p); } if (!tl->num_parts) goto error; return tl; error: talloc_free(tl); return NULL; } static struct demuxer *open_source(struct timeline *tl, char *filename) { for (int n = 0; n < tl->num_sources; n++) { struct demuxer *d = tl->sources[n]; if (strcmp(d->stream->url, filename) == 0) return d; } struct demuxer *d = demux_open_url(filename, NULL, tl->cancel, tl->global); if (d) { MP_TARRAY_APPEND(tl, tl->sources, tl->num_sources, d); } else { MP_ERR(tl, "EDL: Could not open source file '%s'.\n", filename); } return d; } static double demuxer_chapter_time(struct demuxer *demuxer, int n) { if (n < 0 || n >= demuxer->num_chapters) return -1; return demuxer->chapters[n].pts; } // Append all chapters from src to the chapters array. // Ignore chapters outside of the given time range. static void copy_chapters(struct demux_chapter **chapters, int *num_chapters, struct demuxer *src, double start, double len, double dest_offset) { for (int n = 0; n < src->num_chapters; n++) { double time = demuxer_chapter_time(src, n); if (time >= start && time <= start + len) { struct demux_chapter ch = { .pts = dest_offset + time - start, .metadata = mp_tags_dup(*chapters, src->chapters[n].metadata), }; MP_TARRAY_APPEND(NULL, *chapters, *num_chapters, ch); } } } // return length of the source in seconds, or -1 if unknown static double source_get_length(struct demuxer *demuxer) { double time; // <= 0 means DEMUXER_CTRL_NOTIMPL or DEMUXER_CTRL_DONTKNOW if (demux_control(demuxer, DEMUXER_CTRL_GET_TIME_LENGTH, &time) <= 0) time = -1; return time; } static void resolve_timestamps(struct tl_part *part, struct demuxer *demuxer) { if (part->chapter_ts) { double start = demuxer_chapter_time(demuxer, part->offset); double length = part->length; double end = length; if (end >= 0) end = demuxer_chapter_time(demuxer, part->offset + part->length); if (end >= 0 && start >= 0) length = end - start; part->offset = start; part->length = length; } if (!part->offset_set) part->offset = demuxer->start_time; } static void build_timeline(struct timeline *tl, struct tl_parts *parts) { tl->parts = talloc_array_ptrtype(tl, tl->parts, parts->num_parts + 1); double starttime = 0; for (int n = 0; n < parts->num_parts; n++) { struct tl_part *part = &parts->parts[n]; struct demuxer *source = open_source(tl, part->filename); if (!source) goto error; resolve_timestamps(part, source); double end_time = source_get_length(source); if (end_time >= 0) end_time += source->start_time; // Unknown length => use rest of the file. If duration is unknown, make // something up. if (part->length < 0) { if (end_time < 0) { MP_WARN(tl, "EDL: source file '%s' has unknown duration.\n", part->filename); end_time = 1; } part->length = end_time - part->offset; } else if (end_time >= 0) { double end_part = part->offset + part->length; if (end_part > end_time) { MP_WARN(tl, "EDL: entry %d uses %f " "seconds, but file has only %f seconds.\n", n, end_part, end_time); } } // Add a chapter between each file. struct demux_chapter ch = { .pts = starttime, .metadata = talloc_zero(tl, struct mp_tags), }; mp_tags_set_str(ch.metadata, "title", part->filename); MP_TARRAY_APPEND(tl, tl->chapters, tl->num_chapters, ch); // Also copy the source file's chapters for the relevant parts copy_chapters(&tl->chapters, &tl->num_chapters, source, part->offset, part->length, starttime); tl->parts[n] = (struct timeline_part) { .start = starttime, .source_start = part->offset, .source = source, }; starttime += part->length; } tl->parts[parts->num_parts] = (struct timeline_part) {.start = starttime}; tl->num_parts = parts->num_parts; tl->track_layout = tl->parts[0].source; return; error: tl->num_parts = 0; tl->num_chapters = 0; } // For security, don't allow relative or absolute paths, only plain filenames. // Also, make these filenames relative to the edl source file. static void fix_filenames(struct tl_parts *parts, char *source_path) { struct bstr dirname = mp_dirname(source_path); for (int n = 0; n < parts->num_parts; n++) { struct tl_part *part = &parts->parts[n]; char *filename = mp_basename(part->filename); // plain filename only part->filename = mp_path_join_bstr(parts, dirname, bstr0(filename)); } } static void build_mpv_edl_timeline(struct timeline *tl) { struct priv *p = tl->demuxer->priv; struct tl_parts *parts = parse_edl(p->data); if (!parts) { MP_ERR(tl, "Error in EDL.\n"); return; } MP_TARRAY_APPEND(tl, tl->sources, tl->num_sources, tl->demuxer); // Source is .edl and not edl:// => don't allow arbitrary paths if (tl->demuxer->stream->uncached_type != STREAMTYPE_EDL) fix_filenames(parts, tl->demuxer->filename); build_timeline(tl, parts); talloc_free(parts); } static int try_open_file(struct demuxer *demuxer, enum demux_check check) { struct priv *p = talloc_zero(demuxer, struct priv); demuxer->priv = p; demuxer->fully_read = true; struct stream *s = demuxer->stream; if (s->uncached_type == STREAMTYPE_EDL) { p->data = bstr0(s->path); return 0; } if (check >= DEMUX_CHECK_UNSAFE) { if (!bstr_equals0(stream_peek(s, strlen(HEADER)), HEADER)) return -1; } p->data = stream_read_complete(s, demuxer, 1000000); if (p->data.start == NULL) return -1; bstr_eatstart0(&p->data, HEADER); return 0; } const struct demuxer_desc demuxer_desc_edl = { .name = "edl", .desc = "Edit decision list", .open = try_open_file, .load_timeline = build_mpv_edl_timeline, };
int parse_codec_cfg(const char *cfgfile) { codecs_t *codec = NULL; // current codec codecs_t **codecsp = NULL;// points to audio_codecs or to video_codecs char *endptr; // strtoul()... int *nr_codecsp; int codec_type; /* TYPE_VIDEO/TYPE_AUDIO */ int tmp, i; int codec_cfg_min; for (struct bstr s = builtin_codecs_conf; ; bstr_getline(s, &s)) { if (!s.len) abort(); if (bstr_eatstart0(&s, "release ")) { codec_cfg_min = atoi(s.start); break; } } // in case we call it a second time codecs_uninit_free(); nr_vcodecs = 0; nr_acodecs = 0; if (cfgfile) { // Avoid printing errors from open_stream when trying optional files if (!mp_path_exists(cfgfile)) { mp_tmsg(MSGT_CODECCFG, MSGL_V, "No optional codecs config file: %s\n", cfgfile); return 0; } mp_msg(MSGT_CODECCFG, MSGL_V, "Reading codec config file: %s\n", cfgfile); struct stream *s = open_stream(cfgfile, NULL, NULL); if (!s) return 0; filetext = stream_read_complete(s, NULL, 10000000, 1); free_stream(s); if (!filetext.start) return 0; } else // Parsing modifies the data filetext = bstrdup(NULL, builtin_codecs_conf); void *tmpmem = filetext.start; read_nextline = 1; /* * this only catches release lines at the start of * codecs.conf, before audiocodecs and videocodecs. */ while ((tmp = get_token(1, 1)) == RET_EOL) /* NOTHING */; if (tmp == RET_EOF) goto out; if (!strcmp(token[0], "release")) { if (get_token(1, 2) < 0) goto err_out_parse_error; tmp = atoi(token[0]); if (tmp < codec_cfg_min) goto err_out_release_num; codecs_conf_release = tmp; while ((tmp = get_token(1, 1)) == RET_EOL) /* NOTHING */; if (tmp == RET_EOF) goto out; } else goto err_out_release_num; /* * check if the next block starts with 'audiocodec' or * with 'videocodec' */ if (!strcmp(token[0], "audiocodec") || !strcmp(token[0], "videocodec")) goto loop_enter; goto err_out_parse_error; while ((tmp = get_token(1, 1)) != RET_EOF) { if (tmp == RET_EOL) continue; if (!strcmp(token[0], "audiocodec") || !strcmp(token[0], "videocodec")) { if (!validate_codec(codec, codec_type)) goto err_out_not_valid; loop_enter: if (*token[0] == 'v') { codec_type = TYPE_VIDEO; nr_codecsp = &nr_vcodecs; codecsp = &video_codecs; } else { assert(*token[0] == 'a'); codec_type = TYPE_AUDIO; nr_codecsp = &nr_acodecs; codecsp = &audio_codecs; } if (!(*codecsp = realloc(*codecsp, sizeof(codecs_t) * (*nr_codecsp + 2)))) { mp_tmsg(MSGT_CODECCFG,MSGL_FATAL,"Can't realloc '*codecsp': %s\n", strerror(errno)); goto err_out; } codec=*codecsp + *nr_codecsp; ++*nr_codecsp; memset(codec,0,sizeof(codecs_t)); memset(codec->fourcc, 0xff, sizeof(codec->fourcc)); memset(codec->outfmt, 0xff, sizeof(codec->outfmt)); memset(codec->infmt, 0xff, sizeof(codec->infmt)); if (get_token(1, 1) < 0) goto err_out_parse_error; for (i = 0; i < *nr_codecsp - 1; i++) { if(( (*codecsp)[i].name!=NULL) && (!strcmp(token[0], (*codecsp)[i].name)) ) { mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Codec name '%s' isn't unique.", token[0]); goto err_out_print_linenum; } } if (!(codec->name = strdup(token[0]))) { mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Can't strdup -> 'name': %s\n", strerror(errno)); goto err_out; } } else if (!strcmp(token[0], "info")) { if (codec->info || get_token(1, 1) < 0) goto err_out_parse_error; if (!(codec->info = strdup(token[0]))) { mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Can't strdup -> 'info': %s\n", strerror(errno)); goto err_out; } } else if (!strcmp(token[0], "comment")) { if (get_token(1, 1) < 0) goto err_out_parse_error; add_comment(token[0], &codec->comment); } else if (!strcmp(token[0], "fourcc")) { if (get_token(1, 2) < 0) goto err_out_parse_error; if (!add_to_fourcc(token[0], token[1], codec->fourcc, codec->fourccmap)) goto err_out_print_linenum; } else if (!strcmp(token[0], "format")) { if (get_token(1, 2) < 0) goto err_out_parse_error; if (!add_to_format(token[0], token[1], codec->fourcc,codec->fourccmap)) goto err_out_print_linenum; } else if (!strcmp(token[0], "driver")) { if (get_token(1, 1) < 0) goto err_out_parse_error; if (!(codec->drv = strdup(token[0]))) { mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Can't strdup -> 'driver': %s\n", strerror(errno)); goto err_out; } } else if (!strcmp(token[0], "dll")) { if (get_token(1, 1) < 0) goto err_out_parse_error; if (!(codec->dll = strdup(token[0]))) { mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Can't strdup -> 'dll': %s", strerror(errno)); goto err_out; } } else if (!strcmp(token[0], "guid")) { if (get_token(11, 11) < 0) goto err_out_parse_error; codec->guid.f1=strtoul(token[0],&endptr,0); if ((*endptr != ',' || *(endptr + 1) != '\0') && *endptr != '\0') goto err_out_parse_error; codec->guid.f2=strtoul(token[1],&endptr,0); if ((*endptr != ',' || *(endptr + 1) != '\0') && *endptr != '\0') goto err_out_parse_error; codec->guid.f3=strtoul(token[2],&endptr,0); if ((*endptr != ',' || *(endptr + 1) != '\0') && *endptr != '\0') goto err_out_parse_error; for (i = 0; i < 8; i++) { codec->guid.f4[i]=strtoul(token[i + 3],&endptr,0); if ((*endptr != ',' || *(endptr + 1) != '\0') && *endptr != '\0') goto err_out_parse_error; } } else if (!strcmp(token[0], "out")) { if (get_token(1, 2) < 0) goto err_out_parse_error; if (!add_to_inout(token[0], token[1], codec->outfmt, codec->outflags)) goto err_out_print_linenum; } else if (!strcmp(token[0], "in")) { if (get_token(1, 2) < 0) goto err_out_parse_error; if (!add_to_inout(token[0], token[1], codec->infmt, codec->inflags)) goto err_out_print_linenum; } else if (!strcmp(token[0], "flags")) { if (get_token(1, 1) < 0) goto err_out_parse_error; if (!strcmp(token[0], "seekable")) codec->flags |= CODECS_FLAG_SEEKABLE; else if (!strcmp(token[0], "align16")) codec->flags |= CODECS_FLAG_ALIGN16; else goto err_out_parse_error; } else if (!strcmp(token[0], "status")) { if (get_token(1, 1) < 0) goto err_out_parse_error; if (!strcasecmp(token[0], "working")) codec->status = CODECS_STATUS_WORKING; else if (!strcasecmp(token[0], "crashing")) codec->status = CODECS_STATUS_NOT_WORKING; else if (!strcasecmp(token[0], "untested")) codec->status = CODECS_STATUS_UNTESTED; else if (!strcasecmp(token[0], "buggy")) codec->status = CODECS_STATUS_PROBLEMS; else goto err_out_parse_error; } else if (!strcmp(token[0], "anyinput")) { codec->anyinput = true; } else goto err_out_parse_error; } if (!validate_codec(codec, codec_type)) goto err_out_not_valid; mp_tmsg(MSGT_CODECCFG, MSGL_V, "%d audio & %d video codecs\n", nr_acodecs, nr_vcodecs); if(video_codecs) video_codecs[nr_vcodecs].name = NULL; if(audio_codecs) audio_codecs[nr_acodecs].name = NULL; out: talloc_free(tmpmem); line=NULL; return 1; err_out_parse_error: mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"parse error"); err_out_print_linenum: PRINT_LINENUM; err_out: codecs_uninit_free(); talloc_free(tmpmem); line=NULL; line_num = 0; return 0; err_out_not_valid: mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Codec is not defined correctly."); goto err_out_print_linenum; err_out_release_num: mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"This codecs.conf is too old and incompatible with this MPlayer release!"); goto err_out_print_linenum; }