Michael Niedermayeravformat/concatdec: Check filea5d14972 years agoHistory
964 lines · 30.4 KB · C
RawDownload
/* * Copyright (c) 2012 Nicolas George * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with FFmpeg; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/avstring.h"#include "libavutil/avassert.h"#include "libavutil/bprint.h"#include "libavutil/intreadwrite.h"#include "libavutil/mem.h"#include "libavutil/opt.h"#include "libavutil/parseutils.h"#include "libavutil/timestamp.h"#include "libavcodec/codec_desc.h"#include "libavcodec/bsf.h"#include "avformat.h"#include "avio_internal.h"#include "demux.h"#include "internal.h"#include "url.h" typedef enum ConcatMatchMode {    MATCH_ONE_TO_ONE,    MATCH_EXACT_ID,} ConcatMatchMode; typedef struct ConcatStream {    AVBSFContext *bsf;    int out_stream_index;} ConcatStream; typedef struct {    char *url;    int64_t start_time;    int64_t file_start_time;    int64_t file_inpoint;    int64_t duration;    int64_t user_duration;    int64_t next_dts;    ConcatStream *streams;    int64_t inpoint;    int64_t outpoint;    AVDictionary *metadata;    AVDictionary *options;    int nb_streams;} ConcatFile; typedef struct {    AVClass *class;    ConcatFile *files;    ConcatFile *cur_file;    unsigned nb_files;    AVFormatContext *avf;    int safe;    int seekable;    int eof;    ConcatMatchMode stream_match_mode;    unsigned auto_convert;    int segment_time_metadata;} ConcatContext; static int concat_probe(const AVProbeData *probe){    return memcmp(probe->buf, "ffconcat version 1.0", 20) ?           0 : AVPROBE_SCORE_MAX;} static char *get_keyword(uint8_t **cursor){    char *ret = *cursor += strspn(*cursor, SPACE_CHARS);    *cursor += strcspn(*cursor, SPACE_CHARS);    if (**cursor) {        *((*cursor)++) = 0;        *cursor += strspn(*cursor, SPACE_CHARS);    }    return ret;} static int safe_filename(const char *f){    const char *start = f;     for (; *f; f++) {        /* A-Za-z0-9_- */        if (!((unsigned)((*f | 32) - 'a') < 26 ||              (unsigned)(*f - '0') < 10 || *f == '_' || *f == '-')) {            if (f == start)                return 0;            else if (*f == '/')                start = f + 1;            else if (*f != '.')                return 0;        }    }    return 1;} #define FAIL(retcode) do { ret = (retcode); goto fail; } while(0) static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,                    unsigned *nb_files_alloc){    ConcatContext *cat = avf->priv_data;    ConcatFile *file;    char *url = NULL;    const char *proto;    const char *ptr;    size_t url_len;    int ret;     if (cat->safe && !safe_filename(filename)) {        av_log(avf, AV_LOG_ERROR, "Unsafe file name '%s'\n", filename);        FAIL(AVERROR(EPERM));    }     proto = avio_find_protocol_name(filename);    if (proto && av_strstart(filename, proto, &ptr) &&        (*ptr == ':' || *ptr == ',')) {        url = filename;        filename = NULL;    } else {        url_len = strlen(avf->url) + strlen(filename) + 16;        if (!(url = av_malloc(url_len)))            FAIL(AVERROR(ENOMEM));        ff_make_absolute_url(url, url_len, avf->url, filename);        av_freep(&filename);    }     if (cat->nb_files >= *nb_files_alloc) {        size_t n = FFMAX(*nb_files_alloc * 2, 16);        ConcatFile *new_files;        if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) ||            !(new_files = av_realloc(cat->files, n * sizeof(*cat->files))))            FAIL(AVERROR(ENOMEM));        cat->files = new_files;        *nb_files_alloc = n;    }     file = &cat->files[cat->nb_files++];    memset(file, 0, sizeof(*file));    *rfile = file;     file->url        = url;    file->start_time = AV_NOPTS_VALUE;    file->duration   = AV_NOPTS_VALUE;    file->next_dts   = AV_NOPTS_VALUE;    file->inpoint    = AV_NOPTS_VALUE;    file->outpoint   = AV_NOPTS_VALUE;    file->user_duration = AV_NOPTS_VALUE;     return 0; fail:    av_free(url);    av_free(filename);    return ret;} static int copy_stream_props(AVStream *st, AVStream *source_st){    int ret;     if (st->codecpar->codec_id || !source_st->codecpar->codec_id) {        if (st->codecpar->extradata_size < source_st->codecpar->extradata_size) {            ret = ff_alloc_extradata(st->codecpar,                                     source_st->codecpar->extradata_size);            if (ret < 0)                return ret;        }        if (source_st->codecpar->extradata_size)            memcpy(st->codecpar->extradata, source_st->codecpar->extradata,                   source_st->codecpar->extradata_size);        return 0;    }    if ((ret = avcodec_parameters_copy(st->codecpar, source_st->codecpar)) < 0)        return ret;    st->r_frame_rate        = source_st->r_frame_rate;    st->avg_frame_rate      = source_st->avg_frame_rate;    st->sample_aspect_ratio = source_st->sample_aspect_ratio;    avpriv_set_pts_info(st, 64, source_st->time_base.num, source_st->time_base.den);     av_dict_copy(&st->metadata, source_st->metadata, 0);    return 0;} static int detect_stream_specific(AVFormatContext *avf, int idx){    ConcatContext *cat = avf->priv_data;    AVStream *st = cat->avf->streams[idx];    ConcatStream *cs = &cat->cur_file->streams[idx];    const AVBitStreamFilter *filter;    AVBSFContext *bsf;    int ret;     if (cat->auto_convert && st->codecpar->codec_id == AV_CODEC_ID_H264) {        if (!st->codecpar->extradata_size                                                ||            (st->codecpar->extradata_size >= 3 && AV_RB24(st->codecpar->extradata) == 1) ||            (st->codecpar->extradata_size >= 4 && AV_RB32(st->codecpar->extradata) == 1))            return 0;        av_log(cat->avf, AV_LOG_INFO,               "Auto-inserting h264_mp4toannexb bitstream filter\n");        filter = av_bsf_get_by_name("h264_mp4toannexb");        if (!filter) {            av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb bitstream filter "                   "required for H.264 streams\n");            return AVERROR_BSF_NOT_FOUND;        }        ret = av_bsf_alloc(filter, &bsf);        if (ret < 0)            return ret;        cs->bsf = bsf;         ret = avcodec_parameters_copy(bsf->par_in, st->codecpar);        if (ret < 0)           return ret;         ret = av_bsf_init(bsf);        if (ret < 0)            return ret;         ret = avcodec_parameters_copy(st->codecpar, bsf->par_out);        if (ret < 0)            return ret;    }    return 0;} static int match_streams_one_to_one(AVFormatContext *avf){    ConcatContext *cat = avf->priv_data;    AVStream *st;    int i, ret;     for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {        if (i < avf->nb_streams) {            st = avf->streams[i];        } else {            if (!(st = avformat_new_stream(avf, NULL)))                return AVERROR(ENOMEM);        }        if ((ret = copy_stream_props(st, cat->avf->streams[i])) < 0)            return ret;        cat->cur_file->streams[i].out_stream_index = i;    }    return 0;} static int match_streams_exact_id(AVFormatContext *avf){    ConcatContext *cat = avf->priv_data;    AVStream *st;    int i, j, ret;     for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {        st = cat->avf->streams[i];        for (j = 0; j < avf->nb_streams; j++) {            if (avf->streams[j]->id == st->id) {                av_log(avf, AV_LOG_VERBOSE,                       "Match slave stream #%d with stream #%d id 0x%x\n",                       i, j, st->id);                if ((ret = copy_stream_props(avf->streams[j], st)) < 0)                    return ret;                cat->cur_file->streams[i].out_stream_index = j;            }        }    }    return 0;} static int match_streams(AVFormatContext *avf){    ConcatContext *cat = avf->priv_data;    ConcatStream *map;    int i, ret;     if (cat->cur_file->nb_streams >= cat->avf->nb_streams)        return 0;    map = av_realloc(cat->cur_file->streams,                     cat->avf->nb_streams * sizeof(*map));    if (!map)        return AVERROR(ENOMEM);    cat->cur_file->streams = map;    memset(map + cat->cur_file->nb_streams, 0,           (cat->avf->nb_streams - cat->cur_file->nb_streams) * sizeof(*map));     for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {        map[i].out_stream_index = -1;        if ((ret = detect_stream_specific(avf, i)) < 0)            return ret;    }    switch (cat->stream_match_mode) {    case MATCH_ONE_TO_ONE:        ret = match_streams_one_to_one(avf);        break;    case MATCH_EXACT_ID:        ret = match_streams_exact_id(avf);        break;    default:        ret = AVERROR_BUG;    }    if (ret < 0)        return ret;    cat->cur_file->nb_streams = cat->avf->nb_streams;    return 0;} static int64_t get_best_effort_duration(ConcatFile *file, AVFormatContext *avf){    if (file->user_duration != AV_NOPTS_VALUE)        return file->user_duration;    if (file->outpoint != AV_NOPTS_VALUE)        return av_sat_sub64(file->outpoint, file->file_inpoint);    if (avf->duration > 0)        return avf->duration - (file->file_inpoint - file->file_start_time);    if (file->next_dts != AV_NOPTS_VALUE)        return file->next_dts - file->file_inpoint;    return AV_NOPTS_VALUE;} static int open_file(AVFormatContext *avf, unsigned fileno){    ConcatContext *cat = avf->priv_data;    ConcatFile *file = &cat->files[fileno];    AVDictionary *options = NULL;    int ret;     if (cat->avf)        avformat_close_input(&cat->avf);     cat->avf = avformat_alloc_context();    if (!cat->avf)        return AVERROR(ENOMEM);     cat->avf->flags |= avf->flags & ~AVFMT_FLAG_CUSTOM_IO;    cat->avf->interrupt_callback = avf->interrupt_callback;     if ((ret = ff_copy_whiteblacklists(cat->avf, avf)) < 0)        return ret;     ret = av_dict_copy(&options, file->options, 0);    if (ret < 0)        return ret;     if ((ret = avformat_open_input(&cat->avf, file->url, NULL, &options)) < 0 ||        (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {        av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);        av_dict_free(&options);        avformat_close_input(&cat->avf);        return ret;    }    if (options) {        av_log(avf, AV_LOG_WARNING, "Unused options for '%s'.\n", file->url);        /* TODO log unused options once we have a proper string API */        av_dict_free(&options);    }    cat->cur_file = file;    file->start_time = !fileno ? 0 :                       cat->files[fileno - 1].start_time +                       cat->files[fileno - 1].duration;    file->file_start_time = (cat->avf->start_time == AV_NOPTS_VALUE) ? 0 : cat->avf->start_time;    file->file_inpoint = (file->inpoint == AV_NOPTS_VALUE) ? file->file_start_time : file->inpoint;    file->duration = get_best_effort_duration(file, cat->avf);     if (cat->segment_time_metadata) {        av_dict_set_int(&file->metadata, "lavf.concatdec.start_time", file->start_time, 0);        if (file->duration != AV_NOPTS_VALUE)            av_dict_set_int(&file->metadata, "lavf.concatdec.duration", file->duration, 0);    }     if ((ret = match_streams(avf)) < 0)        return ret;    if (file->inpoint != AV_NOPTS_VALUE) {       if ((ret = avformat_seek_file(cat->avf, -1, INT64_MIN, file->inpoint, file->inpoint, 0)) < 0)           return ret;    }    return 0;} static int concat_read_close(AVFormatContext *avf){    ConcatContext *cat = avf->priv_data;    unsigned i, j;     for (i = 0; i < cat->nb_files; i++) {        av_freep(&cat->files[i].url);        for (j = 0; j < cat->files[i].nb_streams; j++) {            if (cat->files[i].streams[j].bsf)                av_bsf_free(&cat->files[i].streams[j].bsf);        }        av_freep(&cat->files[i].streams);        av_dict_free(&cat->files[i].metadata);        av_dict_free(&cat->files[i].options);    }    if (cat->avf)        avformat_close_input(&cat->avf);    av_freep(&cat->files);    return 0;} #define MAX_ARGS 3#define NEEDS_UNSAFE   (1 << 0)#define NEEDS_FILE     (1 << 1)#define NEEDS_STREAM   (1 << 2) typedef struct ParseSyntax {    const char *keyword;    char args[MAX_ARGS];    uint8_t flags;} ParseSyntax; typedef enum ParseDirective {   DIR_FFCONCAT,   DIR_FILE,   DIR_DURATION,   DIR_INPOINT,   DIR_OUTPOINT,   DIR_FPMETA,   DIR_FPMETAS,   DIR_OPTION,   DIR_STREAM,   DIR_EXSID,   DIR_STMETA,   DIR_STCODEC,   DIR_STEDATA,   DIR_CHAPTER,} ParseDirective; static const ParseSyntax syntax[] = {    [DIR_FFCONCAT ] = { "ffconcat",             "kk",   0 },    [DIR_FILE     ] = { "file",                 "s",    0 },    [DIR_DURATION ] = { "duration",             "d",    NEEDS_FILE },    [DIR_INPOINT  ] = { "inpoint",              "d",    NEEDS_FILE },    [DIR_OUTPOINT ] = { "outpoint",             "d",    NEEDS_FILE },    [DIR_FPMETA   ] = { "file_packet_meta",     "ks",   NEEDS_FILE },    [DIR_FPMETAS  ] = { "file_packet_metadata", "s",    NEEDS_FILE },    [DIR_OPTION   ] = { "option",               "ks",   NEEDS_FILE | NEEDS_UNSAFE },    [DIR_STREAM   ] = { "stream",               "",     0 },    [DIR_EXSID    ] = { "exact_stream_id",      "i",    NEEDS_STREAM },    [DIR_STMETA   ] = { "stream_meta",          "ks",   NEEDS_STREAM },    [DIR_STCODEC  ] = { "stream_codec",         "k",    NEEDS_STREAM },    [DIR_STEDATA  ] = { "stream_extradata",     "k",    NEEDS_STREAM },    [DIR_CHAPTER  ] = { "chapter",              "idd",  0 },}; static int concat_parse_script(AVFormatContext *avf){    ConcatContext *cat = avf->priv_data;    unsigned nb_files_alloc = 0;    AVBPrint bp;    uint8_t *cursor, *keyword;    ConcatFile *file = NULL;    AVStream *stream = NULL;    AVChapter *chapter = NULL;    unsigned line = 0, arg;    const ParseSyntax *dir;    char *arg_kw[MAX_ARGS];    char *arg_str[MAX_ARGS] = { 0 };    int64_t arg_int[MAX_ARGS];    int ret;     av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);     while ((ret = ff_read_line_to_bprint_overwrite(avf->pb, &bp)) >= 0) {        line++;        cursor = bp.str;        keyword = get_keyword(&cursor);        if (!*keyword || *keyword == '#')            continue;        for (dir = syntax; dir < syntax + FF_ARRAY_ELEMS(syntax); dir++)            if (!strcmp(dir->keyword, keyword))                break;        if (dir >= syntax + FF_ARRAY_ELEMS(syntax)) {            av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n",                   line, keyword);            FAIL(AVERROR_INVALIDDATA);        }         /* Flags check */        if ((dir->flags & NEEDS_UNSAFE) && cat->safe) {            av_log(avf, AV_LOG_ERROR, "Line %d: %s not allowed if safe\n", line, keyword);            FAIL(AVERROR_INVALIDDATA);        }        if ((dir->flags & NEEDS_FILE) && !cat->nb_files) {            av_log(avf, AV_LOG_ERROR, "Line %d: %s without file\n", line, keyword);            FAIL(AVERROR_INVALIDDATA);

Lines 1–500 of 964.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t