00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 #include <stdio.h>
00036 #include <string.h>
00037 #include <sys/stat.h>
00038 #ifdef _WIN32
00039 #include <direct.h>
00040 #define mkdir(a, b) _mkdir(a)
00041 #endif
00042 
00043 #include "libavformat/avformat.h"
00044 #include "libavutil/intreadwrite.h"
00045 #include "libavutil/mathematics.h"
00046 
00047 static int usage(const char *argv0, int ret)
00048 {
00049     fprintf(stderr, "%s [-split] [-n basename] file1 [file2] ...\n", argv0);
00050     return ret;
00051 }
00052 
00053 struct MoofOffset {
00054     int64_t time;
00055     int64_t offset;
00056     int duration;
00057 };
00058 
00059 struct VideoFile {
00060     const char *name;
00061     int64_t duration;
00062     int bitrate;
00063     int track_id;
00064     int is_audio, is_video;
00065     int width, height;
00066     int chunks;
00067     int sample_rate, channels;
00068     uint8_t *codec_private;
00069     int codec_private_size;
00070     struct MoofOffset *offsets;
00071     int timescale;
00072     const char *fourcc;
00073     int blocksize;
00074     int tag;
00075 };
00076 
00077 struct VideoFiles {
00078     int nb_files;
00079     int64_t duration;
00080     struct VideoFile **files;
00081     int video_file, audio_file;
00082     int nb_video_files, nb_audio_files;
00083 };
00084 
00085 static int copy_tag(AVIOContext *in, AVIOContext *out, int32_t tag_name)
00086 {
00087     int32_t size, tag;
00088 
00089     size = avio_rb32(in);
00090     tag  = avio_rb32(in);
00091     avio_wb32(out, size);
00092     avio_wb32(out, tag);
00093     if (tag != tag_name)
00094         return -1;
00095     size -= 8;
00096     while (size > 0) {
00097         char buf[1024];
00098         int len = FFMIN(sizeof(buf), size);
00099         if (avio_read(in, buf, len) != len)
00100             break;
00101         avio_write(out, buf, len);
00102         size -= len;
00103     }
00104     return 0;
00105 }
00106 
00107 static int write_fragment(const char *filename, AVIOContext *in)
00108 {
00109     AVIOContext *out = NULL;
00110     int ret;
00111 
00112     if ((ret = avio_open2(&out, filename, AVIO_FLAG_WRITE, NULL, NULL)) < 0)
00113         return ret;
00114     copy_tag(in, out, MKBETAG('m', 'o', 'o', 'f'));
00115     copy_tag(in, out, MKBETAG('m', 'd', 'a', 't'));
00116 
00117     avio_flush(out);
00118     avio_close(out);
00119 
00120     return ret;
00121 }
00122 
00123 static int write_fragments(struct VideoFiles *files, int start_index,
00124                            AVIOContext *in)
00125 {
00126     char dirname[100], filename[500];
00127     int i, j;
00128 
00129     for (i = start_index; i < files->nb_files; i++) {
00130         struct VideoFile *vf = files->files[i];
00131         const char *type     = vf->is_video ? "video" : "audio";
00132         snprintf(dirname, sizeof(dirname), "QualityLevels(%d)", vf->bitrate);
00133         mkdir(dirname, 0777);
00134         for (j = 0; j < vf->chunks; j++) {
00135             snprintf(filename, sizeof(filename), "%s/Fragments(%s=%"PRId64")",
00136                      dirname, type, vf->offsets[j].time);
00137             avio_seek(in, vf->offsets[j].offset, SEEK_SET);
00138             write_fragment(filename, in);
00139         }
00140     }
00141     return 0;
00142 }
00143 
00144 static int read_tfra(struct VideoFiles *files, int start_index, AVIOContext *f)
00145 {
00146     int ret = AVERROR_EOF, track_id;
00147     int version, fieldlength, i, j;
00148     int64_t pos   = avio_tell(f);
00149     uint32_t size = avio_rb32(f);
00150     struct VideoFile *vf = NULL;
00151 
00152     if (avio_rb32(f) != MKBETAG('t', 'f', 'r', 'a'))
00153         goto fail;
00154     version = avio_r8(f);
00155     avio_rb24(f);
00156     track_id = avio_rb32(f); 
00157     for (i = start_index; i < files->nb_files && !vf; i++)
00158         if (files->files[i]->track_id == track_id)
00159             vf = files->files[i];
00160     if (!vf) {
00161         
00162         ret = 0;
00163         goto fail;
00164     }
00165     fieldlength = avio_rb32(f);
00166     vf->chunks  = avio_rb32(f);
00167     vf->offsets = av_mallocz(sizeof(*vf->offsets) * vf->chunks);
00168     if (!vf->offsets) {
00169         ret = AVERROR(ENOMEM);
00170         goto fail;
00171     }
00172     for (i = 0; i < vf->chunks; i++) {
00173         if (version == 1) {
00174             vf->offsets[i].time   = avio_rb64(f);
00175             vf->offsets[i].offset = avio_rb64(f);
00176         } else {
00177             vf->offsets[i].time   = avio_rb32(f);
00178             vf->offsets[i].offset = avio_rb32(f);
00179         }
00180         for (j = 0; j < ((fieldlength >> 4) & 3) + 1; j++)
00181             avio_r8(f);
00182         for (j = 0; j < ((fieldlength >> 2) & 3) + 1; j++)
00183             avio_r8(f);
00184         for (j = 0; j < ((fieldlength >> 0) & 3) + 1; j++)
00185             avio_r8(f);
00186         if (i > 0)
00187             vf->offsets[i - 1].duration = vf->offsets[i].time -
00188                                           vf->offsets[i - 1].time;
00189     }
00190     if (vf->chunks > 0)
00191         vf->offsets[vf->chunks - 1].duration = vf->duration -
00192                                                vf->offsets[vf->chunks - 1].time;
00193     ret = 0;
00194 
00195 fail:
00196     avio_seek(f, pos + size, SEEK_SET);
00197     return ret;
00198 }
00199 
00200 static int read_mfra(struct VideoFiles *files, int start_index,
00201                      const char *file, int split)
00202 {
00203     int err = 0;
00204     AVIOContext *f = NULL;
00205     int32_t mfra_size;
00206 
00207     if ((err = avio_open2(&f, file, AVIO_FLAG_READ, NULL, NULL)) < 0)
00208         goto fail;
00209     avio_seek(f, avio_size(f) - 4, SEEK_SET);
00210     mfra_size = avio_rb32(f);
00211     avio_seek(f, -mfra_size, SEEK_CUR);
00212     if (avio_rb32(f) != mfra_size) {
00213         err = AVERROR_INVALIDDATA;
00214         goto fail;
00215     }
00216     if (avio_rb32(f) != MKBETAG('m', 'f', 'r', 'a')) {
00217         err = AVERROR_INVALIDDATA;
00218         goto fail;
00219     }
00220     while (!read_tfra(files, start_index, f)) {
00221         
00222     }
00223 
00224     if (split)
00225         write_fragments(files, start_index, f);
00226 
00227 fail:
00228     if (f)
00229         avio_close(f);
00230     if (err)
00231         fprintf(stderr, "Unable to read the MFRA atom in %s\n", file);
00232     return err;
00233 }
00234 
00235 static int get_private_data(struct VideoFile *vf, AVCodecContext *codec)
00236 {
00237     vf->codec_private_size = codec->extradata_size;
00238     vf->codec_private      = av_mallocz(codec->extradata_size);
00239     if (!vf->codec_private)
00240         return AVERROR(ENOMEM);
00241     memcpy(vf->codec_private, codec->extradata, codec->extradata_size);
00242     return 0;
00243 }
00244 
00245 static int get_video_private_data(struct VideoFile *vf, AVCodecContext *codec)
00246 {
00247     AVIOContext *io = NULL;
00248     uint16_t sps_size, pps_size;
00249     int err = AVERROR(EINVAL);
00250 
00251     if (codec->codec_id == AV_CODEC_ID_VC1)
00252         return get_private_data(vf, codec);
00253 
00254     avio_open_dyn_buf(&io);
00255     if (codec->extradata_size < 11 || codec->extradata[0] != 1)
00256         goto fail;
00257     sps_size = AV_RB16(&codec->extradata[6]);
00258     if (11 + sps_size > codec->extradata_size)
00259         goto fail;
00260     avio_wb32(io, 0x00000001);
00261     avio_write(io, &codec->extradata[8], sps_size);
00262     pps_size = AV_RB16(&codec->extradata[9 + sps_size]);
00263     if (11 + sps_size + pps_size > codec->extradata_size)
00264         goto fail;
00265     avio_wb32(io, 0x00000001);
00266     avio_write(io, &codec->extradata[11 + sps_size], pps_size);
00267     err = 0;
00268 
00269 fail:
00270     vf->codec_private_size = avio_close_dyn_buf(io, &vf->codec_private);
00271     return err;
00272 }
00273 
00274 static int handle_file(struct VideoFiles *files, const char *file, int split)
00275 {
00276     AVFormatContext *ctx = NULL;
00277     int err = 0, i, orig_files = files->nb_files;
00278     char errbuf[50], *ptr;
00279     struct VideoFile *vf;
00280 
00281     err = avformat_open_input(&ctx, file, NULL, NULL);
00282     if (err < 0) {
00283         av_strerror(err, errbuf, sizeof(errbuf));
00284         fprintf(stderr, "Unable to open %s: %s\n", file, errbuf);
00285         return 1;
00286     }
00287 
00288     err = avformat_find_stream_info(ctx, NULL);
00289     if (err < 0) {
00290         av_strerror(err, errbuf, sizeof(errbuf));
00291         fprintf(stderr, "Unable to identify %s: %s\n", file, errbuf);
00292         goto fail;
00293     }
00294 
00295     if (ctx->nb_streams < 1) {
00296         fprintf(stderr, "No streams found in %s\n", file);
00297         goto fail;
00298     }
00299     if (!files->duration)
00300         files->duration = ctx->duration;
00301 
00302     for (i = 0; i < ctx->nb_streams; i++) {
00303         AVStream *st = ctx->streams[i];
00304         vf = av_mallocz(sizeof(*vf));
00305         files->files = av_realloc(files->files,
00306                                   sizeof(*files->files) * (files->nb_files + 1));
00307         files->files[files->nb_files] = vf;
00308 
00309         vf->name = file;
00310         if ((ptr = strrchr(file, '/')) != NULL)
00311             vf->name = ptr + 1;
00312 
00313         vf->bitrate   = st->codec->bit_rate;
00314         vf->track_id  = st->id;
00315         vf->timescale = st->time_base.den;
00316         vf->duration  = av_rescale_rnd(ctx->duration, vf->timescale,
00317                                        AV_TIME_BASE, AV_ROUND_UP);
00318         vf->is_audio  = st->codec->codec_type == AVMEDIA_TYPE_AUDIO;
00319         vf->is_video  = st->codec->codec_type == AVMEDIA_TYPE_VIDEO;
00320 
00321         if (!vf->is_audio && !vf->is_video) {
00322             fprintf(stderr,
00323                     "Track %d in %s is neither video nor audio, skipping\n",
00324                     vf->track_id, file);
00325             av_freep(&files->files[files->nb_files]);
00326             continue;
00327         }
00328 
00329         if (vf->is_audio) {
00330             if (files->audio_file < 0)
00331                 files->audio_file = files->nb_files;
00332             files->nb_audio_files++;
00333             vf->channels    = st->codec->channels;
00334             vf->sample_rate = st->codec->sample_rate;
00335             if (st->codec->codec_id == AV_CODEC_ID_AAC) {
00336                 vf->fourcc    = "AACL";
00337                 vf->tag       = 255;
00338                 vf->blocksize = 4;
00339             } else if (st->codec->codec_id == AV_CODEC_ID_WMAPRO) {
00340                 vf->fourcc    = "WMAP";
00341                 vf->tag       = st->codec->codec_tag;
00342                 vf->blocksize = st->codec->block_align;
00343             }
00344             get_private_data(vf, st->codec);
00345         }
00346         if (vf->is_video) {
00347             if (files->video_file < 0)
00348                 files->video_file = files->nb_files;
00349             files->nb_video_files++;
00350             vf->width  = st->codec->width;
00351             vf->height = st->codec->height;
00352             if (st->codec->codec_id == AV_CODEC_ID_H264)
00353                 vf->fourcc = "H264";
00354             else if (st->codec->codec_id == AV_CODEC_ID_VC1)
00355                 vf->fourcc = "WVC1";
00356             get_video_private_data(vf, st->codec);
00357         }
00358 
00359         files->nb_files++;
00360     }
00361 
00362     avformat_close_input(&ctx);
00363 
00364     err = read_mfra(files, orig_files, file, split);
00365 
00366 fail:
00367     if (ctx)
00368         avformat_close_input(&ctx);
00369     return err;
00370 }
00371 
00372 static void output_server_manifest(struct VideoFiles *files,
00373                                    const char *basename)
00374 {
00375     char filename[1000];
00376     FILE *out;
00377     int i;
00378 
00379     snprintf(filename, sizeof(filename), "%s.ism", basename);
00380     out = fopen(filename, "w");
00381     if (!out) {
00382         perror(filename);
00383         return;
00384     }
00385     fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
00386     fprintf(out, "<smil xmlns=\"http://www.w3.org/2001/SMIL20/Language\">\n");
00387     fprintf(out, "\t<head>\n");
00388     fprintf(out, "\t\t<meta name=\"clientManifestRelativePath\" "
00389                  "content=\"%s.ismc\" />\n", basename);
00390     fprintf(out, "\t</head>\n");
00391     fprintf(out, "\t<body>\n");
00392     fprintf(out, "\t\t<switch>\n");
00393     for (i = 0; i < files->nb_files; i++) {
00394         struct VideoFile *vf = files->files[i];
00395         const char *type     = vf->is_video ? "video" : "audio";
00396         fprintf(out, "\t\t\t<%s src=\"%s\" systemBitrate=\"%d\">\n",
00397                 type, vf->name, vf->bitrate);
00398         fprintf(out, "\t\t\t\t<param name=\"trackID\" value=\"%d\" "
00399                      "valueType=\"data\" />\n", vf->track_id);
00400         fprintf(out, "\t\t\t</%s>\n", type);
00401     }
00402     fprintf(out, "\t\t</switch>\n");
00403     fprintf(out, "\t</body>\n");
00404     fprintf(out, "</smil>\n");
00405     fclose(out);
00406 }
00407 
00408 static void output_client_manifest(struct VideoFiles *files,
00409                                    const char *basename, int split)
00410 {
00411     char filename[1000];
00412     FILE *out;
00413     int i, j;
00414 
00415     if (split)
00416         snprintf(filename, sizeof(filename), "Manifest");
00417     else
00418         snprintf(filename, sizeof(filename), "%s.ismc", basename);
00419     out = fopen(filename, "w");
00420     if (!out) {
00421         perror(filename);
00422         return;
00423     }
00424     fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
00425     fprintf(out, "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" "
00426                  "Duration=\"%"PRId64 "\">\n", files->duration * 10);
00427     if (files->video_file >= 0) {
00428         struct VideoFile *vf = files->files[files->video_file];
00429         struct VideoFile *first_vf = vf;
00430         int index = 0;
00431         fprintf(out,
00432                 "\t<StreamIndex Type=\"video\" QualityLevels=\"%d\" "
00433                 "Chunks=\"%d\" "
00434                 "Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n",
00435                 files->nb_video_files, vf->chunks);
00436         for (i = 0; i < files->nb_files; i++) {
00437             vf = files->files[i];
00438             if (!vf->is_video)
00439                 continue;
00440             fprintf(out,
00441                     "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
00442                     "FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" "
00443                     "CodecPrivateData=\"",
00444                     index, vf->bitrate, vf->fourcc, vf->width, vf->height);
00445             for (j = 0; j < vf->codec_private_size; j++)
00446                 fprintf(out, "%02X", vf->codec_private[j]);
00447             fprintf(out, "\" />\n");
00448             index++;
00449             if (vf->chunks != first_vf->chunks)
00450                 fprintf(stderr, "Mismatched number of video chunks in %s and %s\n",
00451                         vf->name, first_vf->name);
00452         }
00453         vf = first_vf;
00454         for (i = 0; i < vf->chunks; i++) {
00455             for (j = files->video_file + 1; j < files->nb_files; j++) {
00456                 if (files->files[j]->is_video &&
00457                     vf->offsets[i].duration != files->files[j]->offsets[i].duration)
00458                     fprintf(stderr, "Mismatched duration of video chunk %d in %s and %s\n",
00459                             i, vf->name, files->files[j]->name);
00460             }
00461             fprintf(out, "\t\t<c n=\"%d\" d=\"%d\" />\n", i,
00462                     vf->offsets[i].duration);
00463         }
00464         fprintf(out, "\t</StreamIndex>\n");
00465     }
00466     if (files->audio_file >= 0) {
00467         struct VideoFile *vf = files->files[files->audio_file];
00468         struct VideoFile *first_vf = vf;
00469         int index = 0;
00470         fprintf(out,
00471                 "\t<StreamIndex Type=\"audio\" QualityLevels=\"%d\" "
00472                 "Chunks=\"%d\" "
00473                 "Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n",
00474                 files->nb_audio_files, vf->chunks);
00475         for (i = 0; i < files->nb_files; i++) {
00476             vf = files->files[i];
00477             if (!vf->is_audio)
00478                 continue;
00479             fprintf(out,
00480                     "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
00481                     "FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" "
00482                     "BitsPerSample=\"16\" PacketSize=\"%d\" "
00483                     "AudioTag=\"%d\" CodecPrivateData=\"",
00484                     index, vf->bitrate, vf->fourcc, vf->sample_rate,
00485                     vf->channels, vf->blocksize, vf->tag);
00486             for (j = 0; j < vf->codec_private_size; j++)
00487                 fprintf(out, "%02X", vf->codec_private[j]);
00488             fprintf(out, "\" />\n");
00489             index++;
00490             if (vf->chunks != first_vf->chunks)
00491                 fprintf(stderr, "Mismatched number of audio chunks in %s and %s\n",
00492                         vf->name, first_vf->name);
00493         }
00494         vf = first_vf;
00495         for (i = 0; i < vf->chunks; i++) {
00496             for (j = files->audio_file + 1; j < files->nb_files; j++) {
00497                 if (files->files[j]->is_audio &&
00498                     vf->offsets[i].duration != files->files[j]->offsets[i].duration)
00499                     fprintf(stderr, "Mismatched duration of audio chunk %d in %s and %s\n",
00500                             i, vf->name, files->files[j]->name);
00501             }
00502             fprintf(out, "\t\t<c n=\"%d\" d=\"%d\" />\n",
00503                     i, vf->offsets[i].duration);
00504         }
00505         fprintf(out, "\t</StreamIndex>\n");
00506     }
00507     fprintf(out, "</SmoothStreamingMedia>\n");
00508     fclose(out);
00509 }
00510 
00511 static void clean_files(struct VideoFiles *files)
00512 {
00513     int i;
00514     for (i = 0; i < files->nb_files; i++) {
00515         av_freep(&files->files[i]->codec_private);
00516         av_freep(&files->files[i]->offsets);
00517         av_freep(&files->files[i]);
00518     }
00519     av_freep(&files->files);
00520     files->nb_files = 0;
00521 }
00522 
00523 int main(int argc, char **argv)
00524 {
00525     const char *basename = NULL;
00526     int split = 0, i;
00527     struct VideoFiles vf = { 0, .video_file = -1, .audio_file = -1 };
00528 
00529     av_register_all();
00530 
00531     for (i = 1; i < argc; i++) {
00532         if (!strcmp(argv[i], "-n")) {
00533             basename = argv[i + 1];
00534             i++;
00535         } else if (!strcmp(argv[i], "-split")) {
00536             split = 1;
00537         } else if (argv[i][0] == '-') {
00538             return usage(argv[0], 1);
00539         } else {
00540             if (handle_file(&vf, argv[i], split))
00541                 return 1;
00542         }
00543     }
00544     if (!vf.nb_files || (!basename && !split))
00545         return usage(argv[0], 1);
00546 
00547     if (!split)
00548         output_server_manifest(&vf, basename);
00549     output_client_manifest(&vf, basename, split);
00550 
00551     clean_files(&vf);
00552 
00553     return 0;
00554 }