ismindex: Verify that all bitrate variants match
[ffmpeg.git] / tools / ismindex.c
index 4e14d19..54c49dc 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include <sys/stat.h>
+#ifdef _WIN32
+#include <io.h>
+#define mkdir(a, b) mkdir(a)
+#endif
+
 #include "libavformat/avformat.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mathematics.h"
@@ -82,7 +87,7 @@ static int copy_tag(AVIOContext *in, AVIOContext *out, int32_t tag_name)
     int32_t size, tag;
 
     size = avio_rb32(in);
-    tag = avio_rb32(in);
+    tag  = avio_rb32(in);
     avio_wb32(out, size);
     avio_wb32(out, tag);
     if (tag != tag_name)
@@ -123,7 +128,7 @@ static int write_fragments(struct VideoFiles *files, int start_index,
 
     for (i = start_index; i < files->nb_files; i++) {
         struct VideoFile *vf = files->files[i];
-        const char *type = vf->is_video ? "video" : "audio";
+        const char *type     = vf->is_video ? "video" : "audio";
         snprintf(dirname, sizeof(dirname), "QualityLevels(%d)", vf->bitrate);
         mkdir(dirname, 0777);
         for (j = 0; j < vf->chunks; j++) {
@@ -140,7 +145,7 @@ static int read_tfra(struct VideoFiles *files, int start_index, AVIOContext *f)
 {
     int ret = AVERROR_EOF, track_id;
     int version, fieldlength, i, j;
-    int64_t pos = avio_tell(f);
+    int64_t pos   = avio_tell(f);
     uint32_t size = avio_rb32(f);
     struct VideoFile *vf = NULL;
 
@@ -149,17 +154,16 @@ static int read_tfra(struct VideoFiles *files, int start_index, AVIOContext *f)
     version = avio_r8(f);
     avio_rb24(f);
     track_id = avio_rb32(f); /* track id */
-    for (i = start_index; i < files->nb_files && !vf; i++) {
+    for (i = start_index; i < files->nb_files && !vf; i++)
         if (files->files[i]->track_id == track_id)
             vf = files->files[i];
-    }
     if (!vf) {
         /* Ok, continue parsing the next atom */
         ret = 0;
         goto fail;
     }
     fieldlength = avio_rb32(f);
-    vf->chunks = avio_rb32(f);
+    vf->chunks  = avio_rb32(f);
     vf->offsets = av_mallocz(sizeof(*vf->offsets) * vf->chunks);
     if (!vf->offsets) {
         ret = AVERROR(ENOMEM);
@@ -167,10 +171,10 @@ static int read_tfra(struct VideoFiles *files, int start_index, AVIOContext *f)
     }
     for (i = 0; i < vf->chunks; i++) {
         if (version == 1) {
-            vf->offsets[i].time = avio_rb64(f);
+            vf->offsets[i].time   = avio_rb64(f);
             vf->offsets[i].offset = avio_rb64(f);
         } else {
-            vf->offsets[i].time = avio_rb32(f);
+            vf->offsets[i].time   = avio_rb32(f);
             vf->offsets[i].offset = avio_rb32(f);
         }
         for (j = 0; j < ((fieldlength >> 4) & 3) + 1; j++)
@@ -187,6 +191,7 @@ static int read_tfra(struct VideoFiles *files, int start_index, AVIOContext *f)
         vf->offsets[vf->chunks - 1].duration = vf->duration -
                                                vf->offsets[vf->chunks - 1].time;
     ret = 0;
+
 fail:
     avio_seek(f, pos + size, SEEK_SET);
     return ret;
@@ -204,10 +209,14 @@ static int read_mfra(struct VideoFiles *files, int start_index,
     avio_seek(f, avio_size(f) - 4, SEEK_SET);
     mfra_size = avio_rb32(f);
     avio_seek(f, -mfra_size, SEEK_CUR);
-    if (avio_rb32(f) != mfra_size)
+    if (avio_rb32(f) != mfra_size) {
+        err = AVERROR_INVALIDDATA;
         goto fail;
-    if (avio_rb32(f) != MKBETAG('m', 'f', 'r', 'a'))
+    }
+    if (avio_rb32(f) != MKBETAG('m', 'f', 'r', 'a')) {
+        err = AVERROR_INVALIDDATA;
         goto fail;
+    }
     while (!read_tfra(files, start_index, f)) {
         /* Empty */
     }
@@ -218,13 +227,15 @@ static int read_mfra(struct VideoFiles *files, int start_index,
 fail:
     if (f)
         avio_close(f);
+    if (err)
+        fprintf(stderr, "Unable to read the MFRA atom in %s\n", file);
     return err;
 }
 
 static int get_private_data(struct VideoFile *vf, AVCodecContext *codec)
 {
     vf->codec_private_size = codec->extradata_size;
-    vf->codec_private = av_mallocz(codec->extradata_size);
+    vf->codec_private      = av_mallocz(codec->extradata_size);
     if (!vf->codec_private)
         return AVERROR(ENOMEM);
     memcpy(vf->codec_private, codec->extradata, codec->extradata_size);
@@ -254,6 +265,7 @@ static int get_video_private_data(struct VideoFile *vf, AVCodecContext *codec)
     avio_wb32(io, 0x00000001);
     avio_write(io, &codec->extradata[11 + sps_size], pps_size);
     err = 0;
+
 fail:
     vf->codec_private_size = avio_close_dyn_buf(io, &vf->codec_private);
     return err;
@@ -291,24 +303,25 @@ static int handle_file(struct VideoFiles *files, const char *file, int split)
         AVStream *st = ctx->streams[i];
         vf = av_mallocz(sizeof(*vf));
         files->files = av_realloc(files->files,
-                                  sizeof(*files->files)*(files->nb_files + 1));
+                                  sizeof(*files->files) * (files->nb_files + 1));
         files->files[files->nb_files] = vf;
 
         vf->name = file;
         if ((ptr = strrchr(file, '/')) != NULL)
             vf->name = ptr + 1;
 
-        vf->bitrate = st->codec->bit_rate;
-        vf->track_id = st->id;
+        vf->bitrate   = st->codec->bit_rate;
+        vf->track_id  = st->id;
         vf->timescale = st->time_base.den;
-        vf->duration = av_rescale_rnd(ctx->duration, vf->timescale,
-                                      AV_TIME_BASE, AV_ROUND_UP);
-        vf->is_audio = st->codec->codec_type == AVMEDIA_TYPE_AUDIO;
-        vf->is_video = st->codec->codec_type == AVMEDIA_TYPE_VIDEO;
+        vf->duration  = av_rescale_rnd(ctx->duration, vf->timescale,
+                                       AV_TIME_BASE, AV_ROUND_UP);
+        vf->is_audio  = st->codec->codec_type == AVMEDIA_TYPE_AUDIO;
+        vf->is_video  = st->codec->codec_type == AVMEDIA_TYPE_VIDEO;
 
         if (!vf->is_audio && !vf->is_video) {
-            fprintf(stderr, "Track %d in %s is neither video nor audio, "
-                            "skipping\n", vf->track_id, file);
+            fprintf(stderr,
+                    "Track %d in %s is neither video nor audio, skipping\n",
+                    vf->track_id, file);
             av_freep(&files->files[files->nb_files]);
             continue;
         }
@@ -317,15 +330,15 @@ static int handle_file(struct VideoFiles *files, const char *file, int split)
             if (files->audio_file < 0)
                 files->audio_file = files->nb_files;
             files->nb_audio_files++;
-            vf->channels = st->codec->channels;
+            vf->channels    = st->codec->channels;
             vf->sample_rate = st->codec->sample_rate;
             if (st->codec->codec_id == CODEC_ID_AAC) {
-                vf->fourcc = "AACL";
-                vf->tag = 255;
+                vf->fourcc    = "AACL";
+                vf->tag       = 255;
                 vf->blocksize = 4;
             } else if (st->codec->codec_id == CODEC_ID_WMAPRO) {
-                vf->fourcc = "WMAP";
-                vf->tag = st->codec->codec_tag;
+                vf->fourcc    = "WMAP";
+                vf->tag       = st->codec->codec_tag;
                 vf->blocksize = st->codec->block_align;
             }
             get_private_data(vf, st->codec);
@@ -334,7 +347,7 @@ static int handle_file(struct VideoFiles *files, const char *file, int split)
             if (files->video_file < 0)
                 files->video_file = files->nb_files;
             files->nb_video_files++;
-            vf->width = st->codec->width;
+            vf->width  = st->codec->width;
             vf->height = st->codec->height;
             if (st->codec->codec_id == CODEC_ID_H264)
                 vf->fourcc = "H264";
@@ -348,7 +361,7 @@ static int handle_file(struct VideoFiles *files, const char *file, int split)
 
     avformat_close_input(&ctx);
 
-    read_mfra(files, orig_files, file, split);
+    err = read_mfra(files, orig_files, file, split);
 
 fail:
     if (ctx)
@@ -379,7 +392,7 @@ static void output_server_manifest(struct VideoFiles *files,
     fprintf(out, "\t\t<switch>\n");
     for (i = 0; i < files->nb_files; i++) {
         struct VideoFile *vf = files->files[i];
-        const char *type = vf->is_video ? "video" : "audio";
+        const char *type     = vf->is_video ? "video" : "audio";
         fprintf(out, "\t\t\t<%s src=\"%s\" systemBitrate=\"%d\">\n",
                 type, vf->name, vf->bitrate);
         fprintf(out, "\t\t\t\t<param name=\"trackID\" value=\"%d\" "
@@ -410,59 +423,85 @@ static void output_client_manifest(struct VideoFiles *files,
     }
     fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
     fprintf(out, "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" "
-                 "Duration=\"%"PRId64"\">\n", files->duration * 10);
+                 "Duration=\"%"PRId64 "\">\n", files->duration * 10);
     if (files->video_file >= 0) {
         struct VideoFile *vf = files->files[files->video_file];
+        struct VideoFile *first_vf = vf;
         int index = 0;
-        fprintf(out, "\t<StreamIndex Type=\"video\" QualityLevels=\"%d\" "
-                     "Chunks=\"%d\" "
-                     "Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n",
-                     files->nb_video_files, vf->chunks);
+        fprintf(out,
+                "\t<StreamIndex Type=\"video\" QualityLevels=\"%d\" "
+                "Chunks=\"%d\" "
+                "Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n",
+                files->nb_video_files, vf->chunks);
         for (i = 0; i < files->nb_files; i++) {
             vf = files->files[i];
             if (!vf->is_video)
                 continue;
-            fprintf(out, "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
-                         "FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" "
-                         "CodecPrivateData=\"",
-                         index, vf->bitrate, vf->fourcc, vf->width, vf->height);
+            fprintf(out,
+                    "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
+                    "FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" "
+                    "CodecPrivateData=\"",
+                    index, vf->bitrate, vf->fourcc, vf->width, vf->height);
             for (j = 0; j < vf->codec_private_size; j++)
                 fprintf(out, "%02X", vf->codec_private[j]);
             fprintf(out, "\" />\n");
             index++;
+            if (vf->chunks != first_vf->chunks)
+                fprintf(stderr, "Mismatched number of video chunks in %s and %s\n",
+                        vf->name, first_vf->name);
         }
-        vf = files->files[files->video_file];
-        for (i = 0; i < vf->chunks; i++)
+        vf = first_vf;
+        for (i = 0; i < vf->chunks; i++) {
+            for (j = files->video_file + 1; j < files->nb_files; j++) {
+                if (files->files[j]->is_video &&
+                    vf->offsets[i].duration != files->files[j]->offsets[i].duration)
+                    fprintf(stderr, "Mismatched duration of video chunk %d in %s and %s\n",
+                            i, vf->name, files->files[j]->name);
+            }
             fprintf(out, "\t\t<c n=\"%d\" d=\"%d\" />\n", i,
                     vf->offsets[i].duration);
+        }
         fprintf(out, "\t</StreamIndex>\n");
     }
     if (files->audio_file >= 0) {
         struct VideoFile *vf = files->files[files->audio_file];
+        struct VideoFile *first_vf = vf;
         int index = 0;
-        fprintf(out, "\t<StreamIndex Type=\"audio\" QualityLevels=\"%d\" "
-                     "Chunks=\"%d\" "
-                     "Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n",
-                     files->nb_audio_files, vf->chunks);
+        fprintf(out,
+                "\t<StreamIndex Type=\"audio\" QualityLevels=\"%d\" "
+                "Chunks=\"%d\" "
+                "Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n",
+                files->nb_audio_files, vf->chunks);
         for (i = 0; i < files->nb_files; i++) {
             vf = files->files[i];
             if (!vf->is_audio)
                 continue;
-            fprintf(out, "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
-                         "FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" "
-                         "BitsPerSample=\"16\" PacketSize=\"%d\" "
-                         "AudioTag=\"%d\" CodecPrivateData=\"",
-                         index, vf->bitrate, vf->fourcc, vf->sample_rate,
-                         vf->channels, vf->blocksize, vf->tag);
+            fprintf(out,
+                    "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
+                    "FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" "
+                    "BitsPerSample=\"16\" PacketSize=\"%d\" "
+                    "AudioTag=\"%d\" CodecPrivateData=\"",
+                    index, vf->bitrate, vf->fourcc, vf->sample_rate,
+                    vf->channels, vf->blocksize, vf->tag);
             for (j = 0; j < vf->codec_private_size; j++)
                 fprintf(out, "%02X", vf->codec_private[j]);
             fprintf(out, "\" />\n");
             index++;
+            if (vf->chunks != first_vf->chunks)
+                fprintf(stderr, "Mismatched number of audio chunks in %s and %s\n",
+                        vf->name, first_vf->name);
         }
-        vf = files->files[files->audio_file];
-        for (i = 0; i < vf->chunks; i++)
+        vf = first_vf;
+        for (i = 0; i < vf->chunks; i++) {
+            for (j = files->audio_file + 1; j < files->nb_files; j++) {
+                if (files->files[j]->is_audio &&
+                    vf->offsets[i].duration != files->files[j]->offsets[i].duration)
+                    fprintf(stderr, "Mismatched duration of audio chunk %d in %s and %s\n",
+                            i, vf->name, files->files[j]->name);
+            }
             fprintf(out, "\t\t<c n=\"%d\" d=\"%d\" />\n",
                     i, vf->offsets[i].duration);
+        }
         fprintf(out, "\t</StreamIndex>\n");
     }
     fprintf(out, "</SmoothStreamingMedia>\n");
@@ -498,7 +537,8 @@ int main(int argc, char **argv)
         } else if (argv[i][0] == '-') {
             return usage(argv[0], 1);
         } else {
-            handle_file(&vf, argv[i], split);
+            if (handle_file(&vf, argv[i], split))
+                return 1;
         }
     }
     if (!vf.nb_files || (!basename && !split))