FFmpeg
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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 {
68 };
69 
70 struct Track {
71  const char *name;
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;
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  fprintf(stderr, "Unable to open %s: %s\n", filename, av_err2str(ret));
153  return ret;
154  }
155  ret = copy_tag(in, out, MKBETAG('m', 'o', 'o', 'f'));
156  if (!ret)
157  ret = copy_tag(in, out, MKBETAG('m', 'd', 'a', 't'));
158 
159  avio_flush(out);
160  avio_close(out);
161 
162  return ret;
163 }
164 
165 static int skip_fragment(AVIOContext *in)
166 {
167  int ret;
168  ret = skip_tag(in, MKBETAG('m', 'o', 'o', 'f'));
169  if (!ret)
170  ret = skip_tag(in, MKBETAG('m', 'd', 'a', 't'));
171  return ret;
172 }
173 
174 static int write_fragments(struct Tracks *tracks, int start_index,
175  AVIOContext *in, const char *basename,
176  int split, int ismf, const char* output_prefix)
177 {
178  char dirname[2048], filename[2048], idxname[2048];
179  int i, j, ret = 0, fragment_ret;
180  FILE* out = NULL;
181 
182  if (ismf) {
183  snprintf(idxname, sizeof(idxname), "%s%s.ismf", output_prefix, basename);
184  out = fopen(idxname, "w");
185  if (!out) {
186  ret = AVERROR(errno);
187  perror(idxname);
188  goto fail;
189  }
190  }
191  for (i = start_index; i < tracks->nb_tracks; i++) {
192  struct Track *track = tracks->tracks[i];
193  const char *type = track->is_video ? "video" : "audio";
194  snprintf(dirname, sizeof(dirname), "%sQualityLevels(%d)", output_prefix, track->bitrate);
195  if (split) {
196  if (mkdir(dirname, 0777) == -1 && errno != EEXIST) {
197  ret = AVERROR(errno);
198  perror(dirname);
199  goto fail;
200  }
201  }
202  for (j = 0; j < track->chunks; j++) {
203  snprintf(filename, sizeof(filename), "%s/Fragments(%s=%"PRId64")",
204  dirname, type, track->offsets[j].time);
205  avio_seek(in, track->offsets[j].offset, SEEK_SET);
206  if (ismf)
207  fprintf(out, "%s %"PRId64, filename, avio_tell(in));
208  if (split)
209  fragment_ret = write_fragment(filename, in);
210  else
211  fragment_ret = skip_fragment(in);
212  if (ismf)
213  fprintf(out, " %"PRId64"\n", avio_tell(in));
214  if (fragment_ret != 0) {
215  fprintf(stderr, "failed fragment %d in track %d (%s)\n", j,
216  track->track_id, track->name);
217  ret = fragment_ret;
218  }
219  }
220  }
221 fail:
222  if (out)
223  fclose(out);
224  return ret;
225 }
226 
227 static int64_t read_trun_duration(AVIOContext *in, int default_duration,
228  int64_t end)
229 {
230  int64_t dts = 0;
231  int64_t pos;
232  int flags, i;
233  int entries;
234  int64_t first_pts = 0;
235  int64_t max_pts = 0;
236  avio_r8(in); /* version */
237  flags = avio_rb24(in);
238  if (default_duration <= 0 && !(flags & MOV_TRUN_SAMPLE_DURATION)) {
239  fprintf(stderr, "No sample duration in trun flags\n");
240  return -1;
241  }
242  entries = avio_rb32(in);
243 
246 
247  pos = avio_tell(in);
248  for (i = 0; i < entries && pos < end; i++) {
249  int sample_duration = default_duration;
250  int64_t pts = dts;
251  if (flags & MOV_TRUN_SAMPLE_DURATION) sample_duration = avio_rb32(in);
254  if (flags & MOV_TRUN_SAMPLE_CTS) pts += avio_rb32(in);
255  if (sample_duration < 0) {
256  fprintf(stderr, "Negative sample duration %d\n", sample_duration);
257  return -1;
258  }
259  if (i == 0)
260  first_pts = pts;
261  max_pts = FFMAX(max_pts, pts + sample_duration);
262  dts += sample_duration;
263  pos = avio_tell(in);
264  }
265 
266  return max_pts - first_pts;
267 }
268 
270 {
271  int64_t ret = -1;
272  int32_t moof_size, size, tag;
273  int64_t pos = 0;
274  int default_duration = 0;
275 
276  avio_seek(in, offset, SEEK_SET);
277  moof_size = avio_rb32(in);
278  tag = avio_rb32(in);
279  if (expect_tag(tag, MKBETAG('m', 'o', 'o', 'f')) != 0)
280  goto fail;
281  while (pos < offset + moof_size) {
282  pos = avio_tell(in);
283  size = avio_rb32(in);
284  tag = avio_rb32(in);
285  if (tag == MKBETAG('t', 'r', 'a', 'f')) {
286  int64_t traf_pos = pos;
287  int64_t traf_size = size;
288  while (pos < traf_pos + traf_size) {
289  pos = avio_tell(in);
290  size = avio_rb32(in);
291  tag = avio_rb32(in);
292  if (tag == MKBETAG('t', 'f', 'h', 'd')) {
293  int flags = 0;
294  avio_r8(in); /* version */
295  flags = avio_rb24(in);
296  avio_rb32(in); /* track_id */
298  avio_rb64(in);
299  if (flags & MOV_TFHD_STSD_ID)
300  avio_rb32(in);
302  default_duration = avio_rb32(in);
303  }
304  if (tag == MKBETAG('t', 'r', 'u', 'n')) {
305  return read_trun_duration(in, default_duration,
306  pos + size);
307  }
308  avio_seek(in, pos + size, SEEK_SET);
309  }
310  fprintf(stderr, "Couldn't find trun\n");
311  goto fail;
312  }
313  avio_seek(in, pos + size, SEEK_SET);
314  }
315  fprintf(stderr, "Couldn't find traf\n");
316 
317 fail:
318  return ret;
319 }
320 
321 static int read_tfra(struct Tracks *tracks, int start_index, AVIOContext *f)
322 {
323  int ret = AVERROR_EOF, track_id;
324  int version, fieldlength, i, j;
325  int64_t pos = avio_tell(f);
326  uint32_t size = avio_rb32(f);
327  struct Track *track = NULL;
328 
329  if (avio_rb32(f) != MKBETAG('t', 'f', 'r', 'a'))
330  goto fail;
331  version = avio_r8(f);
332  avio_rb24(f);
333  track_id = avio_rb32(f); /* track id */
334  for (i = start_index; i < tracks->nb_tracks && !track; i++)
335  if (tracks->tracks[i]->track_id == track_id)
336  track = tracks->tracks[i];
337  if (!track) {
338  /* Ok, continue parsing the next atom */
339  ret = 0;
340  goto fail;
341  }
342  fieldlength = avio_rb32(f);
343  track->chunks = avio_rb32(f);
344  track->offsets = av_calloc(track->chunks, sizeof(*track->offsets));
345  if (!track->offsets) {
346  track->chunks = 0;
347  ret = AVERROR(ENOMEM);
348  goto fail;
349  }
350  // The duration here is always the difference between consecutive
351  // start times.
352  for (i = 0; i < track->chunks; i++) {
353  if (version == 1) {
354  track->offsets[i].time = avio_rb64(f);
355  track->offsets[i].offset = avio_rb64(f);
356  } else {
357  track->offsets[i].time = avio_rb32(f);
358  track->offsets[i].offset = avio_rb32(f);
359  }
360  for (j = 0; j < ((fieldlength >> 4) & 3) + 1; j++)
361  avio_r8(f);
362  for (j = 0; j < ((fieldlength >> 2) & 3) + 1; j++)
363  avio_r8(f);
364  for (j = 0; j < ((fieldlength >> 0) & 3) + 1; j++)
365  avio_r8(f);
366  if (i > 0)
367  track->offsets[i - 1].duration = track->offsets[i].time -
368  track->offsets[i - 1].time;
369  }
370  if (track->chunks > 0) {
371  track->offsets[track->chunks - 1].duration = track->offsets[0].time +
372  track->duration -
373  track->offsets[track->chunks - 1].time;
374  }
375  // Now try to read the actual durations from the trun sample data.
376  for (i = 0; i < track->chunks; i++) {
378  if (duration > 0 && llabs(duration - track->offsets[i].duration) > 3) {
379  // 3 allows for integer duration to drift a few units,
380  // e.g., for 1/3 durations
381  track->offsets[i].duration = duration;
382  }
383  }
384  if (track->chunks > 0) {
385  if (track->offsets[track->chunks - 1].duration <= 0) {
386  fprintf(stderr, "Calculated last chunk duration for track %d "
387  "was non-positive (%"PRId64"), probably due to missing "
388  "fragments ", track->track_id,
389  track->offsets[track->chunks - 1].duration);
390  if (track->chunks > 1) {
391  track->offsets[track->chunks - 1].duration =
392  track->offsets[track->chunks - 2].duration;
393  } else {
394  track->offsets[track->chunks - 1].duration = 1;
395  }
396  fprintf(stderr, "corrected to %"PRId64"\n",
397  track->offsets[track->chunks - 1].duration);
398  track->duration = track->offsets[track->chunks - 1].time +
399  track->offsets[track->chunks - 1].duration -
400  track->offsets[0].time;
401  fprintf(stderr, "Track duration corrected to %"PRId64"\n",
402  track->duration);
403  }
404  }
405  ret = 0;
406 
407 fail:
408  avio_seek(f, pos + size, SEEK_SET);
409  return ret;
410 }
411 
412 static int read_mfra(struct Tracks *tracks, int start_index,
413  const char *file, int split, int ismf,
414  const char *basename, const char* output_prefix)
415 {
416  int err = 0;
417  const char* err_str = "";
418  AVIOContext *f = NULL;
419  int32_t mfra_size;
420 
421  if ((err = avio_open2(&f, file, AVIO_FLAG_READ, NULL, NULL)) < 0)
422  goto fail;
423  avio_seek(f, avio_size(f) - 4, SEEK_SET);
424  mfra_size = avio_rb32(f);
425  avio_seek(f, -mfra_size, SEEK_CUR);
426  if (avio_rb32(f) != mfra_size) {
427  err = AVERROR_INVALIDDATA;
428  err_str = "mfra size mismatch";
429  goto fail;
430  }
431  if (avio_rb32(f) != MKBETAG('m', 'f', 'r', 'a')) {
432  err = AVERROR_INVALIDDATA;
433  err_str = "mfra tag mismatch";
434  goto fail;
435  }
436  while (!read_tfra(tracks, start_index, f)) {
437  /* Empty */
438  }
439 
440  if (split || ismf)
441  err = write_fragments(tracks, start_index, f, basename, split, ismf,
442  output_prefix);
443  err_str = "error in write_fragments";
444 
445 fail:
446  if (f)
447  avio_close(f);
448  if (err)
449  fprintf(stderr, "Unable to read the MFRA atom in %s (%s)\n", file, err_str);
450  return err;
451 }
452 
453 static int get_private_data(struct Track *track, AVCodecParameters *codecpar)
454 {
455  track->codec_private_size = 0;
456  track->codec_private = av_mallocz(codecpar->extradata_size);
457  if (!track->codec_private)
458  return AVERROR(ENOMEM);
459  track->codec_private_size = codecpar->extradata_size;
460  memcpy(track->codec_private, codecpar->extradata, codecpar->extradata_size);
461  return 0;
462 }
463 
464 static int get_video_private_data(struct Track *track, AVCodecParameters *codecpar)
465 {
466  AVIOContext *io = NULL;
467  uint16_t sps_size, pps_size;
468  int err;
469 
470  if (codecpar->codec_id == AV_CODEC_ID_VC1)
471  return get_private_data(track, codecpar);
472 
473  if ((err = avio_open_dyn_buf(&io)) < 0)
474  goto fail;
475  err = AVERROR(EINVAL);
476  if (codecpar->extradata_size < 11 || codecpar->extradata[0] != 1)
477  goto fail;
478  sps_size = AV_RB16(&codecpar->extradata[6]);
479  if (11 + sps_size > codecpar->extradata_size)
480  goto fail;
481  avio_wb32(io, 0x00000001);
482  avio_write(io, &codecpar->extradata[8], sps_size);
483  pps_size = AV_RB16(&codecpar->extradata[9 + sps_size]);
484  if (11 + sps_size + pps_size > codecpar->extradata_size)
485  goto fail;
486  avio_wb32(io, 0x00000001);
487  avio_write(io, &codecpar->extradata[11 + sps_size], pps_size);
488  err = 0;
489 
490 fail:
492  return err;
493 }
494 
495 static int handle_file(struct Tracks *tracks, const char *file, int split,
496  int ismf, const char *basename,
497  const char* output_prefix)
498 {
500  int err = 0, i, orig_tracks = tracks->nb_tracks;
501  char *ptr;
502  struct Track *track;
503 
504  err = avformat_open_input(&ctx, file, NULL, NULL);
505  if (err < 0) {
506  fprintf(stderr, "Unable to open %s: %s\n", file, av_err2str(err));
507  return 1;
508  }
509 
511  if (err < 0) {
512  fprintf(stderr, "Unable to identify %s: %s\n", file, av_err2str(err));
513  goto fail;
514  }
515 
516  if (ctx->nb_streams < 1) {
517  fprintf(stderr, "No streams found in %s\n", file);
518  goto fail;
519  }
520 
521  for (i = 0; i < ctx->nb_streams; i++) {
522  struct Track **temp;
523  AVStream *st = ctx->streams[i];
524 
525  if (st->codecpar->bit_rate == 0) {
526  fprintf(stderr, "Skipping track %d in %s as it has zero bitrate\n",
527  st->id, file);
528  continue;
529  }
530 
531  track = av_mallocz(sizeof(*track));
532  if (!track) {
533  err = AVERROR(ENOMEM);
534  goto fail;
535  }
536  temp = av_realloc_array(tracks->tracks,
537  tracks->nb_tracks + 1,
538  sizeof(*tracks->tracks));
539  if (!temp) {
540  av_free(track);
541  err = AVERROR(ENOMEM);
542  goto fail;
543  }
544  tracks->tracks = temp;
545  tracks->tracks[tracks->nb_tracks] = track;
546 
547  track->name = file;
548  if ((ptr = strrchr(file, '/')))
549  track->name = ptr + 1;
550 
551  track->bitrate = st->codecpar->bit_rate;
552  track->track_id = st->id;
553  track->timescale = st->time_base.den;
554  track->duration = st->duration;
557 
558  if (!track->is_audio && !track->is_video) {
559  fprintf(stderr,
560  "Track %d in %s is neither video nor audio, skipping\n",
561  track->track_id, file);
562  av_freep(&tracks->tracks[tracks->nb_tracks]);
563  continue;
564  }
565 
566  tracks->duration = FFMAX(tracks->duration,
568  track->timescale, AV_ROUND_UP));
569 
570  if (track->is_audio) {
571  if (tracks->audio_track < 0)
572  tracks->audio_track = tracks->nb_tracks;
573  tracks->nb_audio_tracks++;
574  track->channels = st->codecpar->ch_layout.nb_channels;
575  track->sample_rate = st->codecpar->sample_rate;
576  if (st->codecpar->codec_id == AV_CODEC_ID_AAC) {
577  track->fourcc = "AACL";
578  track->tag = 255;
579  track->blocksize = 4;
580  } else if (st->codecpar->codec_id == AV_CODEC_ID_WMAPRO) {
581  track->fourcc = "WMAP";
582  track->tag = st->codecpar->codec_tag;
583  track->blocksize = st->codecpar->block_align;
584  }
585  get_private_data(track, st->codecpar);
586  }
587  if (track->is_video) {
588  if (tracks->video_track < 0)
589  tracks->video_track = tracks->nb_tracks;
590  tracks->nb_video_tracks++;
591  track->width = st->codecpar->width;
592  track->height = st->codecpar->height;
593  if (st->codecpar->codec_id == AV_CODEC_ID_H264)
594  track->fourcc = "H264";
595  else if (st->codecpar->codec_id == AV_CODEC_ID_VC1)
596  track->fourcc = "WVC1";
597  get_video_private_data(track, st->codecpar);
598  }
599 
600  tracks->nb_tracks++;
601  }
602 
604 
605  err = read_mfra(tracks, orig_tracks, file, split, ismf, basename,
606  output_prefix);
607 
608 fail:
609  if (ctx)
611  return err;
612 }
613 
614 static void output_server_manifest(struct Tracks *tracks, const char *basename,
615  const char *output_prefix,
616  const char *path_prefix,
617  const char *ismc_prefix)
618 {
619  char filename[1000];
620  FILE *out;
621  int i;
622 
623  snprintf(filename, sizeof(filename), "%s%s.ism", output_prefix, basename);
624  out = fopen(filename, "w");
625  if (!out) {
626  perror(filename);
627  return;
628  }
629  fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
630  fprintf(out, "<smil xmlns=\"http://www.w3.org/2001/SMIL20/Language\">\n");
631  fprintf(out, "\t<head>\n");
632  fprintf(out, "\t\t<meta name=\"clientManifestRelativePath\" "
633  "content=\"%s%s.ismc\" />\n", ismc_prefix, basename);
634  fprintf(out, "\t</head>\n");
635  fprintf(out, "\t<body>\n");
636  fprintf(out, "\t\t<switch>\n");
637  for (i = 0; i < tracks->nb_tracks; i++) {
638  struct Track *track = tracks->tracks[i];
639  const char *type = track->is_video ? "video" : "audio";
640  fprintf(out, "\t\t\t<%s src=\"%s%s\" systemBitrate=\"%d\">\n",
641  type, path_prefix, track->name, track->bitrate);
642  fprintf(out, "\t\t\t\t<param name=\"trackID\" value=\"%d\" "
643  "valueType=\"data\" />\n", track->track_id);
644  fprintf(out, "\t\t\t</%s>\n", type);
645  }
646  fprintf(out, "\t\t</switch>\n");
647  fprintf(out, "\t</body>\n");
648  fprintf(out, "</smil>\n");
649  fclose(out);
650 }
651 
652 static void print_track_chunks(FILE *out, struct Tracks *tracks, int main,
653  const char *type)
654 {
655  int i, j;
656  int64_t pos = 0;
657  struct Track *track = tracks->tracks[main];
658  int should_print_time_mismatch = 1;
659 
660  for (i = 0; i < track->chunks; i++) {
661  for (j = main + 1; j < tracks->nb_tracks; j++) {
662  if (tracks->tracks[j]->is_audio == track->is_audio) {
663  if (track->offsets[i].duration != tracks->tracks[j]->offsets[i].duration) {
664  fprintf(stderr, "Mismatched duration of %s chunk %d in %s (%d) and %s (%d)\n",
665  type, i, track->name, main, tracks->tracks[j]->name, j);
666  should_print_time_mismatch = 1;
667  }
668  if (track->offsets[i].time != tracks->tracks[j]->offsets[i].time) {
669  if (should_print_time_mismatch)
670  fprintf(stderr, "Mismatched (start) time of %s chunk %d in %s (%d) and %s (%d)\n",
671  type, i, track->name, main, tracks->tracks[j]->name, j);
672  should_print_time_mismatch = 0;
673  }
674  }
675  }
676  fprintf(out, "\t\t<c n=\"%d\" d=\"%"PRId64"\" ",
677  i, track->offsets[i].duration);
678  if (pos != track->offsets[i].time) {
679  fprintf(out, "t=\"%"PRId64"\" ", track->offsets[i].time);
680  pos = track->offsets[i].time;
681  }
682  pos += track->offsets[i].duration;
683  fprintf(out, "/>\n");
684  }
685 }
686 
687 static void output_client_manifest(struct Tracks *tracks, const char *basename,
688  const char *output_prefix, int split)
689 {
690  char filename[1000];
691  FILE *out;
692  int i, j;
693 
694  if (split)
695  snprintf(filename, sizeof(filename), "%sManifest", output_prefix);
696  else
697  snprintf(filename, sizeof(filename), "%s%s.ismc", output_prefix, basename);
698  out = fopen(filename, "w");
699  if (!out) {
700  perror(filename);
701  return;
702  }
703  fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
704  fprintf(out, "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" "
705  "Duration=\"%"PRId64 "\">\n", tracks->duration * 10);
706  if (tracks->video_track >= 0) {
707  struct Track *track = tracks->tracks[tracks->video_track];
708  struct Track *first_track = track;
709  int index = 0;
710  fprintf(out,
711  "\t<StreamIndex Type=\"video\" QualityLevels=\"%d\" "
712  "Chunks=\"%d\" "
713  "Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n",
714  tracks->nb_video_tracks, track->chunks);
715  for (i = 0; i < tracks->nb_tracks; i++) {
716  track = tracks->tracks[i];
717  if (!track->is_video)
718  continue;
719  fprintf(out,
720  "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
721  "FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" "
722  "CodecPrivateData=\"",
723  index, track->bitrate, track->fourcc, track->width, track->height);
724  for (j = 0; j < track->codec_private_size; j++)
725  fprintf(out, "%02X", track->codec_private[j]);
726  fprintf(out, "\" />\n");
727  index++;
728  if (track->chunks != first_track->chunks)
729  fprintf(stderr, "Mismatched number of video chunks in %s (id: %d, chunks %d) and %s (id: %d, chunks %d)\n",
730  track->name, track->track_id, track->chunks, first_track->name, first_track->track_id, first_track->chunks);
731  }
732  print_track_chunks(out, tracks, tracks->video_track, "video");
733  fprintf(out, "\t</StreamIndex>\n");
734  }
735  if (tracks->audio_track >= 0) {
736  struct Track *track = tracks->tracks[tracks->audio_track];
737  struct Track *first_track = track;
738  int index = 0;
739  fprintf(out,
740  "\t<StreamIndex Type=\"audio\" QualityLevels=\"%d\" "
741  "Chunks=\"%d\" "
742  "Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n",
743  tracks->nb_audio_tracks, track->chunks);
744  for (i = 0; i < tracks->nb_tracks; i++) {
745  track = tracks->tracks[i];
746  if (!track->is_audio)
747  continue;
748  fprintf(out,
749  "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
750  "FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" "
751  "BitsPerSample=\"16\" PacketSize=\"%d\" "
752  "AudioTag=\"%d\" CodecPrivateData=\"",
753  index, track->bitrate, track->fourcc, track->sample_rate,
754  track->channels, track->blocksize, track->tag);
755  for (j = 0; j < track->codec_private_size; j++)
756  fprintf(out, "%02X", track->codec_private[j]);
757  fprintf(out, "\" />\n");
758  index++;
759  if (track->chunks != first_track->chunks)
760  fprintf(stderr, "Mismatched number of audio chunks in %s and %s\n",
761  track->name, first_track->name);
762  }
763  print_track_chunks(out, tracks, tracks->audio_track, "audio");
764  fprintf(out, "\t</StreamIndex>\n");
765  }
766  fprintf(out, "</SmoothStreamingMedia>\n");
767  fclose(out);
768 }
769 
770 static void clean_tracks(struct Tracks *tracks)
771 {
772  int i;
773  for (i = 0; i < tracks->nb_tracks; i++) {
774  av_freep(&tracks->tracks[i]->codec_private);
775  av_freep(&tracks->tracks[i]->offsets);
776  av_freep(&tracks->tracks[i]);
777  }
778  av_freep(&tracks->tracks);
779  tracks->nb_tracks = 0;
780 }
781 
782 int main(int argc, char **argv)
783 {
784  const char *basename = NULL;
785  const char *path_prefix = "", *ismc_prefix = "";
786  const char *output_prefix = "";
787  char output_prefix_buf[2048];
788  int split = 0, ismf = 0, i;
789  struct Tracks tracks = { 0, .video_track = -1, .audio_track = -1 };
790 
791  for (i = 1; i < argc; i++) {
792  if (!strcmp(argv[i], "-n")) {
793  basename = argv[i + 1];
794  i++;
795  } else if (!strcmp(argv[i], "-path-prefix")) {
796  path_prefix = argv[i + 1];
797  i++;
798  } else if (!strcmp(argv[i], "-ismc-prefix")) {
799  ismc_prefix = argv[i + 1];
800  i++;
801  } else if (!strcmp(argv[i], "-output")) {
802  output_prefix = argv[i + 1];
803  i++;
804  if (output_prefix[strlen(output_prefix) - 1] != '/') {
805  snprintf(output_prefix_buf, sizeof(output_prefix_buf),
806  "%s/", output_prefix);
807  output_prefix = output_prefix_buf;
808  }
809  } else if (!strcmp(argv[i], "-split")) {
810  split = 1;
811  } else if (!strcmp(argv[i], "-ismf")) {
812  ismf = 1;
813  } else if (argv[i][0] == '-') {
814  return usage(argv[0], 1);
815  } else {
816  if (!basename)
817  ismf = 0;
818  if (handle_file(&tracks, argv[i], split, ismf,
819  basename, output_prefix))
820  return 1;
821  }
822  }
823  if (!tracks.nb_tracks || (!basename && !split))
824  return usage(argv[0], 1);
825 
826  if (!split)
827  output_server_manifest(&tracks, basename, output_prefix,
828  path_prefix, ismc_prefix);
829  output_client_manifest(&tracks, basename, output_prefix, split);
830 
832 
833  return 0;
834 }
flags
const SwsFlags flags[]
Definition: swscale.c:61
AV_ROUND_UP
@ AV_ROUND_UP
Round toward +infinity.
Definition: mathematics.h:134
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:408
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:269
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
int64_t
long long int64_t
Definition: coverity.c:34
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:1333
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:614
AVChannelLayout::nb_channels
int nb_channels
Number of channels in this layout.
Definition: channel_layout.h:329
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:367
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:412
MoofOffset::time
int64_t time
Definition: ismindex.c:65
fail
#define fail()
Definition: checkasm.h:194
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:687
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:174
MOV_TRUN_SAMPLE_DURATION
#define MOV_TRUN_SAMPLE_DURATION
Definition: isom.h:406
clean_tracks
static void clean_tracks(struct Tracks *tracks)
Definition: ismindex.c:770
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
AVStream::duration
int64_t duration
Decoding: duration of the stream, in stream time base.
Definition: avformat.h:804
Tracks::nb_tracks
int nb_tracks
Definition: ismindex.c:89
MOV_TRUN_DATA_OFFSET
#define MOV_TRUN_DATA_OFFSET
Definition: isom.h:404
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:217
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:485
MOV_TFHD_DEFAULT_DURATION
#define MOV_TFHD_DEFAULT_DURATION
Definition: isom.h:398
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:405
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:1265
AVStream::codecpar
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:768
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:784
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:321
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:396
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:415
AVFormatContext::nb_streams
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1321
AV_CODEC_ID_AAC
@ AV_CODEC_ID_AAC
Definition: codec_id.h:450
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:2507
Track::channels
int channels
Definition: ismindex.c:78
f
f
Definition: af_crystalizer.c:122
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
av_err2str
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: error.h:122
main
int main(int argc, char **argv)
Definition: ismindex.c:782
MOV_TFHD_STSD_ID
#define MOV_TFHD_STSD_ID
Definition: isom.h:397
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:89
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:315
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:652
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:464
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:1911
AVStream::id
int id
Format-specific stream ID.
Definition: avformat.h:757
ret
ret
Definition: filter_design.txt:187
AVStream
Stream structure.
Definition: avformat.h:745
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:453
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:227
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:165
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:495
Track::timescale
int timescale
Definition: ismindex.c:82
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
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:35
Track::track_id
int track_id
Definition: ismindex.c:74
MOV_TRUN_SAMPLE_SIZE
#define MOV_TRUN_SAMPLE_SIZE
Definition: isom.h:407
int32_t
int32_t
Definition: audioconvert.c:56
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:409
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