00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "libavutil/avstring.h"
00022 #include "avformat.h"
00023 #include "internal.h"
00024
00025 typedef struct {
00026 char *url;
00027 int64_t start_time;
00028 int64_t duration;
00029 } ConcatFile;
00030
00031 typedef struct {
00032 ConcatFile *files;
00033 ConcatFile *cur_file;
00034 unsigned nb_files;
00035 AVFormatContext *avf;
00036 } ConcatContext;
00037
00038 static int concat_probe(AVProbeData *probe)
00039 {
00040 return 0;
00041 }
00042
00043 static char *get_keyword(uint8_t **cursor)
00044 {
00045 char *ret = *cursor += strspn(*cursor, SPACE_CHARS);
00046 *cursor += strcspn(*cursor, SPACE_CHARS);
00047 if (**cursor) {
00048 *((*cursor)++) = 0;
00049 *cursor += strspn(*cursor, SPACE_CHARS);
00050 }
00051 return ret;
00052 }
00053
00054 #define FAIL(retcode) do { ret = (retcode); goto fail; } while(0)
00055
00056 static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,
00057 unsigned *nb_files_alloc)
00058 {
00059 ConcatContext *cat = avf->priv_data;
00060 ConcatFile *file;
00061 char *url;
00062 size_t url_len;
00063
00064 url_len = strlen(avf->filename) + strlen(filename) + 16;
00065 if (!(url = av_malloc(url_len)))
00066 return AVERROR(ENOMEM);
00067 ff_make_absolute_url(url, url_len, avf->filename, filename);
00068 av_free(filename);
00069
00070 if (cat->nb_files >= *nb_files_alloc) {
00071 unsigned n = FFMAX(*nb_files_alloc * 2, 16);
00072 ConcatFile *new_files;
00073 if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) ||
00074 !(new_files = av_realloc(cat->files, n * sizeof(*cat->files))))
00075 return AVERROR(ENOMEM);
00076 cat->files = new_files;
00077 *nb_files_alloc = n;
00078 }
00079
00080 file = &cat->files[cat->nb_files++];
00081 memset(file, 0, sizeof(*file));
00082 *rfile = file;
00083
00084 file->url = url;
00085 file->start_time = AV_NOPTS_VALUE;
00086 file->duration = AV_NOPTS_VALUE;
00087
00088 return 0;
00089 }
00090
00091 static int open_file(AVFormatContext *avf, unsigned fileno)
00092 {
00093 ConcatContext *cat = avf->priv_data;
00094 ConcatFile *file = &cat->files[fileno];
00095 int ret;
00096
00097 if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 ||
00098 (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {
00099 av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);
00100 return ret;
00101 }
00102 cat->cur_file = file;
00103 if (file->start_time == AV_NOPTS_VALUE)
00104 file->start_time = !fileno ? 0 :
00105 cat->files[fileno - 1].start_time +
00106 cat->files[fileno - 1].duration;
00107 return 0;
00108 }
00109
00110 static int concat_read_close(AVFormatContext *avf)
00111 {
00112 ConcatContext *cat = avf->priv_data;
00113 unsigned i;
00114
00115 if (cat->avf)
00116 avformat_close_input(&cat->avf);
00117 for (i = 0; i < cat->nb_files; i++)
00118 av_freep(&cat->files[i].url);
00119 av_freep(&cat->files);
00120 return 0;
00121 }
00122
00123 static int concat_read_header(AVFormatContext *avf)
00124 {
00125 ConcatContext *cat = avf->priv_data;
00126 uint8_t buf[4096];
00127 uint8_t *cursor, *keyword;
00128 int ret, line = 0, i;
00129 unsigned nb_files_alloc = 0;
00130 ConcatFile *file = NULL;
00131 AVStream *st, *source_st;
00132
00133 while (1) {
00134 if ((ret = ff_get_line(avf->pb, buf, sizeof(buf))) <= 0)
00135 break;
00136 line++;
00137 cursor = buf;
00138 keyword = get_keyword(&cursor);
00139 if (!*keyword || *keyword == '#')
00140 continue;
00141
00142 if (!strcmp(keyword, "file")) {
00143 char *filename = av_get_token((const char **)&cursor, SPACE_CHARS);
00144 if (!filename) {
00145 av_log(avf, AV_LOG_ERROR, "Line %d: filename required\n", line);
00146 FAIL(AVERROR_INVALIDDATA);
00147 }
00148 if ((ret = add_file(avf, filename, &file, &nb_files_alloc)) < 0)
00149 FAIL(ret);
00150 } else {
00151 av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n",
00152 line, keyword);
00153 FAIL(AVERROR_INVALIDDATA);
00154 }
00155 }
00156 if (ret < 0)
00157 FAIL(ret);
00158
00159 if ((ret = open_file(avf, 0)) < 0)
00160 FAIL(ret);
00161 for (i = 0; i < cat->avf->nb_streams; i++) {
00162 if (!(st = avformat_new_stream(avf, NULL)))
00163 FAIL(AVERROR(ENOMEM));
00164 source_st = cat->avf->streams[i];
00165 if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0)
00166 FAIL(ret);
00167 st->r_frame_rate = source_st->r_frame_rate;
00168 st->avg_frame_rate = source_st->avg_frame_rate;
00169 st->time_base = source_st->time_base;
00170 st->sample_aspect_ratio = source_st->sample_aspect_ratio;
00171 }
00172
00173 return 0;
00174
00175 fail:
00176 concat_read_close(avf);
00177 return ret;
00178 }
00179
00180 static int open_next_file(AVFormatContext *avf)
00181 {
00182 ConcatContext *cat = avf->priv_data;
00183 unsigned fileno = cat->cur_file - cat->files;
00184
00185 if (cat->cur_file->duration == AV_NOPTS_VALUE)
00186 cat->cur_file->duration = cat->avf->duration;
00187
00188 if (++fileno >= cat->nb_files)
00189 return AVERROR_EOF;
00190 avformat_close_input(&cat->avf);
00191 return open_file(avf, fileno);
00192 }
00193
00194 static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
00195 {
00196 ConcatContext *cat = avf->priv_data;
00197 int ret;
00198 int64_t delta;
00199
00200 while (1) {
00201 if ((ret = av_read_frame(cat->avf, pkt)) != AVERROR_EOF ||
00202 (ret = open_next_file(avf)) < 0)
00203 break;
00204 }
00205 delta = av_rescale_q(cat->cur_file->start_time - cat->avf->start_time,
00206 AV_TIME_BASE_Q,
00207 cat->avf->streams[pkt->stream_index]->time_base);
00208 if (pkt->pts != AV_NOPTS_VALUE)
00209 pkt->pts += delta;
00210 if (pkt->dts != AV_NOPTS_VALUE)
00211 pkt->dts += delta;
00212 return ret;
00213 }
00214
00215 AVInputFormat ff_concat_demuxer = {
00216 .name = "concat",
00217 .long_name = NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
00218 .priv_data_size = sizeof(ConcatContext),
00219 .read_probe = concat_probe,
00220 .read_header = concat_read_header,
00221 .read_packet = concat_read_packet,
00222 .read_close = concat_read_close,
00223 };