FFmpeg
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
concatdec.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 Nicolas George
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include "libavutil/avstring.h"
22 #include "avformat.h"
23 #include "internal.h"
24 
25 typedef struct {
26  char *url;
27  int64_t start_time;
28  int64_t duration;
29 } ConcatFile;
30 
31 typedef struct {
34  unsigned nb_files;
37 
39 {
40  return 0;
41 }
42 
43 static char *get_keyword(uint8_t **cursor)
44 {
45  char *ret = *cursor += strspn(*cursor, SPACE_CHARS);
46  *cursor += strcspn(*cursor, SPACE_CHARS);
47  if (**cursor) {
48  *((*cursor)++) = 0;
49  *cursor += strspn(*cursor, SPACE_CHARS);
50  }
51  return ret;
52 }
53 
54 #define FAIL(retcode) do { ret = (retcode); goto fail; } while(0)
55 
56 static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,
57  unsigned *nb_files_alloc)
58 {
59  ConcatContext *cat = avf->priv_data;
60  ConcatFile *file;
61  char *url;
62  size_t url_len;
63 
64  url_len = strlen(avf->filename) + strlen(filename) + 16;
65  if (!(url = av_malloc(url_len)))
66  return AVERROR(ENOMEM);
67  ff_make_absolute_url(url, url_len, avf->filename, filename);
68  av_free(filename);
69 
70  if (cat->nb_files >= *nb_files_alloc) {
71  size_t n = FFMAX(*nb_files_alloc * 2, 16);
72  ConcatFile *new_files;
73  if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) ||
74  !(new_files = av_realloc(cat->files, n * sizeof(*cat->files))))
75  return AVERROR(ENOMEM);
76  cat->files = new_files;
77  *nb_files_alloc = n;
78  }
79 
80  file = &cat->files[cat->nb_files++];
81  memset(file, 0, sizeof(*file));
82  *rfile = file;
83 
84  file->url = url;
85  file->start_time = AV_NOPTS_VALUE;
86  file->duration = AV_NOPTS_VALUE;
87 
88  return 0;
89 }
90 
91 static int open_file(AVFormatContext *avf, unsigned fileno)
92 {
93  ConcatContext *cat = avf->priv_data;
94  ConcatFile *file = &cat->files[fileno];
95  int ret;
96 
97  if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 ||
98  (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {
99  av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);
100  return ret;
101  }
102  cat->cur_file = file;
103  if (file->start_time == AV_NOPTS_VALUE)
104  file->start_time = !fileno ? 0 :
105  cat->files[fileno - 1].start_time +
106  cat->files[fileno - 1].duration;
107  return 0;
108 }
109 
111 {
112  ConcatContext *cat = avf->priv_data;
113  unsigned i;
114 
115  if (cat->avf)
116  avformat_close_input(&cat->avf);
117  for (i = 0; i < cat->nb_files; i++)
118  av_freep(&cat->files[i].url);
119  av_freep(&cat->files);
120  return 0;
121 }
122 
124 {
125  ConcatContext *cat = avf->priv_data;
126  uint8_t buf[4096];
127  uint8_t *cursor, *keyword;
128  int ret, line = 0, i;
129  unsigned nb_files_alloc = 0;
130  ConcatFile *file = NULL;
131  AVStream *st, *source_st;
132 
133  while (1) {
134  if ((ret = ff_get_line(avf->pb, buf, sizeof(buf))) <= 0)
135  break;
136  line++;
137  cursor = buf;
138  keyword = get_keyword(&cursor);
139  if (!*keyword || *keyword == '#')
140  continue;
141 
142  if (!strcmp(keyword, "file")) {
143  char *filename = av_get_token((const char **)&cursor, SPACE_CHARS);
144  if (!filename) {
145  av_log(avf, AV_LOG_ERROR, "Line %d: filename required\n", line);
147  }
148  if ((ret = add_file(avf, filename, &file, &nb_files_alloc)) < 0)
149  FAIL(ret);
150  } else {
151  av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n",
152  line, keyword);
154  }
155  }
156  if (ret < 0)
157  FAIL(ret);
158 
159  if ((ret = open_file(avf, 0)) < 0)
160  FAIL(ret);
161  for (i = 0; i < cat->avf->nb_streams; i++) {
162  if (!(st = avformat_new_stream(avf, NULL)))
163  FAIL(AVERROR(ENOMEM));
164  source_st = cat->avf->streams[i];
165  if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0)
166  FAIL(ret);
167  st->r_frame_rate = source_st->r_frame_rate;
168  st->avg_frame_rate = source_st->avg_frame_rate;
169  st->time_base = source_st->time_base;
170  st->sample_aspect_ratio = source_st->sample_aspect_ratio;
171  }
172 
173  return 0;
174 
175 fail:
176  concat_read_close(avf);
177  return ret;
178 }
179 
181 {
182  ConcatContext *cat = avf->priv_data;
183  unsigned fileno = cat->cur_file - cat->files;
184 
185  if (cat->cur_file->duration == AV_NOPTS_VALUE)
186  cat->cur_file->duration = cat->avf->duration;
187 
188  if (++fileno >= cat->nb_files)
189  return AVERROR_EOF;
190  avformat_close_input(&cat->avf);
191  return open_file(avf, fileno);
192 }
193 
195 {
196  ConcatContext *cat = avf->priv_data;
197  int ret;
198  int64_t delta;
199 
200  while (1) {
201  if ((ret = av_read_frame(cat->avf, pkt)) != AVERROR_EOF ||
202  (ret = open_next_file(avf)) < 0)
203  break;
204  }
205  delta = av_rescale_q(cat->cur_file->start_time - cat->avf->start_time,
207  cat->avf->streams[pkt->stream_index]->time_base);
208  if (pkt->pts != AV_NOPTS_VALUE)
209  pkt->pts += delta;
210  if (pkt->dts != AV_NOPTS_VALUE)
211  pkt->dts += delta;
212  return ret;
213 }
214 
216  .name = "concat",
217  .long_name = NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
218  .priv_data_size = sizeof(ConcatContext),
223 };