FFmpeg
sidxindex.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 Martin Storsjo
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
8  * License 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <stdio.h>
22 #include <string.h>
23 
24 #include "libavformat/avformat.h"
25 #include "libavutil/avstring.h"
26 #include "libavutil/intreadwrite.h"
27 #include "libavutil/mathematics.h"
28 
29 static int usage(const char *argv0, int ret)
30 {
31  fprintf(stderr, "%s -out foo.mpd file1\n", argv0);
32  return ret;
33 }
34 
35 struct Track {
36  const char *name;
37  int64_t duration;
38  int bitrate;
39  int track_id;
40  int is_audio, is_video;
41  int width, height;
42  int sample_rate, channels;
43  int timescale;
44  char codec_str[30];
46 };
47 
48 struct Tracks {
49  int nb_tracks;
50  int64_t duration;
51  struct Track **tracks;
53 };
54 
55 static void set_codec_str(AVCodecParameters *codecpar, char *str, int size)
56 {
57  switch (codecpar->codec_id) {
58  case AV_CODEC_ID_H264:
59  snprintf(str, size, "avc1");
60  if (codecpar->extradata_size >= 4 && codecpar->extradata[0] == 1) {
61  av_strlcatf(str, size, ".%02x%02x%02x",
62  codecpar->extradata[1], codecpar->extradata[2],
63  codecpar->extradata[3]);
64  }
65  break;
66  case AV_CODEC_ID_AAC:
67  snprintf(str, size, "mp4a.40"); // 0x40 is the mp4 object type for AAC
68  if (codecpar->extradata_size >= 2) {
69  int aot = codecpar->extradata[0] >> 3;
70  if (aot == 31)
71  aot = ((AV_RB16(codecpar->extradata) >> 5) & 0x3f) + 32;
72  av_strlcatf(str, size, ".%d", aot);
73  }
74  break;
75  }
76 }
77 
78 static int find_sidx(struct Tracks *tracks, int start_index,
79  const char *file)
80 {
81  int err = 0;
82  AVIOContext *f = NULL;
83  int i;
84 
85  if ((err = avio_open2(&f, file, AVIO_FLAG_READ, NULL, NULL)) < 0)
86  goto fail;
87 
88  while (!f->eof_reached) {
89  int64_t pos = avio_tell(f);
90  int32_t size, tag;
91 
92  size = avio_rb32(f);
93  tag = avio_rb32(f);
94  if (size < 8)
95  break;
96  if (tag == MKBETAG('s', 'i', 'd', 'x')) {
97  for (i = start_index; i < tracks->nb_tracks; i++) {
98  struct Track *track = tracks->tracks[i];
99  if (!track->sidx_start) {
100  track->sidx_start = pos;
101  track->sidx_length = size;
102  } else if (pos == track->sidx_start + track->sidx_length) {
103  track->sidx_length = pos + size - track->sidx_start;
104  }
105  }
106  }
107  if (avio_seek(f, pos + size, SEEK_SET) != pos + size)
108  break;
109  }
110 
111 fail:
112  if (f)
113  avio_close(f);
114  return err;
115 }
116 
117 static int handle_file(struct Tracks *tracks, const char *file)
118 {
120  int err = 0, i, orig_tracks = tracks->nb_tracks;
121  char errbuf[50], *ptr;
122  struct Track *track;
123 
124  err = avformat_open_input(&ctx, file, NULL, NULL);
125  if (err < 0) {
126  av_strerror(err, errbuf, sizeof(errbuf));
127  fprintf(stderr, "Unable to open %s: %s\n", file, errbuf);
128  return 1;
129  }
130 
131  err = avformat_find_stream_info(ctx, NULL);
132  if (err < 0) {
133  av_strerror(err, errbuf, sizeof(errbuf));
134  fprintf(stderr, "Unable to identify %s: %s\n", file, errbuf);
135  goto fail;
136  }
137 
138  if (ctx->nb_streams < 1) {
139  fprintf(stderr, "No streams found in %s\n", file);
140  goto fail;
141  }
142  if (ctx->nb_streams > 1)
143  tracks->multiple_tracks_per_file = 1;
144 
145  for (i = 0; i < ctx->nb_streams; i++) {
146  struct Track **temp;
147  AVStream *st = ctx->streams[i];
148 
149  if (st->codecpar->bit_rate == 0) {
150  fprintf(stderr, "Skipping track %d in %s as it has zero bitrate\n",
151  st->id, file);
152  continue;
153  }
154 
155  track = av_mallocz(sizeof(*track));
156  if (!track) {
157  err = AVERROR(ENOMEM);
158  goto fail;
159  }
160  temp = av_realloc_array(tracks->tracks, tracks->nb_tracks + 1,
161  sizeof(*tracks->tracks));
162  if (!temp) {
163  av_free(track);
164  err = AVERROR(ENOMEM);
165  goto fail;
166  }
167  tracks->tracks = temp;
168  tracks->tracks[tracks->nb_tracks] = track;
169 
170  track->name = file;
171  if ((ptr = strrchr(file, '/')))
172  track->name = ptr + 1;
173 
174  track->bitrate = st->codecpar->bit_rate;
175  track->track_id = st->id;
176  track->timescale = st->time_base.den;
177  track->duration = st->duration;
180 
181  if (!track->is_audio && !track->is_video) {
182  fprintf(stderr,
183  "Track %d in %s is neither video nor audio, skipping\n",
184  track->track_id, file);
185  av_freep(&tracks->tracks[tracks->nb_tracks]);
186  continue;
187  }
188 
189  tracks->duration = FFMAX(tracks->duration,
191  track->timescale, AV_ROUND_UP));
192 
193  if (track->is_audio) {
194  track->channels = st->codecpar->channels;
195  track->sample_rate = st->codecpar->sample_rate;
196  }
197  if (track->is_video) {
198  track->width = st->codecpar->width;
199  track->height = st->codecpar->height;
200  }
201  set_codec_str(st->codecpar, track->codec_str, sizeof(track->codec_str));
202 
203  tracks->nb_tracks++;
204  }
205 
206  avformat_close_input(&ctx);
207 
208  err = find_sidx(tracks, orig_tracks, file);
209 
210 fail:
211  if (ctx)
212  avformat_close_input(&ctx);
213  return err;
214 }
215 
216 static void write_time(FILE *out, int64_t time, int decimals, enum AVRounding round)
217 {
218  int seconds = time / AV_TIME_BASE;
219  int fractions = time % AV_TIME_BASE;
220  int minutes = seconds / 60;
221  int hours = minutes / 60;
222  fractions = av_rescale_rnd(fractions, pow(10, decimals), AV_TIME_BASE, round);
223  seconds %= 60;
224  minutes %= 60;
225  fprintf(out, "PT");
226  if (hours)
227  fprintf(out, "%dH", hours);
228  if (hours || minutes)
229  fprintf(out, "%dM", minutes);
230  fprintf(out, "%d.%0*dS", seconds, decimals, fractions);
231 }
232 
233 static int output_mpd(struct Tracks *tracks, const char *filename)
234 {
235  FILE *out;
236  int i, j, ret = 0;
237  struct Track **adaptation_sets_buf[2] = { NULL };
238  struct Track ***adaptation_sets;
239  int nb_tracks_buf[2] = { 0 };
240  int *nb_tracks;
241  int set, nb_sets;
242 
243  if (!tracks->multiple_tracks_per_file) {
244  adaptation_sets = adaptation_sets_buf;
245  nb_tracks = nb_tracks_buf;
246  nb_sets = 2;
247  for (i = 0; i < 2; i++) {
248  adaptation_sets[i] = av_malloc_array(tracks->nb_tracks, sizeof(*adaptation_sets[i]));
249  if (!adaptation_sets[i]) {
250  ret = AVERROR(ENOMEM);
251  goto err;
252  }
253  }
254  for (i = 0; i < tracks->nb_tracks; i++) {
255  int set_index = -1;
256  if (tracks->tracks[i]->is_video)
257  set_index = 0;
258  else if (tracks->tracks[i]->is_audio)
259  set_index = 1;
260  else
261  continue;
262  adaptation_sets[set_index][nb_tracks[set_index]++] = tracks->tracks[i];
263  }
264  } else {
265  adaptation_sets = &tracks->tracks;
266  nb_tracks = &tracks->nb_tracks;
267  nb_sets = 1;
268  }
269 
270  out = fopen(filename, "w");
271  if (!out) {
272  ret = AVERROR(errno);
273  perror(filename);
274  return ret;
275  }
276  fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
277  fprintf(out, "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
278  "\txmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n"
279  "\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
280  "\txsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd\"\n"
281  "\tprofiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\"\n"
282  "\ttype=\"static\"\n");
283  fprintf(out, "\tmediaPresentationDuration=\"");
284  write_time(out, tracks->duration, 1, AV_ROUND_DOWN);
285  fprintf(out, "\"\n");
286  fprintf(out, "\tminBufferTime=\"PT5S\">\n");
287 
288  fprintf(out, "\t<Period start=\"PT0.0S\">\n");
289 
290  for (set = 0; set < nb_sets; set++) {
291  if (nb_tracks[set] == 0)
292  continue;
293  fprintf(out, "\t\t<AdaptationSet segmentAlignment=\"true\">\n");
294  if (nb_sets == 1) {
295  for (i = 0; i < nb_tracks[set]; i++) {
296  struct Track *track = adaptation_sets[set][i];
297  if (strcmp(track->name, adaptation_sets[set][0]->name))
298  break;
299  fprintf(out, "\t\t\t<ContentComponent id=\"%d\" contentType=\"%s\" />\n", track->track_id, track->is_audio ? "audio" : "video");
300  }
301  }
302 
303  for (i = 0; i < nb_tracks[set]; ) {
304  struct Track *first_track = adaptation_sets[set][i];
305  int width = 0, height = 0, sample_rate = 0, channels = 0, bitrate = 0;
306  fprintf(out, "\t\t\t<Representation id=\"%d\" codecs=\"", i);
307  for (j = i; j < nb_tracks[set]; j++) {
308  struct Track *track = adaptation_sets[set][j];
309  if (strcmp(track->name, first_track->name))
310  break;
311  if (track->is_audio) {
312  sample_rate = track->sample_rate;
313  channels = track->channels;
314  }
315  if (track->is_video) {
316  width = track->width;
317  height = track->height;
318  }
319  bitrate += track->bitrate;
320  if (j > i)
321  fprintf(out, ",");
322  fprintf(out, "%s", track->codec_str);
323  }
324  fprintf(out, "\" mimeType=\"%s/mp4\" bandwidth=\"%d\"",
325  width ? "video" : "audio", bitrate);
326  if (width > 0 && height > 0)
327  fprintf(out, " width=\"%d\" height=\"%d\"", width, height);
328  if (sample_rate > 0)
329  fprintf(out, " audioSamplingRate=\"%d\"", sample_rate);
330  fprintf(out, ">\n");
331  if (channels > 0)
332  fprintf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n", channels);
333  fprintf(out, "\t\t\t\t<BaseURL>%s</BaseURL>\n", first_track->name);
334  fprintf(out, "\t\t\t\t<SegmentBase indexRange=\"%"PRId64"-%"PRId64"\" />\n", first_track->sidx_start, first_track->sidx_start + first_track->sidx_length - 1);
335  fprintf(out, "\t\t\t</Representation>\n");
336  i = j;
337  }
338  fprintf(out, "\t\t</AdaptationSet>\n");
339  }
340  fprintf(out, "\t</Period>\n");
341  fprintf(out, "</MPD>\n");
342 
343  fclose(out);
344 err:
345  for (i = 0; i < 2; i++)
346  av_free(adaptation_sets_buf[i]);
347  return ret;
348 }
349 
350 static void clean_tracks(struct Tracks *tracks)
351 {
352  int i;
353  for (i = 0; i < tracks->nb_tracks; i++) {
354  av_freep(&tracks->tracks[i]);
355  }
356  av_freep(&tracks->tracks);
357  tracks->nb_tracks = 0;
358 }
359 
360 int main(int argc, char **argv)
361 {
362  const char *out = NULL;
363  struct Tracks tracks = { 0 };
364  int i;
365 
366  for (i = 1; i < argc; i++) {
367  if (!strcmp(argv[i], "-out")) {
368  out = argv[i + 1];
369  i++;
370  } else if (argv[i][0] == '-') {
371  return usage(argv[0], 1);
372  } else {
373  if (handle_file(&tracks, argv[i]))
374  return 1;
375  }
376  }
377  if (!tracks.nb_tracks || !out)
378  return usage(argv[0], 1);
379 
380  output_mpd(&tracks, out);
381 
382  clean_tracks(&tracks);
383 
384  return 0;
385 }
#define NULL
Definition: coverity.c:32
Bytestream IO Context.
Definition: avio.h:161
else temp
Definition: vf_mcdeint.c:256
channels
Definition: aptx.c:30
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: avcodec.h:3960
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:246
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
static void clean_tracks(struct Tracks *tracks)
Definition: sidxindex.c:350
int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options)
Open an input stream and read the header.
Definition: utils.c:537
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:236
int is_audio
Definition: ismindex.c:74
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_WB32 unsigned int_TMPL AV_WB24 unsigned int_TMPL AV_RB16
Definition: bytestream.h:87
AVRounding
Rounding methods.
Definition: mathematics.h:79
int tag
Definition: ismindex.c:84
This struct describes the properties of an encoded stream.
Definition: avcodec.h:3952
int height
Definition: ismindex.c:75
Format I/O context.
Definition: avformat.h:1358
int main(int argc, char **argv)
Definition: sidxindex.c:360
Round toward +infinity.
Definition: mathematics.h:83
static int find_sidx(struct Tracks *tracks, int start_index, const char *file)
Definition: sidxindex.c:78
int width
Video only.
Definition: avcodec.h:4026
unsigned int avio_rb32(AVIOContext *s)
Definition: aviobuf.c:803
#define f(width, name)
Definition: cbs_vp9.c:255
int id
Format-specific stream ID.
Definition: avformat.h:888
int bitrate
Definition: ismindex.c:72
int nb_tracks
Definition: ismindex.c:88
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1426
static void write_time(FILE *out, int64_t time, int decimals, enum AVRounding round)
Definition: sidxindex.c:216
Definition: ismindex.c:69
ptrdiff_t size
Definition: opengl_enc.c:100
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition: avio.h:557
void * av_realloc_array(void *ptr, size_t nmemb, size_t size)
Definition: mem.c:198
int width
Definition: ismindex.c:75
char codec_str[30]
Definition: sidxindex.c:44
int64_t bit_rate
The average bitrate of the encoded data (in bits per second).
Definition: avcodec.h:3989
int is_video
Definition: ismindex.c:74
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
int channels
Definition: ismindex.c:77
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: aviobuf.c:1217
static int output_mpd(struct Tracks *tracks, const char *filename)
Definition: sidxindex.c:233
enum AVMediaType codec_type
General type of the encoded data.
Definition: avcodec.h:3956
static int usage(const char *argv0, int ret)
Definition: sidxindex.c:29
int sample_rate
Definition: ismindex.c:77
int64_t sidx_start
Definition: sidxindex.c:45
static av_always_inline av_const double round(double x)
Definition: libm.h:444
static void set_codec_str(AVCodecParameters *codecpar, char *str, int size)
Definition: sidxindex.c:55
int64_t duration
Definition: ismindex.c:89
#define FFMAX(a, b)
Definition: common.h:94
#define fail()
Definition: checkasm.h:121
int extradata_size
Size of the extradata content in bytes.
Definition: avcodec.h:3978
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1414
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
int32_t
AVFormatContext * ctx
Definition: movenc.c:48
int multiple_tracks_per_file
Definition: sidxindex.c:52
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)
Rescale a 64-bit integer with specified rounding.
Definition: mathematics.c:58
Stream structure.
Definition: avformat.h:881
const char * name
Definition: ismindex.c:70
int64_t duration
Definition: ismindex.c:71
int timescale
Definition: ismindex.c:81
size_t av_strlcatf(char *dst, size_t size, const char *fmt,...)
Definition: avstring.c:101
int avio_open2(AVIOContext **s, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options)
Create and initialize a AVIOContext for accessing the resource indicated by url.
Definition: aviobuf.c:1211
#define snprintf
Definition: snprintf.h:34
struct Track ** tracks
Definition: ismindex.c:90
Round toward -infinity.
Definition: mathematics.h:82
int track_id
Definition: ismindex.c:73
int64_t sidx_length
Definition: sidxindex.c:45
int av_strerror(int errnum, char *errbuf, size_t errbuf_size)
Put a description of the AVERROR code errnum in errbuf.
Definition: error.c:105
int64_t duration
Decoding: duration of the stream, in stream time base.
Definition: avformat.h:930
int sample_rate
Audio only.
Definition: avcodec.h:4070
Main libavformat public API header.
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
Read packets of a media file to get stream information.
Definition: utils.c:3577
int den
Denominator.
Definition: rational.h:60
void avformat_close_input(AVFormatContext **s)
Close an opened input AVFormatContext.
Definition: utils.c:4441
#define MKBETAG(a, b, c, d)
Definition: common.h:367
static int handle_file(struct Tracks *tracks, const char *file)
Definition: sidxindex.c:117
#define av_free(p)
int eof_reached
true if was unable to read due to error or eof
Definition: avio.h:239
uint8_t * extradata
Extra binary data needed for initializing the decoder, codec-dependent.
Definition: avcodec.h:3974
int channels
Audio only.
Definition: avcodec.h:4066
FILE * out
Definition: movenc.c:54
#define av_freep(p)
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1028
#define av_malloc_array(a, b)
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented...
Definition: avformat.h:910
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions