Exemplo n.º 1
0
// Disable style definitions generated by the libavcodec converter.
// We always want the user defined style instead.
static void disable_styles(bstr header)
{
    while (header.len) {
        int n = bstr_find(header, bstr0("\nStyle: "));
        if (n < 0)
            break;
        header.start[n + 1] = '#'; // turn into a comment
        header = bstr_cut(header, 2);
    }
}
Exemplo n.º 2
0
// Unlike with bstr_split(), tok is a string, and not a set of char.
// If tok is in str, return true, and: concat(out_left, tok, out_right) == str
// Otherwise, return false, and set out_left==str, out_right==""
bool bstr_split_tok(bstr str, const char *tok, bstr *out_left, bstr *out_right)
{
    bstr bsep = bstr0(tok);
    int pos = bstr_find(str, bsep);
    if (pos < 0)
        pos = str.len;
    *out_left = bstr_splice(str, 0, pos);
    *out_right = bstr_cut(str, pos + bsep.len);
    return pos != str.len;
}
Exemplo n.º 3
0
Arquivo: cmd_list.c Projeto: Jehan/mpv
bool mp_replace_legacy_cmd(void *t, bstr *s)
{
    for (const struct legacy_cmd *entry = legacy_cmds; entry->old; entry++) {
        bstr old = bstr0(entry->old);
        if (bstrcasecmp(bstr_splice(*s, 0, old.len), old) == 0) {
            bstr rest = bstr_cut(*s, old.len);
            *s = bstr0(talloc_asprintf(t, "%s%.*s", entry->new, BSTR_P(rest)));
            return true;
        }
    }
Exemplo n.º 4
0
Arquivo: cue.c Projeto: ThreeGe/mpv
static char *read_quoted(void *talloc_ctx, struct bstr *data)
{
    *data = bstr_lstrip(*data);
    if (!eat_char(data, '"'))
        return NULL;
    int end = bstrchr(*data, '"');
    if (end < 0)
        return NULL;
    struct bstr res = bstr_splice(*data, 0, end);
    *data = bstr_cut(*data, end + 1);
    return bstrto0(talloc_ctx, res);
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
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);
    buf[len] = 0;
    char *endptr;
    long long r = strtoll(buf, &endptr, base);
    if (rest)
        *rest = bstr_cut(str, endptr - buf);
    return r;
}
Exemplo n.º 7
0
Arquivo: cue.c Projeto: ThreeGe/mpv
static enum cue_command read_cmd(struct bstr *data, struct bstr *out_params)
{
    struct bstr line = bstr_strip_linebreaks(bstr_getline(*data, data));
    line = bstr_lstrip(line);
    if (line.len == 0)
        return CUE_EMPTY;
    for (int n = 0; cue_command_strings[n].command != -1; n++) {
        struct bstr name = bstr0(cue_command_strings[n].text);
        if (bstr_startswith(line, name)) {
            struct bstr rest = bstr_cut(line, name.len);
            if (rest.len && !strchr(WHITESPACE, rest.start[0]))
                continue;
            if (out_params)
                *out_params = rest;
            return cue_command_strings[n].command;
        }
    }
    return CUE_ERROR;
}
Exemplo n.º 8
0
Arquivo: cue.c Projeto: ThreeGe/mpv
static struct bstr skip_utf8_bom(struct bstr data)
{
    return bstr_startswith0(data, "\xEF\xBB\xBF") ? bstr_cut(data, 3) : data;
}
Exemplo n.º 9
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->mp_opt = NULL;
    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);
    }

    bool old_syntax = !bstr_startswith0(p->arg, "--");
    if (old_syntax) {
        p->arg = bstr_cut(p->arg, 1);
    } else {
        p->arg = bstr_cut(p->arg, 2);
        int idx = bstrchr(p->arg, '=');
        if (idx > 0) {
            p->param = bstr_cut(p->arg, idx + 1);
            p->arg = bstr_splice(p->arg, 0, idx);
        }
    }

    p->mp_opt = m_config_get_option(p->config, p->arg);
    if (!p->mp_opt) {
        // Automagic "no-" arguments: "--no-bla" turns into "--bla=no".
        if (!bstr_startswith0(p->arg, "no-"))
            return -1;

        struct bstr s = bstr_cut(p->arg, 3);
        p->mp_opt = m_config_get_option(p->config, s);
        if (!p->mp_opt || p->mp_opt->type != &m_option_type_flag)
            return -1;
        // Avoid allowing "--no-no-bla".
        if (bstr_startswith(bstr0(p->mp_opt->name), bstr0("no-")))
            return -1;
        // Flag options never have parameters.
        old_syntax = false;
        if (p->param.len)
            return -2;
        p->arg = s;
        p->param = bstr0("no");
    }

    if (bstr_endswith0(p->arg, "-clr"))
        old_syntax = false;

    if (old_syntax && !(p->mp_opt->type->flags & M_OPT_TYPE_OLD_SYNTAX_NO_PARAM))
    {
        if (p->argc < 1)
            return -3;
        p->param = bstr0(p->argv[0]);
        p->argc--;
        p->argv++;
    }

    return 0;
}
Exemplo n.º 10
0
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;
}
Exemplo n.º 11
0
/* 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,
};
Exemplo n.º 12
0
static struct bstr read_quoted(struct bstr *data)
{
    *data = bstr_lstrip(*data);
    if (!eat_char(data, '"'))
        return (struct bstr) {0};
    int end = bstrchr(*data, '"');
    if (end < 0)
        return (struct bstr) {0};
    struct bstr res = bstr_splice(*data, 0, end);
    *data = bstr_cut(*data, end + 1);
    return res;
}

// Read a 2 digit unsigned decimal integer.
// Return -1 on failure.
static int read_int_2(struct bstr *data)
{
    *data = bstr_lstrip(*data);
    if (data->len && data->start[0] == '-')
        return -1;
    struct bstr s = *data;
    int res = (int)bstrtoll(s, &s, 10);
    if (data->len == s.len || data->len - s.len > 2)
        return -1;
    *data = s;
    return res;
}

static double read_time(struct bstr *data)
{
    struct bstr s = *data;
    bool ok = true;
    double t1 = read_int_2(&s);
    ok = eat_char(&s, ':') && ok;
    double t2 = read_int_2(&s);
    ok = eat_char(&s, ':') && ok;
    double t3 = read_int_2(&s);
    ok = ok && t1 >= 0 && t2 >= 0 && t3 >= 0;
    return ok ? t1 * 60.0 + t2 + t3 * SECS_PER_CUE_FRAME : 0;
}

static struct bstr skip_utf8_bom(struct bstr data)
{
    return bstr_startswith0(data, "\xEF\xBB\xBF") ? bstr_cut(data, 3) : data;
}

// Check if the text in data is most likely CUE data. This is used by the
// demuxer code to check the file type.
// data is the start of the probed file, possibly cut off at a random point.
bool mp_probe_cue(struct bstr data)
{
    bool valid = false;
    data = skip_utf8_bom(data);
    for (;;) {
        enum cue_command cmd = read_cmd(&data, NULL);
        // End reached. Since the line was most likely cut off, don't use the
        // result of the last parsing call.
        if (data.len == 0)
            break;
        if (cmd == CUE_ERROR)
            return false;
        if (cmd != CUE_EMPTY)
            valid = true;
    }
    return valid;
}