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