Esempio n. 1
0
ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname,
                              char *charset)
{
    ASS_Track *track;

    struct stream *s = open_stream(fname, NULL, NULL);
    if (!s)
        // Stream code should have printed an error already
        return NULL;
    struct bstr content = stream_read_complete(s, NULL, 100000000, 1);
    if (content.start == NULL)
        mp_tmsg(MSGT_ASS, MSGL_ERR, "Refusing to load subtitle file "
                "larger than 100 MB: %s\n", fname);
    free_stream(s);
    if (content.len == 0) {
        talloc_free(content.start);
        return NULL;
    }
    content.start[content.len] = 0;
    track = ass_read_memory(library, content.start, content.len, charset);
    if (track) {
        free(track->name);
        track->name = strdup(fname);
    }
    talloc_free(content.start);
    return track;
}
Esempio n. 2
0
// return value:
//     0 = EOF or no stream found
//     1 = successfully read a packet
static int demux_mf_fill_buffer(demuxer_t *demuxer)
{
    mf_t *mf = demuxer->priv;
    if (mf->curr_frame >= mf->nr_of_files)
        return 0;

    struct stream *entry_stream = NULL;
    if (mf->streams)
        entry_stream = mf->streams[mf->curr_frame];
    struct stream *stream = entry_stream;
    if (!stream) {
        char *filename = mf->names[mf->curr_frame];
        if (filename)
            stream = stream_open(filename, demuxer->opts);
    }

    if (stream) {
        stream_seek(stream, 0);
        bstr data = stream_read_complete(stream, NULL, MF_MAX_FILE_SIZE);
        if (data.len) {
            demux_packet_t *dp = new_demux_packet(data.len);
            memcpy(dp->buffer, data.start, data.len);
            dp->pts = mf->curr_frame / mf->sh->fps;
            dp->keyframe = true;
            demuxer_add_packet(demuxer, demuxer->streams[0], dp);
        }
        talloc_free(data.start);
    }

    if (stream && stream != entry_stream)
        free_stream(stream);

    mf->curr_frame++;
    return 1;
}
Esempio n. 3
0
static void encode_2pass_prepare(struct encode_lavc_context *ctx,
                                 AVDictionary **dictp,
                                 AVStream *stream,
                                 AVCodecContext *codec,
                                 struct stream **bytebuf,
                                 const char *prefix)
{
    if (!*bytebuf) {
        char buf[sizeof(ctx->avc->filename) + 12];
        AVDictionaryEntry *de = av_dict_get(ctx->voptions, "flags", NULL, 0);

        snprintf(buf, sizeof(buf), "%s-%s-pass1.log", ctx->avc->filename,
                 prefix);
        buf[sizeof(buf) - 1] = 0;

        if (value_has_flag(de ? de->value : "", "pass2")) {
            if (!(*bytebuf = stream_open(buf, ctx->global))) {
                MP_WARN(ctx, "%s: could not open '%s', "
                       "disabling 2-pass encoding at pass 2\n", prefix, buf);
                codec->flags &= ~AV_CODEC_FLAG_PASS2;
                set_to_avdictionary(ctx, dictp, "flags", "-pass2");
            } else {
                struct bstr content = stream_read_complete(*bytebuf, NULL,
                                                           1000000000);
                if (content.start == NULL) {
                    MP_WARN(ctx, "%s: could not read '%s', "
                           "disabling 2-pass encoding at pass 1\n",
                           prefix, ctx->avc->filename);
                } else {
                    content.start[content.len] = 0;
                    codec->stats_in = content.start;
                }
                free_stream(*bytebuf);
                *bytebuf = NULL;
            }
        }

        if (value_has_flag(de ? de->value : "", "pass1")) {
            if (!(*bytebuf = open_output_stream(buf, ctx->global))) {
                MP_WARN(ctx,
                    "%s: could not open '%s', disabling "
                    "2-pass encoding at pass 1\n",
                    prefix, ctx->avc->filename);
                set_to_avdictionary(ctx, dictp, "flags", "-pass1");
            }
        }
    }
}
Esempio n. 4
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,
};
Esempio n. 5
0
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;
}