1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Copyright (c) 2012 Nicolas George
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * This file is part of FFmpeg.
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
7cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public License
8cabdff1aSopenharmony_ci * as published by the Free Software Foundation; either
9cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
10cabdff1aSopenharmony_ci *
11cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
12cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
13cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14cabdff1aSopenharmony_ci * GNU Lesser General Public License for more details.
15cabdff1aSopenharmony_ci *
16cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public License
17cabdff1aSopenharmony_ci * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18cabdff1aSopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19cabdff1aSopenharmony_ci */
20cabdff1aSopenharmony_ci
21cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
22cabdff1aSopenharmony_ci#include "libavutil/avassert.h"
23cabdff1aSopenharmony_ci#include "libavutil/bprint.h"
24cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
25cabdff1aSopenharmony_ci#include "libavutil/opt.h"
26cabdff1aSopenharmony_ci#include "libavutil/parseutils.h"
27cabdff1aSopenharmony_ci#include "libavutil/timestamp.h"
28cabdff1aSopenharmony_ci#include "libavcodec/bsf.h"
29cabdff1aSopenharmony_ci#include "avformat.h"
30cabdff1aSopenharmony_ci#include "avio_internal.h"
31cabdff1aSopenharmony_ci#include "demux.h"
32cabdff1aSopenharmony_ci#include "internal.h"
33cabdff1aSopenharmony_ci#include "url.h"
34cabdff1aSopenharmony_ci
35cabdff1aSopenharmony_citypedef enum ConcatMatchMode {
36cabdff1aSopenharmony_ci    MATCH_ONE_TO_ONE,
37cabdff1aSopenharmony_ci    MATCH_EXACT_ID,
38cabdff1aSopenharmony_ci} ConcatMatchMode;
39cabdff1aSopenharmony_ci
40cabdff1aSopenharmony_citypedef struct ConcatStream {
41cabdff1aSopenharmony_ci    AVBSFContext *bsf;
42cabdff1aSopenharmony_ci    int out_stream_index;
43cabdff1aSopenharmony_ci} ConcatStream;
44cabdff1aSopenharmony_ci
45cabdff1aSopenharmony_citypedef struct {
46cabdff1aSopenharmony_ci    char *url;
47cabdff1aSopenharmony_ci    int64_t start_time;
48cabdff1aSopenharmony_ci    int64_t file_start_time;
49cabdff1aSopenharmony_ci    int64_t file_inpoint;
50cabdff1aSopenharmony_ci    int64_t duration;
51cabdff1aSopenharmony_ci    int64_t user_duration;
52cabdff1aSopenharmony_ci    int64_t next_dts;
53cabdff1aSopenharmony_ci    ConcatStream *streams;
54cabdff1aSopenharmony_ci    int64_t inpoint;
55cabdff1aSopenharmony_ci    int64_t outpoint;
56cabdff1aSopenharmony_ci    AVDictionary *metadata;
57cabdff1aSopenharmony_ci    AVDictionary *options;
58cabdff1aSopenharmony_ci    int nb_streams;
59cabdff1aSopenharmony_ci} ConcatFile;
60cabdff1aSopenharmony_ci
61cabdff1aSopenharmony_citypedef struct {
62cabdff1aSopenharmony_ci    AVClass *class;
63cabdff1aSopenharmony_ci    ConcatFile *files;
64cabdff1aSopenharmony_ci    ConcatFile *cur_file;
65cabdff1aSopenharmony_ci    unsigned nb_files;
66cabdff1aSopenharmony_ci    AVFormatContext *avf;
67cabdff1aSopenharmony_ci    int safe;
68cabdff1aSopenharmony_ci    int seekable;
69cabdff1aSopenharmony_ci    int eof;
70cabdff1aSopenharmony_ci    ConcatMatchMode stream_match_mode;
71cabdff1aSopenharmony_ci    unsigned auto_convert;
72cabdff1aSopenharmony_ci    int segment_time_metadata;
73cabdff1aSopenharmony_ci} ConcatContext;
74cabdff1aSopenharmony_ci
75cabdff1aSopenharmony_cistatic int concat_probe(const AVProbeData *probe)
76cabdff1aSopenharmony_ci{
77cabdff1aSopenharmony_ci    return memcmp(probe->buf, "ffconcat version 1.0", 20) ?
78cabdff1aSopenharmony_ci           0 : AVPROBE_SCORE_MAX;
79cabdff1aSopenharmony_ci}
80cabdff1aSopenharmony_ci
81cabdff1aSopenharmony_cistatic char *get_keyword(uint8_t **cursor)
82cabdff1aSopenharmony_ci{
83cabdff1aSopenharmony_ci    char *ret = *cursor += strspn(*cursor, SPACE_CHARS);
84cabdff1aSopenharmony_ci    *cursor += strcspn(*cursor, SPACE_CHARS);
85cabdff1aSopenharmony_ci    if (**cursor) {
86cabdff1aSopenharmony_ci        *((*cursor)++) = 0;
87cabdff1aSopenharmony_ci        *cursor += strspn(*cursor, SPACE_CHARS);
88cabdff1aSopenharmony_ci    }
89cabdff1aSopenharmony_ci    return ret;
90cabdff1aSopenharmony_ci}
91cabdff1aSopenharmony_ci
92cabdff1aSopenharmony_cistatic int safe_filename(const char *f)
93cabdff1aSopenharmony_ci{
94cabdff1aSopenharmony_ci    const char *start = f;
95cabdff1aSopenharmony_ci
96cabdff1aSopenharmony_ci    for (; *f; f++) {
97cabdff1aSopenharmony_ci        /* A-Za-z0-9_- */
98cabdff1aSopenharmony_ci        if (!((unsigned)((*f | 32) - 'a') < 26 ||
99cabdff1aSopenharmony_ci              (unsigned)(*f - '0') < 10 || *f == '_' || *f == '-')) {
100cabdff1aSopenharmony_ci            if (f == start)
101cabdff1aSopenharmony_ci                return 0;
102cabdff1aSopenharmony_ci            else if (*f == '/')
103cabdff1aSopenharmony_ci                start = f + 1;
104cabdff1aSopenharmony_ci            else if (*f != '.')
105cabdff1aSopenharmony_ci                return 0;
106cabdff1aSopenharmony_ci        }
107cabdff1aSopenharmony_ci    }
108cabdff1aSopenharmony_ci    return 1;
109cabdff1aSopenharmony_ci}
110cabdff1aSopenharmony_ci
111cabdff1aSopenharmony_ci#define FAIL(retcode) do { ret = (retcode); goto fail; } while(0)
112cabdff1aSopenharmony_ci
113cabdff1aSopenharmony_cistatic int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,
114cabdff1aSopenharmony_ci                    unsigned *nb_files_alloc)
115cabdff1aSopenharmony_ci{
116cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
117cabdff1aSopenharmony_ci    ConcatFile *file;
118cabdff1aSopenharmony_ci    char *url = NULL;
119cabdff1aSopenharmony_ci    const char *proto;
120cabdff1aSopenharmony_ci    const char *ptr;
121cabdff1aSopenharmony_ci    size_t url_len;
122cabdff1aSopenharmony_ci    int ret;
123cabdff1aSopenharmony_ci
124cabdff1aSopenharmony_ci    if (cat->safe && !safe_filename(filename)) {
125cabdff1aSopenharmony_ci        av_log(avf, AV_LOG_ERROR, "Unsafe file name '%s'\n", filename);
126cabdff1aSopenharmony_ci        FAIL(AVERROR(EPERM));
127cabdff1aSopenharmony_ci    }
128cabdff1aSopenharmony_ci
129cabdff1aSopenharmony_ci    proto = avio_find_protocol_name(filename);
130cabdff1aSopenharmony_ci    if (proto && av_strstart(filename, proto, &ptr) &&
131cabdff1aSopenharmony_ci        (*ptr == ':' || *ptr == ',')) {
132cabdff1aSopenharmony_ci        url = filename;
133cabdff1aSopenharmony_ci        filename = NULL;
134cabdff1aSopenharmony_ci    } else {
135cabdff1aSopenharmony_ci        url_len = strlen(avf->url) + strlen(filename) + 16;
136cabdff1aSopenharmony_ci        if (!(url = av_malloc(url_len)))
137cabdff1aSopenharmony_ci            FAIL(AVERROR(ENOMEM));
138cabdff1aSopenharmony_ci        ff_make_absolute_url(url, url_len, avf->url, filename);
139cabdff1aSopenharmony_ci        av_freep(&filename);
140cabdff1aSopenharmony_ci    }
141cabdff1aSopenharmony_ci
142cabdff1aSopenharmony_ci    if (cat->nb_files >= *nb_files_alloc) {
143cabdff1aSopenharmony_ci        size_t n = FFMAX(*nb_files_alloc * 2, 16);
144cabdff1aSopenharmony_ci        ConcatFile *new_files;
145cabdff1aSopenharmony_ci        if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) ||
146cabdff1aSopenharmony_ci            !(new_files = av_realloc(cat->files, n * sizeof(*cat->files))))
147cabdff1aSopenharmony_ci            FAIL(AVERROR(ENOMEM));
148cabdff1aSopenharmony_ci        cat->files = new_files;
149cabdff1aSopenharmony_ci        *nb_files_alloc = n;
150cabdff1aSopenharmony_ci    }
151cabdff1aSopenharmony_ci
152cabdff1aSopenharmony_ci    file = &cat->files[cat->nb_files++];
153cabdff1aSopenharmony_ci    memset(file, 0, sizeof(*file));
154cabdff1aSopenharmony_ci    *rfile = file;
155cabdff1aSopenharmony_ci
156cabdff1aSopenharmony_ci    file->url        = url;
157cabdff1aSopenharmony_ci    file->start_time = AV_NOPTS_VALUE;
158cabdff1aSopenharmony_ci    file->duration   = AV_NOPTS_VALUE;
159cabdff1aSopenharmony_ci    file->next_dts   = AV_NOPTS_VALUE;
160cabdff1aSopenharmony_ci    file->inpoint    = AV_NOPTS_VALUE;
161cabdff1aSopenharmony_ci    file->outpoint   = AV_NOPTS_VALUE;
162cabdff1aSopenharmony_ci    file->user_duration = AV_NOPTS_VALUE;
163cabdff1aSopenharmony_ci
164cabdff1aSopenharmony_ci    return 0;
165cabdff1aSopenharmony_ci
166cabdff1aSopenharmony_cifail:
167cabdff1aSopenharmony_ci    av_free(url);
168cabdff1aSopenharmony_ci    av_free(filename);
169cabdff1aSopenharmony_ci    return ret;
170cabdff1aSopenharmony_ci}
171cabdff1aSopenharmony_ci
172cabdff1aSopenharmony_cistatic int copy_stream_props(AVStream *st, AVStream *source_st)
173cabdff1aSopenharmony_ci{
174cabdff1aSopenharmony_ci    int ret;
175cabdff1aSopenharmony_ci
176cabdff1aSopenharmony_ci    if (st->codecpar->codec_id || !source_st->codecpar->codec_id) {
177cabdff1aSopenharmony_ci        if (st->codecpar->extradata_size < source_st->codecpar->extradata_size) {
178cabdff1aSopenharmony_ci            ret = ff_alloc_extradata(st->codecpar,
179cabdff1aSopenharmony_ci                                     source_st->codecpar->extradata_size);
180cabdff1aSopenharmony_ci            if (ret < 0)
181cabdff1aSopenharmony_ci                return ret;
182cabdff1aSopenharmony_ci        }
183cabdff1aSopenharmony_ci        memcpy(st->codecpar->extradata, source_st->codecpar->extradata,
184cabdff1aSopenharmony_ci               source_st->codecpar->extradata_size);
185cabdff1aSopenharmony_ci        return 0;
186cabdff1aSopenharmony_ci    }
187cabdff1aSopenharmony_ci    if ((ret = avcodec_parameters_copy(st->codecpar, source_st->codecpar)) < 0)
188cabdff1aSopenharmony_ci        return ret;
189cabdff1aSopenharmony_ci    st->r_frame_rate        = source_st->r_frame_rate;
190cabdff1aSopenharmony_ci    st->avg_frame_rate      = source_st->avg_frame_rate;
191cabdff1aSopenharmony_ci    st->sample_aspect_ratio = source_st->sample_aspect_ratio;
192cabdff1aSopenharmony_ci    avpriv_set_pts_info(st, 64, source_st->time_base.num, source_st->time_base.den);
193cabdff1aSopenharmony_ci
194cabdff1aSopenharmony_ci    av_dict_copy(&st->metadata, source_st->metadata, 0);
195cabdff1aSopenharmony_ci    ff_stream_side_data_copy(st, source_st);
196cabdff1aSopenharmony_ci    return 0;
197cabdff1aSopenharmony_ci}
198cabdff1aSopenharmony_ci
199cabdff1aSopenharmony_cistatic int detect_stream_specific(AVFormatContext *avf, int idx)
200cabdff1aSopenharmony_ci{
201cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
202cabdff1aSopenharmony_ci    AVStream *st = cat->avf->streams[idx];
203cabdff1aSopenharmony_ci    ConcatStream *cs = &cat->cur_file->streams[idx];
204cabdff1aSopenharmony_ci    const AVBitStreamFilter *filter;
205cabdff1aSopenharmony_ci    AVBSFContext *bsf;
206cabdff1aSopenharmony_ci    int ret;
207cabdff1aSopenharmony_ci
208cabdff1aSopenharmony_ci    if (cat->auto_convert && st->codecpar->codec_id == AV_CODEC_ID_H264) {
209cabdff1aSopenharmony_ci        if (!st->codecpar->extradata_size                                                ||
210cabdff1aSopenharmony_ci            (st->codecpar->extradata_size >= 3 && AV_RB24(st->codecpar->extradata) == 1) ||
211cabdff1aSopenharmony_ci            (st->codecpar->extradata_size >= 4 && AV_RB32(st->codecpar->extradata) == 1))
212cabdff1aSopenharmony_ci            return 0;
213cabdff1aSopenharmony_ci        av_log(cat->avf, AV_LOG_INFO,
214cabdff1aSopenharmony_ci               "Auto-inserting h264_mp4toannexb bitstream filter\n");
215cabdff1aSopenharmony_ci        filter = av_bsf_get_by_name("h264_mp4toannexb");
216cabdff1aSopenharmony_ci        if (!filter) {
217cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb bitstream filter "
218cabdff1aSopenharmony_ci                   "required for H.264 streams\n");
219cabdff1aSopenharmony_ci            return AVERROR_BSF_NOT_FOUND;
220cabdff1aSopenharmony_ci        }
221cabdff1aSopenharmony_ci        ret = av_bsf_alloc(filter, &bsf);
222cabdff1aSopenharmony_ci        if (ret < 0)
223cabdff1aSopenharmony_ci            return ret;
224cabdff1aSopenharmony_ci        cs->bsf = bsf;
225cabdff1aSopenharmony_ci
226cabdff1aSopenharmony_ci        ret = avcodec_parameters_copy(bsf->par_in, st->codecpar);
227cabdff1aSopenharmony_ci        if (ret < 0)
228cabdff1aSopenharmony_ci           return ret;
229cabdff1aSopenharmony_ci
230cabdff1aSopenharmony_ci        ret = av_bsf_init(bsf);
231cabdff1aSopenharmony_ci        if (ret < 0)
232cabdff1aSopenharmony_ci            return ret;
233cabdff1aSopenharmony_ci
234cabdff1aSopenharmony_ci        ret = avcodec_parameters_copy(st->codecpar, bsf->par_out);
235cabdff1aSopenharmony_ci        if (ret < 0)
236cabdff1aSopenharmony_ci            return ret;
237cabdff1aSopenharmony_ci    }
238cabdff1aSopenharmony_ci    return 0;
239cabdff1aSopenharmony_ci}
240cabdff1aSopenharmony_ci
241cabdff1aSopenharmony_cistatic int match_streams_one_to_one(AVFormatContext *avf)
242cabdff1aSopenharmony_ci{
243cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
244cabdff1aSopenharmony_ci    AVStream *st;
245cabdff1aSopenharmony_ci    int i, ret;
246cabdff1aSopenharmony_ci
247cabdff1aSopenharmony_ci    for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {
248cabdff1aSopenharmony_ci        if (i < avf->nb_streams) {
249cabdff1aSopenharmony_ci            st = avf->streams[i];
250cabdff1aSopenharmony_ci        } else {
251cabdff1aSopenharmony_ci            if (!(st = avformat_new_stream(avf, NULL)))
252cabdff1aSopenharmony_ci                return AVERROR(ENOMEM);
253cabdff1aSopenharmony_ci        }
254cabdff1aSopenharmony_ci        if ((ret = copy_stream_props(st, cat->avf->streams[i])) < 0)
255cabdff1aSopenharmony_ci            return ret;
256cabdff1aSopenharmony_ci        cat->cur_file->streams[i].out_stream_index = i;
257cabdff1aSopenharmony_ci    }
258cabdff1aSopenharmony_ci    return 0;
259cabdff1aSopenharmony_ci}
260cabdff1aSopenharmony_ci
261cabdff1aSopenharmony_cistatic int match_streams_exact_id(AVFormatContext *avf)
262cabdff1aSopenharmony_ci{
263cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
264cabdff1aSopenharmony_ci    AVStream *st;
265cabdff1aSopenharmony_ci    int i, j, ret;
266cabdff1aSopenharmony_ci
267cabdff1aSopenharmony_ci    for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {
268cabdff1aSopenharmony_ci        st = cat->avf->streams[i];
269cabdff1aSopenharmony_ci        for (j = 0; j < avf->nb_streams; j++) {
270cabdff1aSopenharmony_ci            if (avf->streams[j]->id == st->id) {
271cabdff1aSopenharmony_ci                av_log(avf, AV_LOG_VERBOSE,
272cabdff1aSopenharmony_ci                       "Match slave stream #%d with stream #%d id 0x%x\n",
273cabdff1aSopenharmony_ci                       i, j, st->id);
274cabdff1aSopenharmony_ci                if ((ret = copy_stream_props(avf->streams[j], st)) < 0)
275cabdff1aSopenharmony_ci                    return ret;
276cabdff1aSopenharmony_ci                cat->cur_file->streams[i].out_stream_index = j;
277cabdff1aSopenharmony_ci            }
278cabdff1aSopenharmony_ci        }
279cabdff1aSopenharmony_ci    }
280cabdff1aSopenharmony_ci    return 0;
281cabdff1aSopenharmony_ci}
282cabdff1aSopenharmony_ci
283cabdff1aSopenharmony_cistatic int match_streams(AVFormatContext *avf)
284cabdff1aSopenharmony_ci{
285cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
286cabdff1aSopenharmony_ci    ConcatStream *map;
287cabdff1aSopenharmony_ci    int i, ret;
288cabdff1aSopenharmony_ci
289cabdff1aSopenharmony_ci    if (cat->cur_file->nb_streams >= cat->avf->nb_streams)
290cabdff1aSopenharmony_ci        return 0;
291cabdff1aSopenharmony_ci    map = av_realloc(cat->cur_file->streams,
292cabdff1aSopenharmony_ci                     cat->avf->nb_streams * sizeof(*map));
293cabdff1aSopenharmony_ci    if (!map)
294cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
295cabdff1aSopenharmony_ci    cat->cur_file->streams = map;
296cabdff1aSopenharmony_ci    memset(map + cat->cur_file->nb_streams, 0,
297cabdff1aSopenharmony_ci           (cat->avf->nb_streams - cat->cur_file->nb_streams) * sizeof(*map));
298cabdff1aSopenharmony_ci
299cabdff1aSopenharmony_ci    for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {
300cabdff1aSopenharmony_ci        map[i].out_stream_index = -1;
301cabdff1aSopenharmony_ci        if ((ret = detect_stream_specific(avf, i)) < 0)
302cabdff1aSopenharmony_ci            return ret;
303cabdff1aSopenharmony_ci    }
304cabdff1aSopenharmony_ci    switch (cat->stream_match_mode) {
305cabdff1aSopenharmony_ci    case MATCH_ONE_TO_ONE:
306cabdff1aSopenharmony_ci        ret = match_streams_one_to_one(avf);
307cabdff1aSopenharmony_ci        break;
308cabdff1aSopenharmony_ci    case MATCH_EXACT_ID:
309cabdff1aSopenharmony_ci        ret = match_streams_exact_id(avf);
310cabdff1aSopenharmony_ci        break;
311cabdff1aSopenharmony_ci    default:
312cabdff1aSopenharmony_ci        ret = AVERROR_BUG;
313cabdff1aSopenharmony_ci    }
314cabdff1aSopenharmony_ci    if (ret < 0)
315cabdff1aSopenharmony_ci        return ret;
316cabdff1aSopenharmony_ci    cat->cur_file->nb_streams = cat->avf->nb_streams;
317cabdff1aSopenharmony_ci    return 0;
318cabdff1aSopenharmony_ci}
319cabdff1aSopenharmony_ci
320cabdff1aSopenharmony_cistatic int64_t get_best_effort_duration(ConcatFile *file, AVFormatContext *avf)
321cabdff1aSopenharmony_ci{
322cabdff1aSopenharmony_ci    if (file->user_duration != AV_NOPTS_VALUE)
323cabdff1aSopenharmony_ci        return file->user_duration;
324cabdff1aSopenharmony_ci    if (file->outpoint != AV_NOPTS_VALUE)
325cabdff1aSopenharmony_ci        return file->outpoint - file->file_inpoint;
326cabdff1aSopenharmony_ci    if (avf->duration > 0)
327cabdff1aSopenharmony_ci        return avf->duration - (file->file_inpoint - file->file_start_time);
328cabdff1aSopenharmony_ci    if (file->next_dts != AV_NOPTS_VALUE)
329cabdff1aSopenharmony_ci        return file->next_dts - file->file_inpoint;
330cabdff1aSopenharmony_ci    return AV_NOPTS_VALUE;
331cabdff1aSopenharmony_ci}
332cabdff1aSopenharmony_ci
333cabdff1aSopenharmony_cistatic int open_file(AVFormatContext *avf, unsigned fileno)
334cabdff1aSopenharmony_ci{
335cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
336cabdff1aSopenharmony_ci    ConcatFile *file = &cat->files[fileno];
337cabdff1aSopenharmony_ci    AVDictionary *options = NULL;
338cabdff1aSopenharmony_ci    int ret;
339cabdff1aSopenharmony_ci
340cabdff1aSopenharmony_ci    if (cat->avf)
341cabdff1aSopenharmony_ci        avformat_close_input(&cat->avf);
342cabdff1aSopenharmony_ci
343cabdff1aSopenharmony_ci    cat->avf = avformat_alloc_context();
344cabdff1aSopenharmony_ci    if (!cat->avf)
345cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
346cabdff1aSopenharmony_ci
347cabdff1aSopenharmony_ci    cat->avf->flags |= avf->flags & ~AVFMT_FLAG_CUSTOM_IO;
348cabdff1aSopenharmony_ci    cat->avf->interrupt_callback = avf->interrupt_callback;
349cabdff1aSopenharmony_ci
350cabdff1aSopenharmony_ci    if ((ret = ff_copy_whiteblacklists(cat->avf, avf)) < 0)
351cabdff1aSopenharmony_ci        return ret;
352cabdff1aSopenharmony_ci
353cabdff1aSopenharmony_ci    ret = av_dict_copy(&options, file->options, 0);
354cabdff1aSopenharmony_ci    if (ret < 0)
355cabdff1aSopenharmony_ci        return ret;
356cabdff1aSopenharmony_ci
357cabdff1aSopenharmony_ci    if ((ret = avformat_open_input(&cat->avf, file->url, NULL, &options)) < 0 ||
358cabdff1aSopenharmony_ci        (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {
359cabdff1aSopenharmony_ci        av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);
360cabdff1aSopenharmony_ci        av_dict_free(&options);
361cabdff1aSopenharmony_ci        avformat_close_input(&cat->avf);
362cabdff1aSopenharmony_ci        return ret;
363cabdff1aSopenharmony_ci    }
364cabdff1aSopenharmony_ci    if (options) {
365cabdff1aSopenharmony_ci        av_log(avf, AV_LOG_WARNING, "Unused options for '%s'.\n", file->url);
366cabdff1aSopenharmony_ci        /* TODO log unused options once we have a proper string API */
367cabdff1aSopenharmony_ci        av_dict_free(&options);
368cabdff1aSopenharmony_ci    }
369cabdff1aSopenharmony_ci    cat->cur_file = file;
370cabdff1aSopenharmony_ci    file->start_time = !fileno ? 0 :
371cabdff1aSopenharmony_ci                       cat->files[fileno - 1].start_time +
372cabdff1aSopenharmony_ci                       cat->files[fileno - 1].duration;
373cabdff1aSopenharmony_ci    file->file_start_time = (cat->avf->start_time == AV_NOPTS_VALUE) ? 0 : cat->avf->start_time;
374cabdff1aSopenharmony_ci    file->file_inpoint = (file->inpoint == AV_NOPTS_VALUE) ? file->file_start_time : file->inpoint;
375cabdff1aSopenharmony_ci    file->duration = get_best_effort_duration(file, cat->avf);
376cabdff1aSopenharmony_ci
377cabdff1aSopenharmony_ci    if (cat->segment_time_metadata) {
378cabdff1aSopenharmony_ci        av_dict_set_int(&file->metadata, "lavf.concatdec.start_time", file->start_time, 0);
379cabdff1aSopenharmony_ci        if (file->duration != AV_NOPTS_VALUE)
380cabdff1aSopenharmony_ci            av_dict_set_int(&file->metadata, "lavf.concatdec.duration", file->duration, 0);
381cabdff1aSopenharmony_ci    }
382cabdff1aSopenharmony_ci
383cabdff1aSopenharmony_ci    if ((ret = match_streams(avf)) < 0)
384cabdff1aSopenharmony_ci        return ret;
385cabdff1aSopenharmony_ci    if (file->inpoint != AV_NOPTS_VALUE) {
386cabdff1aSopenharmony_ci       if ((ret = avformat_seek_file(cat->avf, -1, INT64_MIN, file->inpoint, file->inpoint, 0)) < 0)
387cabdff1aSopenharmony_ci           return ret;
388cabdff1aSopenharmony_ci    }
389cabdff1aSopenharmony_ci    return 0;
390cabdff1aSopenharmony_ci}
391cabdff1aSopenharmony_ci
392cabdff1aSopenharmony_cistatic int concat_read_close(AVFormatContext *avf)
393cabdff1aSopenharmony_ci{
394cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
395cabdff1aSopenharmony_ci    unsigned i, j;
396cabdff1aSopenharmony_ci
397cabdff1aSopenharmony_ci    for (i = 0; i < cat->nb_files; i++) {
398cabdff1aSopenharmony_ci        av_freep(&cat->files[i].url);
399cabdff1aSopenharmony_ci        for (j = 0; j < cat->files[i].nb_streams; j++) {
400cabdff1aSopenharmony_ci            if (cat->files[i].streams[j].bsf)
401cabdff1aSopenharmony_ci                av_bsf_free(&cat->files[i].streams[j].bsf);
402cabdff1aSopenharmony_ci        }
403cabdff1aSopenharmony_ci        av_freep(&cat->files[i].streams);
404cabdff1aSopenharmony_ci        av_dict_free(&cat->files[i].metadata);
405cabdff1aSopenharmony_ci        av_dict_free(&cat->files[i].options);
406cabdff1aSopenharmony_ci    }
407cabdff1aSopenharmony_ci    if (cat->avf)
408cabdff1aSopenharmony_ci        avformat_close_input(&cat->avf);
409cabdff1aSopenharmony_ci    av_freep(&cat->files);
410cabdff1aSopenharmony_ci    return 0;
411cabdff1aSopenharmony_ci}
412cabdff1aSopenharmony_ci
413cabdff1aSopenharmony_ci#define MAX_ARGS 3
414cabdff1aSopenharmony_ci#define NEEDS_UNSAFE   (1 << 0)
415cabdff1aSopenharmony_ci#define NEEDS_FILE     (1 << 1)
416cabdff1aSopenharmony_ci#define NEEDS_STREAM   (1 << 2)
417cabdff1aSopenharmony_ci
418cabdff1aSopenharmony_citypedef struct ParseSyntax {
419cabdff1aSopenharmony_ci    const char *keyword;
420cabdff1aSopenharmony_ci    char args[MAX_ARGS];
421cabdff1aSopenharmony_ci    uint8_t flags;
422cabdff1aSopenharmony_ci} ParseSyntax;
423cabdff1aSopenharmony_ci
424cabdff1aSopenharmony_citypedef enum ParseDirective {
425cabdff1aSopenharmony_ci   DIR_FFCONCAT,
426cabdff1aSopenharmony_ci   DIR_FILE,
427cabdff1aSopenharmony_ci   DIR_DURATION,
428cabdff1aSopenharmony_ci   DIR_INPOINT,
429cabdff1aSopenharmony_ci   DIR_OUTPOINT,
430cabdff1aSopenharmony_ci   DIR_FPMETA,
431cabdff1aSopenharmony_ci   DIR_FPMETAS,
432cabdff1aSopenharmony_ci   DIR_OPTION,
433cabdff1aSopenharmony_ci   DIR_STREAM,
434cabdff1aSopenharmony_ci   DIR_EXSID,
435cabdff1aSopenharmony_ci   DIR_STMETA,
436cabdff1aSopenharmony_ci   DIR_STCODEC,
437cabdff1aSopenharmony_ci   DIR_STEDATA,
438cabdff1aSopenharmony_ci   DIR_CHAPTER,
439cabdff1aSopenharmony_ci} ParseDirective;
440cabdff1aSopenharmony_ci
441cabdff1aSopenharmony_cistatic const ParseSyntax syntax[] = {
442cabdff1aSopenharmony_ci    [DIR_FFCONCAT ] = { "ffconcat",             "kk",   0 },
443cabdff1aSopenharmony_ci    [DIR_FILE     ] = { "file",                 "s",    0 },
444cabdff1aSopenharmony_ci    [DIR_DURATION ] = { "duration",             "d",    NEEDS_FILE },
445cabdff1aSopenharmony_ci    [DIR_INPOINT  ] = { "inpoint",              "d",    NEEDS_FILE },
446cabdff1aSopenharmony_ci    [DIR_OUTPOINT ] = { "outpoint",             "d",    NEEDS_FILE },
447cabdff1aSopenharmony_ci    [DIR_FPMETA   ] = { "file_packet_meta",     "ks",   NEEDS_FILE },
448cabdff1aSopenharmony_ci    [DIR_FPMETAS  ] = { "file_packet_metadata", "s",    NEEDS_FILE },
449cabdff1aSopenharmony_ci    [DIR_OPTION   ] = { "option",               "ks",   NEEDS_FILE | NEEDS_UNSAFE },
450cabdff1aSopenharmony_ci    [DIR_STREAM   ] = { "stream",               "",     0 },
451cabdff1aSopenharmony_ci    [DIR_EXSID    ] = { "exact_stream_id",      "i",    NEEDS_STREAM },
452cabdff1aSopenharmony_ci    [DIR_STMETA   ] = { "stream_meta",          "ks",   NEEDS_STREAM },
453cabdff1aSopenharmony_ci    [DIR_STCODEC  ] = { "stream_codec",         "k",    NEEDS_STREAM },
454cabdff1aSopenharmony_ci    [DIR_STEDATA  ] = { "stream_extradata",     "k",    NEEDS_STREAM },
455cabdff1aSopenharmony_ci    [DIR_CHAPTER  ] = { "chapter",              "idd",  0 },
456cabdff1aSopenharmony_ci};
457cabdff1aSopenharmony_ci
458cabdff1aSopenharmony_cistatic int concat_parse_script(AVFormatContext *avf)
459cabdff1aSopenharmony_ci{
460cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
461cabdff1aSopenharmony_ci    unsigned nb_files_alloc = 0;
462cabdff1aSopenharmony_ci    AVBPrint bp;
463cabdff1aSopenharmony_ci    uint8_t *cursor, *keyword;
464cabdff1aSopenharmony_ci    ConcatFile *file = NULL;
465cabdff1aSopenharmony_ci    AVStream *stream = NULL;
466cabdff1aSopenharmony_ci    AVChapter *chapter = NULL;
467cabdff1aSopenharmony_ci    unsigned line = 0, arg;
468cabdff1aSopenharmony_ci    const ParseSyntax *dir;
469cabdff1aSopenharmony_ci    char *arg_kw[MAX_ARGS];
470cabdff1aSopenharmony_ci    char *arg_str[MAX_ARGS] = { 0 };
471cabdff1aSopenharmony_ci    int64_t arg_int[MAX_ARGS];
472cabdff1aSopenharmony_ci    int ret;
473cabdff1aSopenharmony_ci
474cabdff1aSopenharmony_ci    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
475cabdff1aSopenharmony_ci
476cabdff1aSopenharmony_ci    while ((ret = ff_read_line_to_bprint_overwrite(avf->pb, &bp)) >= 0) {
477cabdff1aSopenharmony_ci        line++;
478cabdff1aSopenharmony_ci        cursor = bp.str;
479cabdff1aSopenharmony_ci        keyword = get_keyword(&cursor);
480cabdff1aSopenharmony_ci        if (!*keyword || *keyword == '#')
481cabdff1aSopenharmony_ci            continue;
482cabdff1aSopenharmony_ci        for (dir = syntax; dir < syntax + FF_ARRAY_ELEMS(syntax); dir++)
483cabdff1aSopenharmony_ci            if (!strcmp(dir->keyword, keyword))
484cabdff1aSopenharmony_ci                break;
485cabdff1aSopenharmony_ci        if (dir >= syntax + FF_ARRAY_ELEMS(syntax)) {
486cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n",
487cabdff1aSopenharmony_ci                   line, keyword);
488cabdff1aSopenharmony_ci            FAIL(AVERROR_INVALIDDATA);
489cabdff1aSopenharmony_ci        }
490cabdff1aSopenharmony_ci
491cabdff1aSopenharmony_ci        /* Flags check */
492cabdff1aSopenharmony_ci        if ((dir->flags & NEEDS_UNSAFE) && cat->safe) {
493cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_ERROR, "Line %d: %s not allowed if safe\n", line, keyword);
494cabdff1aSopenharmony_ci            FAIL(AVERROR_INVALIDDATA);
495cabdff1aSopenharmony_ci        }
496cabdff1aSopenharmony_ci        if ((dir->flags & NEEDS_FILE) && !cat->nb_files) {
497cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_ERROR, "Line %d: %s without file\n", line, keyword);
498cabdff1aSopenharmony_ci            FAIL(AVERROR_INVALIDDATA);
499cabdff1aSopenharmony_ci        }
500cabdff1aSopenharmony_ci        if ((dir->flags & NEEDS_STREAM) && !avf->nb_streams) {
501cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_ERROR, "Line %d: %s without stream\n", line, keyword);
502cabdff1aSopenharmony_ci            FAIL(AVERROR_INVALIDDATA);
503cabdff1aSopenharmony_ci        }
504cabdff1aSopenharmony_ci
505cabdff1aSopenharmony_ci        /* Arguments parsing */
506cabdff1aSopenharmony_ci        for (arg = 0; arg < FF_ARRAY_ELEMS(dir->args) && dir->args[arg]; arg++) {
507cabdff1aSopenharmony_ci            switch (dir->args[arg]) {
508cabdff1aSopenharmony_ci            case 'd': /* duration */
509cabdff1aSopenharmony_ci                arg_kw[arg] = get_keyword(&cursor);
510cabdff1aSopenharmony_ci                ret = av_parse_time(&arg_int[arg], arg_kw[arg], 1);
511cabdff1aSopenharmony_ci                if (ret < 0) {
512cabdff1aSopenharmony_ci                    av_log(avf, AV_LOG_ERROR, "Line %d: invalid duration '%s'\n",
513cabdff1aSopenharmony_ci                           line, arg_kw[arg]);
514cabdff1aSopenharmony_ci                    goto fail;
515cabdff1aSopenharmony_ci                }
516cabdff1aSopenharmony_ci                break;
517cabdff1aSopenharmony_ci            case 'i': /* integer */
518cabdff1aSopenharmony_ci                arg_int[arg] = strtol(get_keyword(&cursor), NULL, 0);
519cabdff1aSopenharmony_ci                break;
520cabdff1aSopenharmony_ci            case 'k': /* keyword */
521cabdff1aSopenharmony_ci                arg_kw[arg] = get_keyword(&cursor);
522cabdff1aSopenharmony_ci                break;
523cabdff1aSopenharmony_ci            case 's': /* string */
524cabdff1aSopenharmony_ci                av_assert0(!arg_str[arg]);
525cabdff1aSopenharmony_ci                arg_str[arg] = av_get_token((const char **)&cursor, SPACE_CHARS);
526cabdff1aSopenharmony_ci                if (!arg_str[arg])
527cabdff1aSopenharmony_ci                    FAIL(AVERROR(ENOMEM));
528cabdff1aSopenharmony_ci                if (!*arg_str[arg]) {
529cabdff1aSopenharmony_ci                    av_log(avf, AV_LOG_ERROR, "Line %d: string required\n", line);
530cabdff1aSopenharmony_ci                    FAIL(AVERROR_INVALIDDATA);
531cabdff1aSopenharmony_ci                }
532cabdff1aSopenharmony_ci                break;
533cabdff1aSopenharmony_ci            default:
534cabdff1aSopenharmony_ci                FAIL(AVERROR_BUG);
535cabdff1aSopenharmony_ci            }
536cabdff1aSopenharmony_ci        }
537cabdff1aSopenharmony_ci
538cabdff1aSopenharmony_ci        /* Directive action */
539cabdff1aSopenharmony_ci        switch ((ParseDirective)(dir - syntax)) {
540cabdff1aSopenharmony_ci
541cabdff1aSopenharmony_ci        case DIR_FFCONCAT:
542cabdff1aSopenharmony_ci            if (strcmp(arg_kw[0], "version") || strcmp(arg_kw[1], "1.0")) {
543cabdff1aSopenharmony_ci                av_log(avf, AV_LOG_ERROR, "Line %d: invalid version\n", line);
544cabdff1aSopenharmony_ci                FAIL(AVERROR_INVALIDDATA);
545cabdff1aSopenharmony_ci            }
546cabdff1aSopenharmony_ci            break;
547cabdff1aSopenharmony_ci
548cabdff1aSopenharmony_ci        case DIR_FILE:
549cabdff1aSopenharmony_ci            ret = add_file(avf, arg_str[0], &file, &nb_files_alloc);
550cabdff1aSopenharmony_ci            arg_str[0] = NULL;
551cabdff1aSopenharmony_ci            if (ret < 0)
552cabdff1aSopenharmony_ci                goto fail;
553cabdff1aSopenharmony_ci            break;
554cabdff1aSopenharmony_ci
555cabdff1aSopenharmony_ci        case DIR_DURATION:
556cabdff1aSopenharmony_ci            file->user_duration = arg_int[0];
557cabdff1aSopenharmony_ci            break;
558cabdff1aSopenharmony_ci
559cabdff1aSopenharmony_ci        case DIR_INPOINT:
560cabdff1aSopenharmony_ci            file->inpoint = arg_int[0];
561cabdff1aSopenharmony_ci            break;
562cabdff1aSopenharmony_ci
563cabdff1aSopenharmony_ci        case DIR_OUTPOINT:
564cabdff1aSopenharmony_ci            file->outpoint = arg_int[0];
565cabdff1aSopenharmony_ci            break;
566cabdff1aSopenharmony_ci
567cabdff1aSopenharmony_ci        case DIR_FPMETA:
568cabdff1aSopenharmony_ci            ret = av_dict_set(&file->metadata, arg_kw[0], arg_str[1], AV_DICT_DONT_STRDUP_VAL);
569cabdff1aSopenharmony_ci            arg_str[1] = NULL;
570cabdff1aSopenharmony_ci            if (ret < 0)
571cabdff1aSopenharmony_ci                FAIL(ret);
572cabdff1aSopenharmony_ci            break;
573cabdff1aSopenharmony_ci
574cabdff1aSopenharmony_ci        case DIR_FPMETAS:
575cabdff1aSopenharmony_ci            if ((ret = av_dict_parse_string(&file->metadata, arg_str[0], "=", "", 0)) < 0) {
576cabdff1aSopenharmony_ci                av_log(avf, AV_LOG_ERROR, "Line %d: failed to parse metadata string\n", line);
577cabdff1aSopenharmony_ci                FAIL(AVERROR_INVALIDDATA);
578cabdff1aSopenharmony_ci            }
579cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_WARNING,
580cabdff1aSopenharmony_ci                   "'file_packet_metadata key=value:key=value' is deprecated, "
581cabdff1aSopenharmony_ci                   "use multiple 'file_packet_meta key value' instead\n");
582cabdff1aSopenharmony_ci            av_freep(&arg_str[0]);
583cabdff1aSopenharmony_ci            break;
584cabdff1aSopenharmony_ci
585cabdff1aSopenharmony_ci        case DIR_OPTION:
586cabdff1aSopenharmony_ci            ret = av_dict_set(&file->options, arg_kw[0], arg_str[1], AV_DICT_DONT_STRDUP_VAL);
587cabdff1aSopenharmony_ci            arg_str[1] = NULL;
588cabdff1aSopenharmony_ci            if (ret < 0)
589cabdff1aSopenharmony_ci                FAIL(ret);
590cabdff1aSopenharmony_ci            break;
591cabdff1aSopenharmony_ci
592cabdff1aSopenharmony_ci        case DIR_STREAM:
593cabdff1aSopenharmony_ci            stream = avformat_new_stream(avf, NULL);
594cabdff1aSopenharmony_ci            if (!stream)
595cabdff1aSopenharmony_ci                FAIL(AVERROR(ENOMEM));
596cabdff1aSopenharmony_ci            break;
597cabdff1aSopenharmony_ci
598cabdff1aSopenharmony_ci        case DIR_EXSID:
599cabdff1aSopenharmony_ci            stream->id = arg_int[0];
600cabdff1aSopenharmony_ci            break;
601cabdff1aSopenharmony_ci        case DIR_STMETA:
602cabdff1aSopenharmony_ci            ret = av_dict_set(&stream->metadata, arg_kw[0], arg_str[1], AV_DICT_DONT_STRDUP_VAL);
603cabdff1aSopenharmony_ci            arg_str[1] = NULL;
604cabdff1aSopenharmony_ci            if (ret < 0)
605cabdff1aSopenharmony_ci                FAIL(ret);
606cabdff1aSopenharmony_ci            break;
607cabdff1aSopenharmony_ci
608cabdff1aSopenharmony_ci        case DIR_STCODEC: {
609cabdff1aSopenharmony_ci            const AVCodecDescriptor *codec = avcodec_descriptor_get_by_name(arg_kw[0]);
610cabdff1aSopenharmony_ci            if (!codec) {
611cabdff1aSopenharmony_ci                av_log(avf, AV_LOG_ERROR, "Line %d: codec '%s' not found\n", line, arg_kw[0]);
612cabdff1aSopenharmony_ci                FAIL(AVERROR_DECODER_NOT_FOUND);
613cabdff1aSopenharmony_ci            }
614cabdff1aSopenharmony_ci            stream->codecpar->codec_type = codec->type;
615cabdff1aSopenharmony_ci            stream->codecpar->codec_id = codec->id;
616cabdff1aSopenharmony_ci            break;
617cabdff1aSopenharmony_ci        }
618cabdff1aSopenharmony_ci
619cabdff1aSopenharmony_ci        case DIR_STEDATA: {
620cabdff1aSopenharmony_ci            int size = ff_hex_to_data(NULL, arg_kw[0]);
621cabdff1aSopenharmony_ci            ret = ff_alloc_extradata(stream->codecpar, size);
622cabdff1aSopenharmony_ci            if (ret < 0)
623cabdff1aSopenharmony_ci                FAIL(ret);
624cabdff1aSopenharmony_ci            ff_hex_to_data(stream->codecpar->extradata, arg_kw[0]);
625cabdff1aSopenharmony_ci            break;
626cabdff1aSopenharmony_ci        }
627cabdff1aSopenharmony_ci
628cabdff1aSopenharmony_ci        case DIR_CHAPTER:
629cabdff1aSopenharmony_ci            chapter = avpriv_new_chapter(avf, arg_int[0], AV_TIME_BASE_Q,
630cabdff1aSopenharmony_ci                                         arg_int[1], arg_int[2], NULL);
631cabdff1aSopenharmony_ci            if (!chapter)
632cabdff1aSopenharmony_ci                FAIL(ENOMEM);
633cabdff1aSopenharmony_ci            break;
634cabdff1aSopenharmony_ci
635cabdff1aSopenharmony_ci        default:
636cabdff1aSopenharmony_ci            FAIL(AVERROR_BUG);
637cabdff1aSopenharmony_ci        }
638cabdff1aSopenharmony_ci    }
639cabdff1aSopenharmony_ci
640cabdff1aSopenharmony_cifail:
641cabdff1aSopenharmony_ci    for (arg = 0; arg < MAX_ARGS; arg++)
642cabdff1aSopenharmony_ci        av_freep(&arg_str[arg]);
643cabdff1aSopenharmony_ci    av_bprint_finalize(&bp, NULL);
644cabdff1aSopenharmony_ci    return ret == AVERROR_EOF ? 0 : ret;
645cabdff1aSopenharmony_ci}
646cabdff1aSopenharmony_ci
647cabdff1aSopenharmony_cistatic int concat_read_header(AVFormatContext *avf)
648cabdff1aSopenharmony_ci{
649cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
650cabdff1aSopenharmony_ci    int64_t time = 0;
651cabdff1aSopenharmony_ci    unsigned i;
652cabdff1aSopenharmony_ci    int ret;
653cabdff1aSopenharmony_ci
654cabdff1aSopenharmony_ci    ret = concat_parse_script(avf);
655cabdff1aSopenharmony_ci    if (ret < 0)
656cabdff1aSopenharmony_ci        return ret;
657cabdff1aSopenharmony_ci    if (!cat->nb_files) {
658cabdff1aSopenharmony_ci        av_log(avf, AV_LOG_ERROR, "No files to concat\n");
659cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
660cabdff1aSopenharmony_ci    }
661cabdff1aSopenharmony_ci
662cabdff1aSopenharmony_ci    for (i = 0; i < cat->nb_files; i++) {
663cabdff1aSopenharmony_ci        if (cat->files[i].start_time == AV_NOPTS_VALUE)
664cabdff1aSopenharmony_ci            cat->files[i].start_time = time;
665cabdff1aSopenharmony_ci        else
666cabdff1aSopenharmony_ci            time = cat->files[i].start_time;
667cabdff1aSopenharmony_ci        if (cat->files[i].user_duration == AV_NOPTS_VALUE) {
668cabdff1aSopenharmony_ci            if (cat->files[i].inpoint == AV_NOPTS_VALUE || cat->files[i].outpoint == AV_NOPTS_VALUE ||
669cabdff1aSopenharmony_ci                cat->files[i].outpoint - (uint64_t)cat->files[i].inpoint != av_sat_sub64(cat->files[i].outpoint, cat->files[i].inpoint)
670cabdff1aSopenharmony_ci            )
671cabdff1aSopenharmony_ci                break;
672cabdff1aSopenharmony_ci            cat->files[i].user_duration = cat->files[i].outpoint - cat->files[i].inpoint;
673cabdff1aSopenharmony_ci        }
674cabdff1aSopenharmony_ci        cat->files[i].duration = cat->files[i].user_duration;
675cabdff1aSopenharmony_ci        time += cat->files[i].user_duration;
676cabdff1aSopenharmony_ci    }
677cabdff1aSopenharmony_ci    if (i == cat->nb_files) {
678cabdff1aSopenharmony_ci        avf->duration = time;
679cabdff1aSopenharmony_ci        cat->seekable = 1;
680cabdff1aSopenharmony_ci    }
681cabdff1aSopenharmony_ci
682cabdff1aSopenharmony_ci    cat->stream_match_mode = avf->nb_streams ? MATCH_EXACT_ID :
683cabdff1aSopenharmony_ci                                               MATCH_ONE_TO_ONE;
684cabdff1aSopenharmony_ci    if ((ret = open_file(avf, 0)) < 0)
685cabdff1aSopenharmony_ci        return ret;
686cabdff1aSopenharmony_ci
687cabdff1aSopenharmony_ci    return 0;
688cabdff1aSopenharmony_ci}
689cabdff1aSopenharmony_ci
690cabdff1aSopenharmony_cistatic int open_next_file(AVFormatContext *avf)
691cabdff1aSopenharmony_ci{
692cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
693cabdff1aSopenharmony_ci    unsigned fileno = cat->cur_file - cat->files;
694cabdff1aSopenharmony_ci
695cabdff1aSopenharmony_ci    cat->cur_file->duration = get_best_effort_duration(cat->cur_file, cat->avf);
696cabdff1aSopenharmony_ci
697cabdff1aSopenharmony_ci    if (++fileno >= cat->nb_files) {
698cabdff1aSopenharmony_ci        cat->eof = 1;
699cabdff1aSopenharmony_ci        return AVERROR_EOF;
700cabdff1aSopenharmony_ci    }
701cabdff1aSopenharmony_ci    return open_file(avf, fileno);
702cabdff1aSopenharmony_ci}
703cabdff1aSopenharmony_ci
704cabdff1aSopenharmony_cistatic int filter_packet(AVFormatContext *avf, ConcatStream *cs, AVPacket *pkt)
705cabdff1aSopenharmony_ci{
706cabdff1aSopenharmony_ci    int ret;
707cabdff1aSopenharmony_ci
708cabdff1aSopenharmony_ci    if (cs->bsf) {
709cabdff1aSopenharmony_ci        ret = av_bsf_send_packet(cs->bsf, pkt);
710cabdff1aSopenharmony_ci        if (ret < 0) {
711cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb filter "
712cabdff1aSopenharmony_ci                   "failed to send input packet\n");
713cabdff1aSopenharmony_ci            return ret;
714cabdff1aSopenharmony_ci        }
715cabdff1aSopenharmony_ci
716cabdff1aSopenharmony_ci        while (!ret)
717cabdff1aSopenharmony_ci            ret = av_bsf_receive_packet(cs->bsf, pkt);
718cabdff1aSopenharmony_ci
719cabdff1aSopenharmony_ci        if (ret < 0 && (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)) {
720cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb filter "
721cabdff1aSopenharmony_ci                   "failed to receive output packet\n");
722cabdff1aSopenharmony_ci            return ret;
723cabdff1aSopenharmony_ci        }
724cabdff1aSopenharmony_ci    }
725cabdff1aSopenharmony_ci    return 0;
726cabdff1aSopenharmony_ci}
727cabdff1aSopenharmony_ci
728cabdff1aSopenharmony_ci/* Returns true if the packet dts is greater or equal to the specified outpoint. */
729cabdff1aSopenharmony_cistatic int packet_after_outpoint(ConcatContext *cat, AVPacket *pkt)
730cabdff1aSopenharmony_ci{
731cabdff1aSopenharmony_ci    if (cat->cur_file->outpoint != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE) {
732cabdff1aSopenharmony_ci        return av_compare_ts(pkt->dts, cat->avf->streams[pkt->stream_index]->time_base,
733cabdff1aSopenharmony_ci                             cat->cur_file->outpoint, AV_TIME_BASE_Q) >= 0;
734cabdff1aSopenharmony_ci    }
735cabdff1aSopenharmony_ci    return 0;
736cabdff1aSopenharmony_ci}
737cabdff1aSopenharmony_ci
738cabdff1aSopenharmony_cistatic int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
739cabdff1aSopenharmony_ci{
740cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
741cabdff1aSopenharmony_ci    int ret;
742cabdff1aSopenharmony_ci    int64_t delta;
743cabdff1aSopenharmony_ci    ConcatStream *cs;
744cabdff1aSopenharmony_ci    AVStream *st;
745cabdff1aSopenharmony_ci    FFStream *sti;
746cabdff1aSopenharmony_ci
747cabdff1aSopenharmony_ci    if (cat->eof)
748cabdff1aSopenharmony_ci        return AVERROR_EOF;
749cabdff1aSopenharmony_ci
750cabdff1aSopenharmony_ci    if (!cat->avf)
751cabdff1aSopenharmony_ci        return AVERROR(EIO);
752cabdff1aSopenharmony_ci
753cabdff1aSopenharmony_ci    while (1) {
754cabdff1aSopenharmony_ci        ret = av_read_frame(cat->avf, pkt);
755cabdff1aSopenharmony_ci        if (ret == AVERROR_EOF) {
756cabdff1aSopenharmony_ci            if ((ret = open_next_file(avf)) < 0)
757cabdff1aSopenharmony_ci                return ret;
758cabdff1aSopenharmony_ci            continue;
759cabdff1aSopenharmony_ci        }
760cabdff1aSopenharmony_ci        if (ret < 0)
761cabdff1aSopenharmony_ci            return ret;
762cabdff1aSopenharmony_ci        if ((ret = match_streams(avf)) < 0) {
763cabdff1aSopenharmony_ci            return ret;
764cabdff1aSopenharmony_ci        }
765cabdff1aSopenharmony_ci        if (packet_after_outpoint(cat, pkt)) {
766cabdff1aSopenharmony_ci            av_packet_unref(pkt);
767cabdff1aSopenharmony_ci            if ((ret = open_next_file(avf)) < 0)
768cabdff1aSopenharmony_ci                return ret;
769cabdff1aSopenharmony_ci            continue;
770cabdff1aSopenharmony_ci        }
771cabdff1aSopenharmony_ci        cs = &cat->cur_file->streams[pkt->stream_index];
772cabdff1aSopenharmony_ci        if (cs->out_stream_index < 0) {
773cabdff1aSopenharmony_ci            av_packet_unref(pkt);
774cabdff1aSopenharmony_ci            continue;
775cabdff1aSopenharmony_ci        }
776cabdff1aSopenharmony_ci        break;
777cabdff1aSopenharmony_ci    }
778cabdff1aSopenharmony_ci    if ((ret = filter_packet(avf, cs, pkt)) < 0)
779cabdff1aSopenharmony_ci        return ret;
780cabdff1aSopenharmony_ci
781cabdff1aSopenharmony_ci    st = cat->avf->streams[pkt->stream_index];
782cabdff1aSopenharmony_ci    sti = ffstream(st);
783cabdff1aSopenharmony_ci    av_log(avf, AV_LOG_DEBUG, "file:%d stream:%d pts:%s pts_time:%s dts:%s dts_time:%s",
784cabdff1aSopenharmony_ci           (unsigned)(cat->cur_file - cat->files), pkt->stream_index,
785cabdff1aSopenharmony_ci           av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
786cabdff1aSopenharmony_ci           av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
787cabdff1aSopenharmony_ci
788cabdff1aSopenharmony_ci    delta = av_rescale_q(cat->cur_file->start_time - cat->cur_file->file_inpoint,
789cabdff1aSopenharmony_ci                         AV_TIME_BASE_Q,
790cabdff1aSopenharmony_ci                         cat->avf->streams[pkt->stream_index]->time_base);
791cabdff1aSopenharmony_ci    if (pkt->pts != AV_NOPTS_VALUE)
792cabdff1aSopenharmony_ci        pkt->pts += delta;
793cabdff1aSopenharmony_ci    if (pkt->dts != AV_NOPTS_VALUE)
794cabdff1aSopenharmony_ci        pkt->dts += delta;
795cabdff1aSopenharmony_ci    av_log(avf, AV_LOG_DEBUG, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
796cabdff1aSopenharmony_ci           av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
797cabdff1aSopenharmony_ci           av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
798cabdff1aSopenharmony_ci    if (cat->cur_file->metadata) {
799cabdff1aSopenharmony_ci        size_t metadata_len;
800cabdff1aSopenharmony_ci        char* packed_metadata = av_packet_pack_dictionary(cat->cur_file->metadata, &metadata_len);
801cabdff1aSopenharmony_ci        if (!packed_metadata)
802cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
803cabdff1aSopenharmony_ci        ret = av_packet_add_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA,
804cabdff1aSopenharmony_ci                                      packed_metadata, metadata_len);
805cabdff1aSopenharmony_ci        if (ret < 0) {
806cabdff1aSopenharmony_ci            av_freep(&packed_metadata);
807cabdff1aSopenharmony_ci            return ret;
808cabdff1aSopenharmony_ci        }
809cabdff1aSopenharmony_ci    }
810cabdff1aSopenharmony_ci
811cabdff1aSopenharmony_ci    if (cat->cur_file->duration == AV_NOPTS_VALUE && sti->cur_dts != AV_NOPTS_VALUE) {
812cabdff1aSopenharmony_ci        int64_t next_dts = av_rescale_q(sti->cur_dts, st->time_base, AV_TIME_BASE_Q);
813cabdff1aSopenharmony_ci        if (cat->cur_file->next_dts == AV_NOPTS_VALUE || next_dts > cat->cur_file->next_dts) {
814cabdff1aSopenharmony_ci            cat->cur_file->next_dts = next_dts;
815cabdff1aSopenharmony_ci        }
816cabdff1aSopenharmony_ci    }
817cabdff1aSopenharmony_ci
818cabdff1aSopenharmony_ci    pkt->stream_index = cs->out_stream_index;
819cabdff1aSopenharmony_ci    return 0;
820cabdff1aSopenharmony_ci}
821cabdff1aSopenharmony_ci
822cabdff1aSopenharmony_cistatic int try_seek(AVFormatContext *avf, int stream,
823cabdff1aSopenharmony_ci                    int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
824cabdff1aSopenharmony_ci{
825cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
826cabdff1aSopenharmony_ci    int64_t t0 = cat->cur_file->start_time - cat->cur_file->file_inpoint;
827cabdff1aSopenharmony_ci
828cabdff1aSopenharmony_ci    ts -= t0;
829cabdff1aSopenharmony_ci    min_ts = min_ts == INT64_MIN ? INT64_MIN : min_ts - t0;
830cabdff1aSopenharmony_ci    max_ts = max_ts == INT64_MAX ? INT64_MAX : max_ts - t0;
831cabdff1aSopenharmony_ci    if (stream >= 0) {
832cabdff1aSopenharmony_ci        if (stream >= cat->avf->nb_streams)
833cabdff1aSopenharmony_ci            return AVERROR(EIO);
834cabdff1aSopenharmony_ci        ff_rescale_interval(AV_TIME_BASE_Q, cat->avf->streams[stream]->time_base,
835cabdff1aSopenharmony_ci                            &min_ts, &ts, &max_ts);
836cabdff1aSopenharmony_ci    }
837cabdff1aSopenharmony_ci    return avformat_seek_file(cat->avf, stream, min_ts, ts, max_ts, flags);
838cabdff1aSopenharmony_ci}
839cabdff1aSopenharmony_ci
840cabdff1aSopenharmony_cistatic int real_seek(AVFormatContext *avf, int stream,
841cabdff1aSopenharmony_ci                     int64_t min_ts, int64_t ts, int64_t max_ts, int flags, AVFormatContext *cur_avf)
842cabdff1aSopenharmony_ci{
843cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
844cabdff1aSopenharmony_ci    int ret, left, right;
845cabdff1aSopenharmony_ci
846cabdff1aSopenharmony_ci    if (stream >= 0) {
847cabdff1aSopenharmony_ci        if (stream >= avf->nb_streams)
848cabdff1aSopenharmony_ci            return AVERROR(EINVAL);
849cabdff1aSopenharmony_ci        ff_rescale_interval(avf->streams[stream]->time_base, AV_TIME_BASE_Q,
850cabdff1aSopenharmony_ci                            &min_ts, &ts, &max_ts);
851cabdff1aSopenharmony_ci    }
852cabdff1aSopenharmony_ci
853cabdff1aSopenharmony_ci    left  = 0;
854cabdff1aSopenharmony_ci    right = cat->nb_files;
855cabdff1aSopenharmony_ci
856cabdff1aSopenharmony_ci    /* Always support seek to start */
857cabdff1aSopenharmony_ci    if (ts <= 0)
858cabdff1aSopenharmony_ci        right = 1;
859cabdff1aSopenharmony_ci    else if (!cat->seekable)
860cabdff1aSopenharmony_ci        return AVERROR(ESPIPE); /* XXX: can we use it? */
861cabdff1aSopenharmony_ci
862cabdff1aSopenharmony_ci    while (right - left > 1) {
863cabdff1aSopenharmony_ci        int mid = (left + right) / 2;
864cabdff1aSopenharmony_ci        if (ts < cat->files[mid].start_time)
865cabdff1aSopenharmony_ci            right = mid;
866cabdff1aSopenharmony_ci        else
867cabdff1aSopenharmony_ci            left  = mid;
868cabdff1aSopenharmony_ci    }
869cabdff1aSopenharmony_ci
870cabdff1aSopenharmony_ci    if (cat->cur_file != &cat->files[left]) {
871cabdff1aSopenharmony_ci        if ((ret = open_file(avf, left)) < 0)
872cabdff1aSopenharmony_ci            return ret;
873cabdff1aSopenharmony_ci    } else {
874cabdff1aSopenharmony_ci        cat->avf = cur_avf;
875cabdff1aSopenharmony_ci    }
876cabdff1aSopenharmony_ci
877cabdff1aSopenharmony_ci    ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
878cabdff1aSopenharmony_ci    if (ret < 0 &&
879cabdff1aSopenharmony_ci        left < cat->nb_files - 1 &&
880cabdff1aSopenharmony_ci        cat->files[left + 1].start_time < max_ts) {
881cabdff1aSopenharmony_ci        if (cat->cur_file == &cat->files[left])
882cabdff1aSopenharmony_ci            cat->avf = NULL;
883cabdff1aSopenharmony_ci        if ((ret = open_file(avf, left + 1)) < 0)
884cabdff1aSopenharmony_ci            return ret;
885cabdff1aSopenharmony_ci        ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
886cabdff1aSopenharmony_ci    }
887cabdff1aSopenharmony_ci    return ret;
888cabdff1aSopenharmony_ci}
889cabdff1aSopenharmony_ci
890cabdff1aSopenharmony_cistatic int concat_seek(AVFormatContext *avf, int stream,
891cabdff1aSopenharmony_ci                       int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
892cabdff1aSopenharmony_ci{
893cabdff1aSopenharmony_ci    ConcatContext *cat = avf->priv_data;
894cabdff1aSopenharmony_ci    ConcatFile *cur_file_saved = cat->cur_file;
895cabdff1aSopenharmony_ci    AVFormatContext *cur_avf_saved = cat->avf;
896cabdff1aSopenharmony_ci    int ret;
897cabdff1aSopenharmony_ci
898cabdff1aSopenharmony_ci    if (flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))
899cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
900cabdff1aSopenharmony_ci    cat->avf = NULL;
901cabdff1aSopenharmony_ci    if ((ret = real_seek(avf, stream, min_ts, ts, max_ts, flags, cur_avf_saved)) < 0) {
902cabdff1aSopenharmony_ci        if (cat->cur_file != cur_file_saved) {
903cabdff1aSopenharmony_ci            if (cat->avf)
904cabdff1aSopenharmony_ci                avformat_close_input(&cat->avf);
905cabdff1aSopenharmony_ci        }
906cabdff1aSopenharmony_ci        cat->avf      = cur_avf_saved;
907cabdff1aSopenharmony_ci        cat->cur_file = cur_file_saved;
908cabdff1aSopenharmony_ci    } else {
909cabdff1aSopenharmony_ci        if (cat->cur_file != cur_file_saved) {
910cabdff1aSopenharmony_ci            avformat_close_input(&cur_avf_saved);
911cabdff1aSopenharmony_ci        }
912cabdff1aSopenharmony_ci        cat->eof = 0;
913cabdff1aSopenharmony_ci    }
914cabdff1aSopenharmony_ci    return ret;
915cabdff1aSopenharmony_ci}
916cabdff1aSopenharmony_ci
917cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(ConcatContext, x)
918cabdff1aSopenharmony_ci#define DEC AV_OPT_FLAG_DECODING_PARAM
919cabdff1aSopenharmony_ci
920cabdff1aSopenharmony_cistatic const AVOption options[] = {
921cabdff1aSopenharmony_ci    { "safe", "enable safe mode",
922cabdff1aSopenharmony_ci      OFFSET(safe), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DEC },
923cabdff1aSopenharmony_ci    { "auto_convert", "automatically convert bitstream format",
924cabdff1aSopenharmony_ci      OFFSET(auto_convert), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DEC },
925cabdff1aSopenharmony_ci    { "segment_time_metadata", "output file segment start time and duration as packet metadata",
926cabdff1aSopenharmony_ci      OFFSET(segment_time_metadata), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC },
927cabdff1aSopenharmony_ci    { NULL }
928cabdff1aSopenharmony_ci};
929cabdff1aSopenharmony_ci
930cabdff1aSopenharmony_cistatic const AVClass concat_class = {
931cabdff1aSopenharmony_ci    .class_name = "concat demuxer",
932cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
933cabdff1aSopenharmony_ci    .option     = options,
934cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
935cabdff1aSopenharmony_ci};
936cabdff1aSopenharmony_ci
937cabdff1aSopenharmony_ci
938cabdff1aSopenharmony_ciconst AVInputFormat ff_concat_demuxer = {
939cabdff1aSopenharmony_ci    .name           = "concat",
940cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
941cabdff1aSopenharmony_ci    .priv_data_size = sizeof(ConcatContext),
942cabdff1aSopenharmony_ci    .flags_internal = FF_FMT_INIT_CLEANUP,
943cabdff1aSopenharmony_ci    .read_probe     = concat_probe,
944cabdff1aSopenharmony_ci    .read_header    = concat_read_header,
945cabdff1aSopenharmony_ci    .read_packet    = concat_read_packet,
946cabdff1aSopenharmony_ci    .read_close     = concat_read_close,
947cabdff1aSopenharmony_ci    .read_seek2     = concat_seek,
948cabdff1aSopenharmony_ci    .priv_class     = &concat_class,
949cabdff1aSopenharmony_ci};
950