FFmpeg
ismindex.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 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 /*
22  * To create a simple file for smooth streaming:
23  * ffmpeg <normal input/transcoding options> -movflags frag_keyframe foo.ismv
24  * ismindex -n foo foo.ismv
25  * This step creates foo.ism and foo.ismc that is required by IIS for
26  * serving it.
27  *
28  * With -ismf, it also creates foo.ismf, which maps fragment names to
29  * start-end offsets in the ismv, for use in your own streaming server.
30  *
31  * By adding -path-prefix path/, the produced foo.ism will refer to the
32  * files foo.ismv as "path/foo.ismv" - the prefix for the generated ismc
33  * file can be set with the -ismc-prefix option similarly.
34  *
35  * To pre-split files for serving as static files by a web server without
36  * any extra server support, create the ismv file as above, and split it:
37  * ismindex -split foo.ismv
38  * This step creates a file Manifest and directories QualityLevel(...),
39  * that can be read directly by a smooth streaming player.
40  *
41  * The -output dir option can be used to request that output files
42  * (both .ism/.ismc, or Manifest/QualityLevels* when splitting)
43  * should be written to this directory instead of in the current directory.
44  * (The directory itself isn't created if it doesn't already exist.)
45  */
46 
47 #include <stdio.h>
48 #include <string.h>
49 
50 #include "libavformat/avformat.h"
51 #include "libavformat/isom.h"
52 #include "libavformat/os_support.h"
53 #include "libavutil/intreadwrite.h"
54 #include "libavutil/mathematics.h"
55 
56 static int usage(const char *argv0, int ret)
57 {
58  fprintf(stderr, "%s [-split] [-ismf] [-n basename] [-path-prefix prefix] "
59  "[-ismc-prefix prefix] [-output dir] file1 [file2] ...\n", argv0);
60  return ret;
61 }
62 
63 struct MoofOffset {
64  int64_t time;
65  int64_t offset;
66  int64_t duration;
67 };
68 
69 struct Track {
70  const char *name;
71  int64_t duration;
72  int bitrate;
73  int track_id;
75  int width, height;
76  int chunks;
78  uint8_t *codec_private;
81  int timescale;
82  const char *fourcc;
83  int blocksize;
84  int tag;
85 };
86 
87 struct Tracks {
88  int nb_tracks;
89  int64_t duration;
90  struct Track **tracks;
93 };
94 
95 static int expect_tag(int32_t got_tag, int32_t expected_tag) {
96  if (got_tag != expected_tag) {
97  char got_tag_str[4], expected_tag_str[4];
98  AV_WB32(got_tag_str, got_tag);
99  AV_WB32(expected_tag_str, expected_tag);
100  fprintf(stderr, "wanted tag %.4s, got %.4s\n", expected_tag_str,
101  got_tag_str);
102  return -1;
103  }
104  return 0;
105 }
106 
107 static int copy_tag(AVIOContext *in, AVIOContext *out, int32_t tag_name)
108 {
109  int32_t size, tag;
110 
111  size = avio_rb32(in);
112  tag = avio_rb32(in);
113  avio_wb32(out, size);
114  avio_wb32(out, tag);
115  if (expect_tag(tag, tag_name) != 0)
116  return -1;
117  size -= 8;
118  while (size > 0) {
119  char buf[1024];
120  int len = FFMIN(sizeof(buf), size);
121  int got;
122  if ((got = avio_read(in, buf, len)) != len) {
123  fprintf(stderr, "short read, wanted %d, got %d\n", len, got);
124  break;
125  }
126  avio_write(out, buf, len);
127  size -= len;
128  }
129  return 0;
130 }
131 
132 static int skip_tag(AVIOContext *in, int32_t tag_name)
133 {
134  int64_t pos = avio_tell(in);
135  int32_t size, tag;
136 
137  size = avio_rb32(in);
138  tag = avio_rb32(in);
139  if (expect_tag(tag, tag_name) != 0)
140  return -1;
141  avio_seek(in, pos + size, SEEK_SET);
142  return 0;
143 }
144 
145 static int write_fragment(const char *filename, AVIOContext *in)
146 {
147  AVIOContext *out = NULL;
148  int ret;
149 
150  if ((ret = avio_open2(&out, filename, AVIO_FLAG_WRITE, NULL, NULL)) < 0) {
151  char errbuf[100];
152  av_strerror(ret, errbuf, sizeof(errbuf));
153  fprintf(stderr, "Unable to open %s: %s\n", filename, errbuf);
154  return ret;
155  }
156  ret = copy_tag(in, out, MKBETAG('m', 'o', 'o', 'f'));
157  if (!ret)
158  ret = copy_tag(in, out, MKBETAG('m', 'd', 'a', 't'));
159 
160  avio_flush(out);
161  avio_close(out);
162 
163  return ret;
164 }
165 
166 static int skip_fragment(AVIOContext *in)
167 {
168  int ret;
169  ret = skip_tag(in, MKBETAG('m', 'o', 'o', 'f'));
170  if (!ret)
171  ret = skip_tag(in, MKBETAG('m', 'd', 'a', 't'));
172  return ret;
173 }
174 
175 static int write_fragments(struct Tracks *tracks, int start_index,
176  AVIOContext *in, const char *basename,
177  int split, int ismf, const char* output_prefix)
178 {
179  char dirname[2048], filename[2048], idxname[2048];
180  int i, j, ret = 0, fragment_ret;
181  FILE* out = NULL;
182 
183  if (ismf) {
184  snprintf(idxname, sizeof(idxname), "%s%s.ismf", output_prefix, basename);
185  out = fopen(idxname, "w");
186  if (!out) {
187  ret = AVERROR(errno);
188  perror(idxname);
189  goto fail;
190  }
191  }
192  for (i = start_index; i < tracks->nb_tracks; i++) {
193  struct Track *track = tracks->tracks[i];
194  const char *type = track->is_video ? "video" : "audio";
195  snprintf(dirname, sizeof(dirname), "%sQualityLevels(%d)", output_prefix, track->bitrate);
196  if (split) {
197  if (mkdir(dirname, 0777) == -1 && errno != EEXIST) {
198  ret = AVERROR(errno);
199  perror(dirname);
200  goto fail;
201  }
202  }
203  for (j = 0; j < track->chunks; j++) {
204  snprintf(filename, sizeof(filename), "%s/Fragments(%s=%"PRId64")",
205  dirname, type, track->offsets[j].time);
206  avio_seek(in, track->offsets[j].offset, SEEK_SET);
207  if (ismf)
208  fprintf(out, "%s %"PRId64, filename, avio_tell(in));
209  if (split)
210  fragment_ret = write_fragment(filename, in);
211  else
212  fragment_ret = skip_fragment(in);
213  if (ismf)
214  fprintf(out, " %"PRId64"\n", avio_tell(in));
215  if (fragment_ret != 0) {
216  fprintf(stderr, "failed fragment %d in track %d (%s)\n", j,
217  track->track_id, track->name);
218  ret = fragment_ret;
219  }
220  }
221  }
222 fail:
223  if (out)
224  fclose(out);
225  return ret;
226 }
227 
228 static int64_t read_trun_duration(AVIOContext *in, int default_duration,
229  int64_t end)
230 {
231  int64_t dts = 0;
232  int64_t pos;
233  int flags, i;
234  int entries;
235  int64_t first_pts = 0;
236  int64_t max_pts = 0;
237  avio_r8(in); /* version */
238  flags = avio_rb24(in);
239  if (default_duration <= 0 && !(flags & MOV_TRUN_SAMPLE_DURATION)) {
240  fprintf(stderr, "No sample duration in trun flags\n");
241  return -1;
242  }
243  entries = avio_rb32(in);
244 
247 
248  pos = avio_tell(in);
249  for (i = 0; i < entries && pos < end; i++) {
250  int sample_duration = default_duration;
251  int64_t pts = dts;
252  if (flags & MOV_TRUN_SAMPLE_DURATION) sample_duration = avio_rb32(in);
255  if (flags & MOV_TRUN_SAMPLE_CTS) pts += avio_rb32(in);
256  if (sample_duration < 0) {
257  fprintf(stderr, "Negative sample duration %d\n", sample_duration);
258  return -1;
259  }
260  if (i == 0)
261  first_pts = pts;
262  max_pts = FFMAX(max_pts, pts + sample_duration);
263  dts += sample_duration;
264  pos = avio_tell(in);
265  }
266 
267  return max_pts - first_pts;
268 }
269 
270 static int64_t read_moof_duration(AVIOContext *in, int64_t offset)
271 {
272  int64_t ret = -1;
273  int32_t moof_size, size, tag;
274  int64_t pos = 0;
275  int default_duration = 0;
276 
277  avio_seek(in, offset, SEEK_SET);
278  moof_size = avio_rb32(in);
279  tag = avio_rb32(in);
280  if (expect_tag(tag, MKBETAG('m', 'o', 'o', 'f')) != 0)
281  goto fail;
282  while (pos < offset + moof_size) {
283  pos = avio_tell(in);
284  size = avio_rb32(in);
285  tag = avio_rb32(in);
286  if (tag == MKBETAG('t', 'r', 'a', 'f')) {
287  int64_t traf_pos = pos;
288  int64_t traf_size = size;
289  while (pos < traf_pos + traf_size) {
290  pos = avio_tell(in);
291  size = avio_rb32(in);
292  tag = avio_rb32(in);
293  if (tag == MKBETAG('t', 'f', 'h', 'd')) {
294  int flags = 0;
295  avio_r8(in); /* version */
296  flags = avio_rb24(in);
297  avio_rb32(in); /* track_id */
299  avio_rb64(in);
300  if (flags & MOV_TFHD_STSD_ID)
301  avio_rb32(in);
303  default_duration = avio_rb32(in);
304  }
305  if (tag == MKBETAG('t', 'r', 'u', 'n')) {
306  return read_trun_duration(in, default_duration,
307  pos + size);
308  }
309  avio_seek(in, pos + size, SEEK_SET);
310  }
311  fprintf(stderr, "Couldn't find trun\n");
312  goto fail;
313  }
314  avio_seek(in, pos + size, SEEK_SET);
315  }
316  fprintf(stderr, "Couldn't find traf\n");
317 
318 fail:
319  return ret;
320 }
321 
322 static int read_tfra(struct Tracks *tracks, int start_index, AVIOContext *f)
323 {
324  int ret = AVERROR_EOF, track_id;
325  int version, fieldlength, i, j;
326  int64_t pos = avio_tell(f);
327  uint32_t size = avio_rb32(f);
328  struct Track *track = NULL;
329 
330  if (avio_rb32(f) != MKBETAG('t', 'f', 'r', 'a'))
331  goto fail;
332  version = avio_r8(f);
333  avio_rb24(f);
334  track_id = avio_rb32(f); /* track id */
335  for (i = start_index; i < tracks->nb_tracks && !track; i++)
336  if (tracks->tracks[i]->track_id == track_id)
337  track = tracks->tracks[i];
338  if (!track) {
339  /* Ok, continue parsing the next atom */
340  ret = 0;
341  goto fail;
342  }
343  fieldlength = avio_rb32(f);
344  track->chunks = avio_rb32(f);
345  track->offsets = av_calloc(track->chunks, sizeof(*track->offsets));
346  if (!track->offsets) {
347  track->chunks = 0;
348  ret = AVERROR(ENOMEM);
349  goto fail;
350  }
351  // The duration here is always the difference between consecutive
352  // start times.
353  for (i = 0; i < track->chunks; i++) {
354  if (version == 1) {
355  track->offsets[i].time = avio_rb64(f);
356  track->offsets[i].offset = avio_rb64(f);
357  } else {
358  track->offsets[i].time = avio_rb32(f);
359  track->offsets[i].offset = avio_rb32(f);
360  }
361  for (j = 0; j < ((fieldlength >> 4) & 3) + 1; j++)
362  avio_r8(f);
363  for (j = 0; j < ((fieldlength >> 2) & 3) + 1; j++)
364  avio_r8(f);
365  for (j = 0; j < ((fieldlength >> 0) & 3) + 1; j++)
366  avio_r8(f);
367  if (i > 0)
368  track->offsets[i - 1].duration = track->offsets[i].time -
369  track->offsets[i - 1].time;
370  }
371  if (track->chunks > 0) {
372  track->offsets[track->chunks - 1].duration = track->offsets[0].time +
373  track->duration -
374  track->offsets[track->chunks - 1].time;
375  }
376  // Now try to read the actual durations from the trun sample data.
377  for (i = 0; i < track->chunks; i++) {
378  int64_t duration = read_moof_duration(f, track->offsets[i].offset);
379  if (duration > 0 && llabs(duration - track->offsets[i].duration) > 3) {
380  // 3 allows for integer duration to drift a few units,
381  // e.g., for 1/3 durations
382  track->offsets[i].duration = duration;
383  }
384  }
385  if (track->chunks > 0) {
386  if (track->offsets[track->chunks - 1].duration <= 0) {
387  fprintf(stderr, "Calculated last chunk duration for track %d "
388  "was non-positive (%"PRId64"), probably due to missing "
389  "fragments ", track->track_id,
390  track->offsets[track->chunks - 1].duration);
391  if (track->chunks > 1) {
392  track->offsets[track->chunks - 1].duration =
393  track->offsets[track->chunks - 2].duration;
394  } else {
395  track->offsets[track->chunks - 1].duration = 1;
396  }
397  fprintf(stderr, "corrected to %"PRId64"\n",
398  track->offsets[track->chunks - 1].duration);
399  track->duration = track->offsets[track->chunks - 1].time +
400  track->offsets[track->chunks - 1].duration -
401  track->offsets[0].time;
402  fprintf(stderr, "Track duration corrected to %"PRId64"\n",
403  track->duration);
404  }
405  }
406  ret = 0;
407 
408 fail:
409  avio_seek(f, pos + size, SEEK_SET);
410  return ret;
411 }
412 
413 static int read_mfra(struct Tracks *tracks, int start_index,
414  const char *file, int split, int ismf,
415  const char *basename, const char* output_prefix)
416 {
417  int err = 0;
418  const char* err_str = "";
419  AVIOContext *f = NULL;
420  int32_t mfra_size;
421 
422  if ((err = avio_open2(&f, file, AVIO_FLAG_READ, NULL, NULL)) < 0)
423  goto fail;
424  avio_seek(f, avio_size(f) - 4, SEEK_SET);
425  mfra_size = avio_rb32(f);
426  avio_seek(f, -mfra_size, SEEK_CUR);
427  if (avio_rb32(f) != mfra_size) {
428  err = AVERROR_INVALIDDATA;
429  err_str = "mfra size mismatch";
430  goto fail;
431  }
432  if (avio_rb32(f) != MKBETAG('m', 'f', 'r', 'a')) {
433  err = AVERROR_INVALIDDATA;
434  err_str = "mfra tag mismatch";
435  goto fail;
436  }
437  while (!read_tfra(tracks, start_index, f)) {
438  /* Empty */
439  }
440 
441  if (split || ismf)
442  err = write_fragments(tracks, start_index, f, basename, split, ismf,
443  output_prefix);
444  err_str = "error in write_fragments";
445 
446 fail:
447  if (f)
448  avio_close(f);
449  if (err)
450  fprintf(stderr, "Unable to read the MFRA atom in %s (%s)\n", file, err_str);
451  return err;
452 }
453 
454 static int get_private_data(struct Track *track, AVCodecParameters *codecpar)
455 {
456  track->codec_private_size = 0;
457  track->codec_private = av_mallocz(codecpar->extradata_size);
458  if (!track->codec_private)
459  return AVERROR(ENOMEM);
460  track->codec_private_size = codecpar->extradata_size;
461  memcpy(track->codec_private, codecpar->extradata, codecpar->extradata_size);
462  return 0;
463 }
464 
465 static int get_video_private_data(struct Track *track, AVCodecParameters *codecpar)
466 {
467  AVIOContext *io = NULL;
468  uint16_t sps_size, pps_size;
469  int err;
470 
471  if (codecpar->codec_id == AV_CODEC_ID_VC1)
472  return get_private_data(track, codecpar);
473 
474  if ((err = avio_open_dyn_buf(&io)) < 0)
475  goto fail;
476  err = AVERROR(EINVAL);
477  if (codecpar->extradata_size < 11 || codecpar->extradata[0] != 1)
478  goto fail;
479  sps_size = AV_RB16(&codecpar->extradata[6]);
480  if (11 + sps_size > codecpar->extradata_size)
481  goto fail;
482  avio_wb32(io, 0x00000001);
483  avio_write(io, &codecpar->extradata[8], sps_size);
484  pps_size = AV_RB16(&codecpar->extradata[9 + sps_size]);
485  if (11 + sps_size + pps_size > codecpar->extradata_size)
486  goto fail;
487  avio_wb32(io, 0x00000001);
488  avio_write(io, &codecpar->extradata[11 + sps_size], pps_size);
489  err = 0;
490 
491 fail:
493  return err;
494 }
495 
496 static int handle_file(struct Tracks *tracks, const char *file, int split,
497  int ismf, const char *basename,
498  const char* output_prefix)
499 {
501  int err = 0, i, orig_tracks = tracks->nb_tracks;
502  char errbuf[50], *ptr;
503  struct Track *track;
504 
505  err = avformat_open_input(&ctx, file, NULL, NULL);
506  if (err < 0) {
507  av_strerror(err, errbuf, sizeof(errbuf));
508  fprintf(stderr, "Unable to open %s: %s\n", file, errbuf);
509  return 1;
510  }
511 
513  if (err < 0) {
514  av_strerror(err, errbuf, sizeof(errbuf));
515  fprintf(stderr, "Unable to identify %s: %s\n", file, errbuf);
516  goto fail;
517  }
518 
519  if (ctx->nb_streams < 1) {
520  fprintf(stderr, "No streams found in %s\n", file);
521  goto fail;
522  }
523 
524  for (i = 0; i < ctx->nb_streams; i++) {
525  struct Track **temp;
526  AVStream *st = ctx->streams[i];
527 
528  if (st->codecpar->bit_rate == 0) {
529  fprintf(stderr, "Skipping track %d in %s as it has zero bitrate\n",
530  st->id, file);
531  continue;
532  }
533 
534  track = av_mallocz(sizeof(*track));
535  if (!track) {
536  err = AVERROR(ENOMEM);
537  goto fail;
538  }
539  temp = av_realloc_array(tracks->tracks,
540  tracks->nb_tracks + 1,
541  sizeof(*tracks->tracks));
542  if (!temp) {
543  av_free(track);
544  err = AVERROR(ENOMEM);
545  goto fail;
546  }
547  tracks->tracks = temp;
548  tracks->tracks[tracks->nb_tracks] = track;
549 
550  track->name = file;
551  if ((ptr = strrchr(file, '/')))
552  track->name = ptr + 1;
553 
554  track->bitrate = st->codecpar->bit_rate;
555  track->track_id = st->id;
556  track->timescale = st->time_base.den;
557  track->duration = st->duration;
560 
561  if (!track->is_audio && !track->is_video) {
562  fprintf(stderr,
563  "Track %d in %s is neither video nor audio, skipping\n",
564  track->track_id, file);
565  av_freep(&tracks->tracks[tracks->nb_tracks]);
566  continue;
567  }
568 
569  tracks->duration = FFMAX(tracks->duration,
571  track->timescale, AV_ROUND_UP));
572 
573  if (track->is_audio) {
574  if (tracks->audio_track < 0)
575  tracks->audio_track = tracks->nb_tracks;
576  tracks->nb_audio_tracks++;
577  track->channels = st->codecpar->ch_layout.nb_channels;
578  track->sample_rate = st->codecpar->sample_rate;
579  if (st->codecpar->codec_id == AV_CODEC_ID_AAC) {
580  track->fourcc = "AACL";
581  track->tag = 255;
582  track->blocksize = 4;
583  } else if (st->codecpar->codec_id == AV_CODEC_ID_WMAPRO) {
584  track->fourcc = "WMAP";
585  track->tag = st->codecpar->codec_tag;
586  track->blocksize = st->codecpar->block_align;
587  }
588  get_private_data(track, st->codecpar);
589  }
590  if (track->is_video) {
591  if (tracks->video_track < 0)
592  tracks->video_track = tracks->nb_tracks;
593  tracks->nb_video_tracks++;
594  track->width = st->codecpar->width;
595  track->height = st->codecpar->height;
596  if (st->codecpar->codec_id == AV_CODEC_ID_H264)
597  track->fourcc = "H264";
598  else if (st->codecpar->codec_id == AV_CODEC_ID_VC1)
599  track->fourcc = "WVC1";
600  get_video_private_data(track, st->codecpar);
601  }
602 
603  tracks->nb_tracks++;
604  }
605 
607 
608  err = read_mfra(tracks, orig_tracks, file, split, ismf, basename,
609  output_prefix);
610 
611 fail:
612  if (ctx)
614  return err;
615 }
616 
617 static void output_server_manifest(struct Tracks *tracks, const char *basename,
618  const char *output_prefix,
619  const char *path_prefix,
620  const char *ismc_prefix)
621 {
622  char filename[1000];
623  FILE *out;
624  int i;
625 
626  snprintf(filename, sizeof(filename), "%s%s.ism", output_prefix, basename);
627  out = fopen(filename, "w");
628  if (!out) {
629  perror(filename);
630  return;
631  }
632  fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
633  fprintf(out, "<smil xmlns=\"http://www.w3.org/2001/SMIL20/Language\">\n");
634  fprintf(out, "\t<head>\n");
635  fprintf(out, "\t\t<meta name=\"clientManifestRelativePath\" "
636  "content=\"%s%s.ismc\" />\n", ismc_prefix, basename);
637  fprintf(out, "\t</head>\n");
638  fprintf(out, "\t<body>\n");
639  fprintf(out, "\t\t<switch>\n");
640  for (i = 0; i < tracks->nb_tracks; i++) {
641  struct Track *track = tracks->tracks[i];
642  const char *type = track->is_video ? "video" : "audio";
643  fprintf(out, "\t\t\t<%s src=\"%s%s\" systemBitrate=\"%d\">\n",
644  type, path_prefix, track->name, track->bitrate);
645  fprintf(out, "\t\t\t\t<param name=\"trackID\" value=\"%d\" "
646  "valueType=\"data\" />\n", track->track_id);
647  fprintf(out, "\t\t\t</%s>\n", type);
648  }
649  fprintf(out, "\t\t</switch>\n");
650  fprintf(out, "\t</body>\n");
651  fprintf(out, "</smil>\n");
652  fclose(out);
653 }
654 
655 static void print_track_chunks(FILE *out, struct Tracks *tracks, int main,
656  const char *type)
657 {
658  int i, j;
659  int64_t pos = 0;
660  struct Track *track = tracks->tracks[main];
661  int should_print_time_mismatch = 1;
662 
663  for (i = 0; i < track->chunks; i++) {
664  for (j = main + 1; j < tracks->nb_tracks; j++) {
665  if (tracks->tracks[j]->is_audio == track->is_audio) {
666  if (track->offsets[i].duration != tracks->tracks[j]->offsets[i].duration) {
667  fprintf(stderr, "Mismatched duration of %s chunk %d in %s (%d) and %s (%d)\n",
668  type, i, track->name, main, tracks->tracks[j]->name, j);
669  should_print_time_mismatch = 1;
670  }
671  if (track->offsets[i].time != tracks->tracks[j]->offsets[i].time) {
672  if (should_print_time_mismatch)
673  fprintf(stderr, "Mismatched (start) time of %s chunk %d in %s (%d) and %s (%d)\n",
674  type, i, track->name, main, tracks->tracks[j]->name, j);
675  should_print_time_mismatch = 0;
676  }
677  }
678  }
679  fprintf(out, "\t\t<c n=\"%d\" d=\"%"PRId64"\" ",
680  i, track->offsets[i].duration);
681  if (pos != track->offsets[i].time) {
682  fprintf(out, "t=\"%"PRId64"\" ", track->offsets[i].time);
683  pos = track->offsets[i].time;
684  }
685  pos += track->offsets[i].duration;
686  fprintf(out, "/>\n");
687  }
688 }
689 
690 static void output_client_manifest(struct Tracks *tracks, const char *basename,
691  const char *output_prefix, int split)
692 {
693  char filename[1000];
694  FILE *out;
695  int i, j;
696 
697  if (split)
698  snprintf(filename, sizeof(filename), "%sManifest", output_prefix);
699  else
700  snprintf(filename, sizeof(filename), "%s%s.ismc", output_prefix, basename);
701  out = fopen(filename, "w");
702  if (!out) {
703  perror(filename);
704  return;
705  }
706  fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
707  fprintf(out, "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" "
708  "Duration=\"%"PRId64 "\">\n", tracks->duration * 10);
709  if (tracks->video_track >= 0) {
710  struct Track *track = tracks->tracks[tracks->video_track];
711  struct Track *first_track = track;
712  int index = 0;
713  fprintf(out,
714  "\t<StreamIndex Type=\"video\" QualityLevels=\"%d\" "
715  "Chunks=\"%d\" "
716  "Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n",
717  tracks->nb_video_tracks, track->chunks);
718  for (i = 0; i < tracks->nb_tracks; i++) {
719  track = tracks->tracks[i];
720  if (!track->is_video)
721  continue;
722  fprintf(out,
723  "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
724  "FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" "
725  "CodecPrivateData=\"",
726  index, track->bitrate, track->fourcc, track->width, track->height);
727  for (j = 0; j < track->codec_private_size; j++)
728  fprintf(out, "%02X", track->codec_private[j]);
729  fprintf(out, "\" />\n");
730  index++;
731  if (track->chunks != first_track->chunks)
732  fprintf(stderr, "Mismatched number of video chunks in %s (id: %d, chunks %d) and %s (id: %d, chunks %d)\n",
733  track->name, track->track_id, track->chunks, first_track->name, first_track->track_id, first_track->chunks);
734  }
735  print_track_chunks(out, tracks, tracks->video_track, "video");
736  fprintf(out, "\t</StreamIndex>\n");
737  }
738  if (tracks->audio_track >= 0) {
739  struct Track *track = tracks->tracks[tracks->audio_track];
740  struct Track *first_track = track;
741  int index = 0;
742  fprintf(out,
743  "\t<StreamIndex Type=\"audio\" QualityLevels=\"%d\" "
744  "Chunks=\"%d\" "
745  "Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n",
746  tracks->nb_audio_tracks, track->chunks);
747  for (i = 0; i < tracks->nb_tracks; i++) {
748  track = tracks->tracks[i];
749  if (!track->is_audio)
750  continue;
751  fprintf(out,
752  "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
753  "FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" "
754  "BitsPerSample=\"16\" PacketSize=\"%d\" "
755  "AudioTag=\"%d\" CodecPrivateData=\"",
756  index, track->bitrate, track->fourcc, track->sample_rate,
757  track->channels, track->blocksize, track->tag);
758  for (j = 0; j < track->codec_private_size; j++)
759  fprintf(out, "%02X", track->codec_private[j]);
760  fprintf(out, "\" />\n");
761  index++;
762  if (track->chunks != first_track->chunks)
763  fprintf(stderr, "Mismatched number of audio chunks in %s and %s\n",
764  track->name, first_track->name);
765  }
766  print_track_chunks(out, tracks, tracks->audio_track, "audio");
767  fprintf(out, "\t</StreamIndex>\n");
768  }
769  fprintf(out, "</SmoothStreamingMedia>\n");
770  fclose(out);
771 }
772 
773 static void clean_tracks(struct Tracks *tracks)
774 {
775  int i;
776  for (i = 0; i < tracks->nb_tracks; i++) {
777  av_freep(&tracks->tracks[i]->codec_private);
778  av_freep(&tracks->tracks[i]->offsets);
779  av_freep(&tracks->tracks[i]);
780  }
781  av_freep(&tracks->tracks);
782  tracks->nb_tracks = 0;
783 }
784 
785 int main(int argc, char **argv)
786 {
787  const char *basename = NULL;
788  const char *path_prefix = "", *ismc_prefix = "";
789  const char *output_prefix = "";
790  char output_prefix_buf[2048];
791  int split = 0, ismf = 0, i;
792  struct Tracks tracks = { 0, .video_track = -1, .audio_track = -1 };
793 
794  for (i = 1; i < argc; i++) {
795  if (!strcmp(argv[i], "-n")) {
796  basename = argv[i + 1];
797  i++;
798  } else if (!strcmp(argv[i], "-path-prefix")) {
799  path_prefix = argv[i + 1];
800  i++;
801  } else if (!strcmp(argv[i], "-ismc-prefix")) {
802  ismc_prefix = argv[i + 1];
803  i++;
804  } else if (!strcmp(argv[i], "-output")) {
805  output_prefix = argv[i + 1];
806  i++;
807  if (output_prefix[strlen(output_prefix) - 1] != '/') {
808  snprintf(output_prefix_buf, sizeof(output_prefix_buf),
809  "%s/", output_prefix);
810  output_prefix = output_prefix_buf;
811  }
812  } else if (!strcmp(argv[i], "-split")) {
813  split = 1;
814  } else if (!strcmp(argv[i], "-ismf")) {
815  ismf = 1;
816  } else if (argv[i][0] == '-') {
817  return usage(argv[0], 1);
818  } else {
819  if (!basename)
820  ismf = 0;
821  if (handle_file(&tracks, argv[i], split, ismf,
822  basename, output_prefix))
823  return 1;
824  }
825  }
826  if (!tracks.nb_tracks || (!basename && !split))
827  return usage(argv[0], 1);
828 
829  if (!split)
830  output_server_manifest(&tracks, basename, output_prefix,
831  path_prefix, ismc_prefix);
832  output_client_manifest(&tracks, basename, output_prefix, split);
833 
835 
836  return 0;
837 }
Track::tag
int tag
Definition: ismindex.c:84
AVCodecParameters::extradata
uint8_t * extradata
Extra binary data needed for initializing the decoder, codec-dependent.
Definition: codec_par.h:75
MOV_TRUN_SAMPLE_FLAGS
#define MOV_TRUN_SAMPLE_FLAGS
Definition: isom.h:347
AVERROR
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
Track
Definition: ismindex.c:69
read_moof_duration
static int64_t read_moof_duration(AVIOContext *in, int64_t offset)
Definition: ismindex.c:270
AVCodecParameters::codec_type
enum AVMediaType codec_type
General type of the encoded data.
Definition: codec_par.h:57
avio_close
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: aviobuf.c:1255
out
FILE * out
Definition: movenc.c:54
AVCodecParameters
This struct describes the properties of an encoded stream.
Definition: codec_par.h:53
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:57
Track::codec_private
uint8_t * codec_private
Definition: ismindex.c:78
write_fragment
static int write_fragment(const char *filename, AVIOContext *in)
Definition: ismindex.c:145
AVFormatContext::streams
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1281
AVCodecParameters::codec_tag
uint32_t codec_tag
Additional information about the codec (corresponds to the AVI FOURCC).
Definition: codec_par.h:65
mathematics.h
Track::chunks
int chunks
Definition: ismindex.c:76
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
output_server_manifest
static void output_server_manifest(struct Tracks *tracks, const char *basename, const char *output_prefix, const char *path_prefix, const char *ismc_prefix)
Definition: ismindex.c:617
AVChannelLayout::nb_channels
int nb_channels
Number of channels in this layout.
Definition: channel_layout.h:300
Tracks::duration
int64_t duration
Definition: ismindex.c:89
MoofOffset
Definition: ismindex.c:63
avio_size
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition: aviobuf.c:352
os_support.h
Track::codec_private_size
int codec_private_size
Definition: ismindex.c:79
avformat_close_input
void avformat_close_input(AVFormatContext **s)
Close an opened input AVFormatContext.
Definition: demux.c:368
read_mfra
static int read_mfra(struct Tracks *tracks, int start_index, const char *file, int split, int ismf, const char *basename, const char *output_prefix)
Definition: ismindex.c:413
avio_open2
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:1249
MoofOffset::time
int64_t time
Definition: ismindex.c:64
fail
#define fail()
Definition: checkasm.h:131
av_strerror
int av_strerror(int errnum, char *errbuf, size_t errbuf_size)
Put a description of the AVERROR code errnum in errbuf.
Definition: error.c:108
expect_tag
static int expect_tag(int32_t got_tag, int32_t expected_tag)
Definition: ismindex.c:95
avio_tell
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition: avio.h:505
output_client_manifest
static void output_client_manifest(struct Tracks *tracks, const char *basename, const char *output_prefix, int split)
Definition: ismindex.c:690
write_fragments
static int write_fragments(struct Tracks *tracks, int start_index, AVIOContext *in, const char *basename, int split, int ismf, const char *output_prefix)
Definition: ismindex.c:175
MOV_TRUN_SAMPLE_DURATION
#define MOV_TRUN_SAMPLE_DURATION
Definition: isom.h:345
clean_tracks
static void clean_tracks(struct Tracks *tracks)
Definition: ismindex.c:773
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
Track::bitrate
int bitrate
Definition: ismindex.c:72
pts
static int64_t pts
Definition: transcode_aac.c:654
AV_ROUND_UP
@ AV_ROUND_UP
Round toward +infinity.
Definition: mathematics.h:83
AVStream::duration
int64_t duration
Decoding: duration of the stream, in stream time base.
Definition: avformat.h:998
Tracks::nb_tracks
int nb_tracks
Definition: ismindex.c:88
MOV_TRUN_DATA_OFFSET
#define MOV_TRUN_DATA_OFFSET
Definition: isom.h:343
avio_close_dyn_buf
int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer)
Return the written size and a pointer to the buffer.
Definition: aviobuf.c:1528
Track::name
const char * name
Definition: ismindex.c:70
avio_rb32
unsigned int avio_rb32(AVIOContext *s)
Definition: aviobuf.c:790
skip_tag
static int skip_tag(AVIOContext *in, int32_t tag_name)
Definition: ismindex.c:132
duration
int64_t duration
Definition: movenc.c:64
avformat_open_input
int avformat_open_input(AVFormatContext **ps, const char *url, const AVInputFormat *fmt, AVDictionary **options)
Open an input stream and read the header.
Definition: demux.c:220
avio_open_dyn_buf
int avio_open_dyn_buf(AVIOContext **s)
Open a write only memory stream.
Definition: aviobuf.c:1483
intreadwrite.h
AV_CODEC_ID_WMAPRO
@ AV_CODEC_ID_WMAPRO
Definition: codec_id.h:464
MOV_TFHD_DEFAULT_DURATION
#define MOV_TFHD_DEFAULT_DURATION
Definition: isom.h:337
av_realloc_array
void * av_realloc_array(void *ptr, size_t nmemb, size_t size)
Definition: mem.c:225
AVMEDIA_TYPE_AUDIO
@ AVMEDIA_TYPE_AUDIO
Definition: avutil.h:202
AVCodecParameters::width
int width
Video only.
Definition: codec_par.h:127
MOV_TRUN_FIRST_SAMPLE_FLAGS
#define MOV_TRUN_FIRST_SAMPLE_FLAGS
Definition: isom.h:344
AVIO_FLAG_WRITE
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:629
ctx
AVFormatContext * ctx
Definition: movenc.c:48
Track::fourcc
const char * fourcc
Definition: ismindex.c:82
Track::height
int height
Definition: ismindex.c:75
AV_CODEC_ID_H264
@ AV_CODEC_ID_H264
Definition: codec_id.h:77
usage
static int usage(const char *argv0, int ret)
Definition: ismindex.c:56
avio_flush
void avio_flush(AVIOContext *s)
Force flushing of buffered data.
Definition: aviobuf.c:252
AVFormatContext
Format I/O context.
Definition: avformat.h:1213
AVStream::codecpar
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1108
AVStream::time_base
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented.
Definition: avformat.h:978
NULL
#define NULL
Definition: coverity.c:32
isom.h
avio_rb64
uint64_t avio_rb64(AVIOContext *s)
Definition: aviobuf.c:937
Track::blocksize
int blocksize
Definition: ismindex.c:83
read_tfra
static int read_tfra(struct Tracks *tracks, int start_index, AVIOContext *f)
Definition: ismindex.c:322
AVCodecParameters::ch_layout
AVChannelLayout ch_layout
Audio only.
Definition: codec_par.h:212
MOV_TFHD_BASE_DATA_OFFSET
#define MOV_TFHD_BASE_DATA_OFFSET
Definition: isom.h:335
index
int index
Definition: gxfenc.c:89
AVCodecParameters::sample_rate
int sample_rate
Audio only.
Definition: codec_par.h:177
AVCodecParameters::extradata_size
int extradata_size
Size of the extradata content in bytes.
Definition: codec_par.h:79
AV_WB32
#define AV_WB32(p, v)
Definition: intreadwrite.h:419
AVFormatContext::nb_streams
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1269
AV_CODEC_ID_AAC
@ AV_CODEC_ID_AAC
Definition: codec_id.h:429
av_rescale_rnd
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
avformat_find_stream_info
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
Read packets of a media file to get stream information.
Definition: demux.c:2415
Track::channels
int channels
Definition: ismindex.c:77
f
f
Definition: af_crystalizer.c:122
AVIOContext
Bytestream IO Context.
Definition: avio.h:162
avio_rb24
unsigned int avio_rb24(AVIOContext *s)
Definition: aviobuf.c:783
Tracks::nb_video_tracks
int nb_video_tracks
Definition: ismindex.c:92
main
int main(int argc, char **argv)
Definition: ismindex.c:785
MOV_TFHD_STSD_ID
#define MOV_TFHD_STSD_ID
Definition: isom.h:336
size
int size
Definition: twinvq_data.h:10344
MKBETAG
#define MKBETAG(a, b, c, d)
Definition: macros.h:56
Tracks::video_track
int video_track
Definition: ismindex.c:91
split
static char * split(char *message, char delim)
Definition: af_channelmap.c:81
Tracks::audio_track
int audio_track
Definition: ismindex.c:91
avio_write
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition: aviobuf.c:232
avio_wb32
void avio_wb32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:394
avio_r8
int avio_r8(AVIOContext *s)
Definition: aviobuf.c:632
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
version
version
Definition: libkvazaar.c:313
MoofOffset::duration
int64_t duration
Definition: ismindex.c:66
Tracks::tracks
struct Track ** tracks
Definition: ismindex.c:90
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:269
Track::sample_rate
int sample_rate
Definition: ismindex.c:77
print_track_chunks
static void print_track_chunks(FILE *out, struct Tracks *tracks, int main, const char *type)
Definition: ismindex.c:655
AVCodecParameters::height
int height
Definition: codec_par.h:128
AV_TIME_BASE
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
AVCodecParameters::block_align
int block_align
Audio only.
Definition: codec_par.h:184
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
av_mallocz
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:264
AV_CODEC_ID_VC1
@ AV_CODEC_ID_VC1
Definition: codec_id.h:120
get_video_private_data
static int get_video_private_data(struct Track *track, AVCodecParameters *codecpar)
Definition: ismindex.c:465
len
int len
Definition: vorbis_enc_data.h:426
Tracks::nb_audio_tracks
int nb_audio_tracks
Definition: ismindex.c:92
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:272
Track::is_video
int is_video
Definition: ismindex.c:74
Tracks
Definition: ismindex.c:87
Track::duration
int64_t duration
Definition: ismindex.c:71
tag
uint32_t tag
Definition: movenc.c:1646
AVStream::id
int id
Format-specific stream ID.
Definition: avformat.h:962
ret
ret
Definition: filter_design.txt:187
AVStream
Stream structure.
Definition: avformat.h:948
avio_seek
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:260
get_private_data
static int get_private_data(struct Track *track, AVCodecParameters *codecpar)
Definition: ismindex.c:454
pos
unsigned int pos
Definition: spdifenc.c:412
avformat.h
Track::width
int width
Definition: ismindex.c:75
Track::offsets
struct MoofOffset * offsets
Definition: ismindex.c:80
read_trun_duration
static int64_t read_trun_duration(AVIOContext *in, int default_duration, int64_t end)
Definition: ismindex.c:228
AVRational::den
int den
Denominator.
Definition: rational.h:60
avio_read
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:641
skip_fragment
static int skip_fragment(AVIOContext *in)
Definition: ismindex.c:166
temp
else temp
Definition: vf_mcdeint.c:248
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:628
MoofOffset::offset
int64_t offset
Definition: ismindex.c:65
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
Track::is_audio
int is_audio
Definition: ismindex.c:74
handle_file
static int handle_file(struct Tracks *tracks, const char *file, int split, int ismf, const char *basename, const char *output_prefix)
Definition: ismindex.c:496
Track::timescale
int timescale
Definition: ismindex.c:81
av_free
#define av_free(p)
Definition: tableprint_vlc.h:33
AVCodecParameters::codec_id
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: codec_par.h:61
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
Track::track_id
int track_id
Definition: ismindex.c:73
MOV_TRUN_SAMPLE_SIZE
#define MOV_TRUN_SAMPLE_SIZE
Definition: isom.h:346
int32_t
int32_t
Definition: audioconvert.c:56
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:561
AVCodecParameters::bit_rate
int64_t bit_rate
The average bitrate of the encoded data (in bits per second).
Definition: codec_par.h:90
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
MOV_TRUN_SAMPLE_CTS
#define MOV_TRUN_SAMPLE_CTS
Definition: isom.h:348
snprintf
#define snprintf
Definition: snprintf.h:34
AV_RB16
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:98
copy_tag
static int copy_tag(AVIOContext *in, AVIOContext *out, int32_t tag_name)
Definition: ismindex.c:107