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