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;
74  int is_audio, is_video;
75  int width, height;
76  int chunks;
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;
91  int video_track, audio_track;
92  int nb_video_tracks, nb_audio_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 
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 
245  if (flags & MOV_TRUN_DATA_OFFSET) avio_rb32(in);
246  if (flags & MOV_TRUN_FIRST_SAMPLE_FLAGS) avio_rb32(in);
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);
253  if (flags & MOV_TRUN_SAMPLE_SIZE) avio_rb32(in);
254  if (flags & MOV_TRUN_SAMPLE_FLAGS) 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 */
298  if (flags & MOV_TFHD_BASE_DATA_OFFSET)
299  avio_rb64(in);
300  if (flags & MOV_TFHD_STSD_ID)
301  avio_rb32(in);
302  if (flags & MOV_TFHD_DEFAULT_DURATION)
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_mallocz_array(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 
512  err = avformat_find_stream_info(ctx, NULL);
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->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 
606  avformat_close_input(&ctx);
607 
608  err = read_mfra(tracks, orig_tracks, file, split, ismf, basename,
609  output_prefix);
610 
611 fail:
612  if (ctx)
613  avformat_close_input(&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 
834  clean_tracks(&tracks);
835 
836  return 0;
837 }
static int copy_tag(AVIOContext *in, AVIOContext *out, int32_t tag_name)
Definition: ismindex.c:107
#define NULL
Definition: coverity.c:32
Bytestream IO Context.
Definition: avio.h:161
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition: aviobuf.c:339
int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer)
Return the written size and a pointer to the buffer.
Definition: aviobuf.c:1459
static int usage(const char *argv0, int ret)
Definition: ismindex.c:56
#define MOV_TFHD_DEFAULT_DURATION
Definition: isom.h:309
else temp
Definition: vf_mcdeint.c:256
channels
Definition: aptx.c:30
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: avcodec.h:3968
#define MOV_TRUN_SAMPLE_CTS
Definition: isom.h:320
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:246
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:675
GLint GLenum type
Definition: opengl_enc.c:104
int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options)
Open an input stream and read the header.
Definition: utils.c:539
static int expect_tag(int32_t got_tag, int32_t expected_tag)
Definition: ismindex.c:95
int version
Definition: avisynth_c.h:858
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:236
int is_audio
Definition: ismindex.c:74
static void print_track_chunks(FILE *out, struct Tracks *tracks, int main, const char *type)
Definition: ismindex.c:655
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_WB32 unsigned int_TMPL AV_WB24 unsigned int_TMPL AV_RB16
Definition: bytestream.h:87
int avio_open_dyn_buf(AVIOContext **s)
Open a write only memory stream.
Definition: aviobuf.c:1430
int tag
Definition: ismindex.c:84
This struct describes the properties of an encoded stream.
Definition: avcodec.h:3960
int height
Definition: ismindex.c:75
Format I/O context.
Definition: avformat.h:1358
uint8_t
Round toward +infinity.
Definition: mathematics.h:83
struct MoofOffset * offsets
Definition: ismindex.c:80
int width
Video only.
Definition: avcodec.h:4034
miscellaneous OS support macros and functions.
unsigned int avio_rb32(AVIOContext *s)
Definition: aviobuf.c:803
#define f(width, name)
Definition: cbs_vp9.c:255
static av_cold int end(AVCodecContext *avctx)
Definition: avrndec.c:90
int id
Format-specific stream ID.
Definition: avformat.h:888
int bitrate
Definition: ismindex.c:72
int nb_tracks
Definition: ismindex.c:88
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1426
#define height
static int skip_fragment(AVIOContext *in)
Definition: ismindex.c:166
#define MOV_TRUN_SAMPLE_SIZE
Definition: isom.h:318
uint32_t tag
Definition: movenc.c:1531
#define AVERROR_EOF
End of file.
Definition: error.h:55
Definition: ismindex.c:69
int64_t time
Definition: ismindex.c:64
ptrdiff_t size
Definition: opengl_enc.c:100
uint64_t avio_rb64(AVIOContext *s)
Definition: aviobuf.c:924
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition: avio.h:557
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition: aviobuf.c:218
void * av_realloc_array(void *ptr, size_t nmemb, size_t size)
Definition: mem.c:198
static int write_fragment(const char *filename, AVIOContext *in)
Definition: ismindex.c:145
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:650
int width
Definition: ismindex.c:75
int64_t bit_rate
The average bitrate of the encoded data (in bits per second).
Definition: avcodec.h:3997
int is_video
Definition: ismindex.c:74
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
int channels
Definition: ismindex.c:77
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: aviobuf.c:1217
enum AVMediaType codec_type
General type of the encoded data.
Definition: avcodec.h:3964
int sample_rate
Definition: ismindex.c:77
static void output_client_manifest(struct Tracks *tracks, const char *basename, const char *output_prefix, int split)
Definition: ismindex.c:690
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
int64_t duration
Definition: ismindex.c:89
#define FFMAX(a, b)
Definition: common.h:94
#define fail()
Definition: checkasm.h:122
int extradata_size
Size of the extradata content in bytes.
Definition: avcodec.h:3986
static char * split(char *message, char delim)
Definition: af_channelmap.c:81
int avio_r8(AVIOContext *s)
Definition: aviobuf.c:641
const char * fourcc
Definition: ismindex.c:82
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1414
int block_align
Audio only.
Definition: avcodec.h:4085
unsigned int avio_rb24(AVIOContext *s)
Definition: aviobuf.c:796
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
#define FFMIN(a, b)
Definition: common.h:96
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
int chunks
Definition: ismindex.c:76
int32_t
AVFormatContext * ctx
Definition: movenc.c:48
int audio_track
Definition: ismindex.c:91
#define MOV_TRUN_SAMPLE_DURATION
Definition: isom.h:317
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)
Rescale a 64-bit integer with specified rounding.
Definition: mathematics.c:58
Stream structure.
Definition: avformat.h:881
static int64_t read_moof_duration(AVIOContext *in, int64_t offset)
Definition: ismindex.c:270
const char * name
Definition: ismindex.c:70
static int skip_tag(AVIOContext *in, int32_t tag_name)
Definition: ismindex.c:132
int64_t duration
Definition: ismindex.c:71
uint8_t * codec_private
Definition: ismindex.c:78
#define MOV_TRUN_FIRST_SAMPLE_FLAGS
Definition: isom.h:316
int timescale
Definition: ismindex.c:81
void * buf
Definition: avisynth_c.h:766
uint8_t pi<< 24) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi-0x80)*(1.0f/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi-0x80)*(1.0/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t,(*(const int16_t *) pi >> 8)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t,*(const int16_t *) pi *(1.0f/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t,*(const int16_t *) pi *(1.0/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t,(*(const int32_t *) pi >> 24)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t,*(const int32_t *) pi *(1.0f/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t,*(const int32_t *) pi *(1.0/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8(lrintf(*(const float *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16(lrintf(*(const float *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(const float *) pi *(1U<< 31)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8(lrint(*(const double *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16(lrint(*(const double *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(const double *) pi *(1U<< 31))))#define SET_CONV_FUNC_GROUP(ofmt, ifmt) static void set_generic_function(AudioConvert *ac){}void ff_audio_convert_free(AudioConvert **ac){if(!*ac) return;ff_dither_free(&(*ac) ->dc);av_freep(ac);}AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt, int channels, int sample_rate, int apply_map){AudioConvert *ac;int in_planar, out_planar;ac=av_mallocz(sizeof(*ac));if(!ac) return NULL;ac->avr=avr;ac->out_fmt=out_fmt;ac->in_fmt=in_fmt;ac->channels=channels;ac->apply_map=apply_map;if(avr->dither_method!=AV_RESAMPLE_DITHER_NONE &&av_get_packed_sample_fmt(out_fmt)==AV_SAMPLE_FMT_S16 &&av_get_bytes_per_sample(in_fmt) > 2){ac->dc=ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, apply_map);if(!ac->dc){av_free(ac);return NULL;}return ac;}in_planar=ff_sample_fmt_is_planar(in_fmt, channels);out_planar=ff_sample_fmt_is_planar(out_fmt, channels);if(in_planar==out_planar){ac->func_type=CONV_FUNC_TYPE_FLAT;ac->planes=in_planar?ac->channels:1;}else if(in_planar) ac->func_type=CONV_FUNC_TYPE_INTERLEAVE;else ac->func_type=CONV_FUNC_TYPE_DEINTERLEAVE;set_generic_function(ac);if(ARCH_AARCH64) ff_audio_convert_init_aarch64(ac);if(ARCH_ARM) ff_audio_convert_init_arm(ac);if(ARCH_X86) ff_audio_convert_init_x86(ac);return ac;}int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in){int use_generic=1;int len=in->nb_samples;int p;if(ac->dc){av_log(ac->avr, AV_LOG_TRACE,"%d samples - audio_convert: %s to %s (dithered)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt));return ff_convert_dither(ac-> in
#define AV_WB32(p, v)
Definition: intreadwrite.h:419
int index
Definition: gxfenc.c:89
void avio_flush(AVIOContext *s)
Force flushing of buffered data.
Definition: aviobuf.c:238
int avio_open2(AVIOContext **s, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options)
Create and initialize a AVIOContext for accessing the resource indicated by url.
Definition: aviobuf.c:1211
#define snprintf
Definition: snprintf.h:34
static void clean_tracks(struct Tracks *tracks)
Definition: ismindex.c:773
#define MOV_TRUN_SAMPLE_FLAGS
Definition: isom.h:319
struct Track ** tracks
Definition: ismindex.c:90
#define MOV_TFHD_STSD_ID
Definition: isom.h:308
int track_id
Definition: ismindex.c:73
static int64_t pts
#define flags(name, subs,...)
Definition: cbs_av1.c:561
int64_t duration
Definition: ismindex.c:66
int codec_private_size
Definition: ismindex.c:79
int av_strerror(int errnum, char *errbuf, size_t errbuf_size)
Put a description of the AVERROR code errnum in errbuf.
Definition: error.c:105
static int get_video_private_data(struct Track *track, AVCodecParameters *codecpar)
Definition: ismindex.c:465
int64_t duration
Decoding: duration of the stream, in stream time base.
Definition: avformat.h:930
int sample_rate
Audio only.
Definition: avcodec.h:4078
Main libavformat public API header.
int video_track
Definition: ismindex.c:91
int blocksize
Definition: ismindex.c:83
#define MOV_TFHD_BASE_DATA_OFFSET
Definition: isom.h:307
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
Read packets of a media file to get stream information.
Definition: utils.c:3600
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
int den
Denominator.
Definition: rational.h:60
void avformat_close_input(AVFormatContext **s)
Close an opened input AVFormatContext.
Definition: utils.c:4474
#define MKBETAG(a, b, c, d)
Definition: common.h:367
#define av_free(p)
int len
uint8_t * extradata
Extra binary data needed for initializing the decoder, codec-dependent.
Definition: avcodec.h:3982
#define MOV_TRUN_DATA_OFFSET
Definition: isom.h:315
int channels
Audio only.
Definition: avcodec.h:4074
void avio_wb32(AVIOContext *s, unsigned int val)
Definition: aviobuf.c:380
FILE * out
Definition: movenc.c:54
#define av_freep(p)
static int read_tfra(struct Tracks *tracks, int start_index, AVIOContext *f)
Definition: ismindex.c:322
static int get_private_data(struct Track *track, AVCodecParameters *codecpar)
Definition: ismindex.c:454
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1028
int nb_audio_tracks
Definition: ismindex.c:92
uint32_t codec_tag
Additional information about the codec (corresponds to the AVI FOURCC).
Definition: avcodec.h:3972
int main(int argc, char **argv)
Definition: ismindex.c:785
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented...
Definition: avformat.h:910
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
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
void * av_mallocz_array(size_t nmemb, size_t size)
Definition: mem.c:191
int64_t offset
Definition: ismindex.c:65
static int64_t read_trun_duration(AVIOContext *in, int default_duration, int64_t end)
Definition: ismindex.c:228
int nb_video_tracks
Definition: ismindex.c:92