Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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.  * To pre-split files for serving as static files by a web server without
  29.  * any extra server support, create the ismv file as above, and split it:
  30.  * ismindex -split foo.ismv
  31.  * This step creates a file Manifest and directories QualityLevel(...),
  32.  * that can be read directly by a smooth streaming player.
  33.  */
  34.  
  35. #include <stdio.h>
  36. #include <string.h>
  37.  
  38. #include "cmdutils.h"
  39.  
  40. #include "libavformat/avformat.h"
  41. #include "libavformat/os_support.h"
  42. #include "libavutil/intreadwrite.h"
  43. #include "libavutil/mathematics.h"
  44.  
  45. static int usage(const char *argv0, int ret)
  46. {
  47.     fprintf(stderr, "%s [-split] [-n basename] file1 [file2] ...\n", argv0);
  48.     return ret;
  49. }
  50.  
  51. struct MoofOffset {
  52.     int64_t time;
  53.     int64_t offset;
  54.     int64_t duration;
  55. };
  56.  
  57. struct Track {
  58.     const char *name;
  59.     int64_t duration;
  60.     int bitrate;
  61.     int track_id;
  62.     int is_audio, is_video;
  63.     int width, height;
  64.     int chunks;
  65.     int sample_rate, channels;
  66.     uint8_t *codec_private;
  67.     int codec_private_size;
  68.     struct MoofOffset *offsets;
  69.     int timescale;
  70.     const char *fourcc;
  71.     int blocksize;
  72.     int tag;
  73. };
  74.  
  75. struct Tracks {
  76.     int nb_tracks;
  77.     int64_t duration;
  78.     struct Track **tracks;
  79.     int video_track, audio_track;
  80.     int nb_video_tracks, nb_audio_tracks;
  81. };
  82.  
  83. static int copy_tag(AVIOContext *in, AVIOContext *out, int32_t tag_name)
  84. {
  85.     int32_t size, tag;
  86.  
  87.     size = avio_rb32(in);
  88.     tag  = avio_rb32(in);
  89.     avio_wb32(out, size);
  90.     avio_wb32(out, tag);
  91.     if (tag != tag_name)
  92.         return -1;
  93.     size -= 8;
  94.     while (size > 0) {
  95.         char buf[1024];
  96.         int len = FFMIN(sizeof(buf), size);
  97.         if (avio_read(in, buf, len) != len)
  98.             break;
  99.         avio_write(out, buf, len);
  100.         size -= len;
  101.     }
  102.     return 0;
  103. }
  104.  
  105. static int write_fragment(const char *filename, AVIOContext *in)
  106. {
  107.     AVIOContext *out = NULL;
  108.     int ret;
  109.  
  110.     if ((ret = avio_open2(&out, filename, AVIO_FLAG_WRITE, NULL, NULL)) < 0)
  111.         return ret;
  112.     copy_tag(in, out, MKBETAG('m', 'o', 'o', 'f'));
  113.     copy_tag(in, out, MKBETAG('m', 'd', 'a', 't'));
  114.  
  115.     avio_flush(out);
  116.     avio_close(out);
  117.  
  118.     return ret;
  119. }
  120.  
  121. static int write_fragments(struct Tracks *tracks, int start_index,
  122.                            AVIOContext *in)
  123. {
  124.     char dirname[100], filename[500];
  125.     int i, j;
  126.  
  127.     for (i = start_index; i < tracks->nb_tracks; i++) {
  128.         struct Track *track = tracks->tracks[i];
  129.         const char *type    = track->is_video ? "video" : "audio";
  130.         snprintf(dirname, sizeof(dirname), "QualityLevels(%d)", track->bitrate);
  131.         if (mkdir(dirname, 0777) == -1)
  132.             return AVERROR(errno);
  133.         for (j = 0; j < track->chunks; j++) {
  134.             snprintf(filename, sizeof(filename), "%s/Fragments(%s=%"PRId64")",
  135.                      dirname, type, track->offsets[j].time);
  136.             avio_seek(in, track->offsets[j].offset, SEEK_SET);
  137.             write_fragment(filename, in);
  138.         }
  139.     }
  140.     return 0;
  141. }
  142.  
  143. static int read_tfra(struct Tracks *tracks, int start_index, AVIOContext *f)
  144. {
  145.     int ret = AVERROR_EOF, track_id;
  146.     int version, fieldlength, i, j;
  147.     int64_t pos   = avio_tell(f);
  148.     uint32_t size = avio_rb32(f);
  149.     struct Track *track = NULL;
  150.  
  151.     if (avio_rb32(f) != MKBETAG('t', 'f', 'r', 'a'))
  152.         goto fail;
  153.     version = avio_r8(f);
  154.     avio_rb24(f);
  155.     track_id = avio_rb32(f); /* track id */
  156.     for (i = start_index; i < tracks->nb_tracks && !track; i++)
  157.         if (tracks->tracks[i]->track_id == track_id)
  158.             track = tracks->tracks[i];
  159.     if (!track) {
  160.         /* Ok, continue parsing the next atom */
  161.         ret = 0;
  162.         goto fail;
  163.     }
  164.     fieldlength = avio_rb32(f);
  165.     track->chunks  = avio_rb32(f);
  166.     track->offsets = av_mallocz(sizeof(*track->offsets) * track->chunks);
  167.     if (!track->offsets) {
  168.         ret = AVERROR(ENOMEM);
  169.         goto fail;
  170.     }
  171.     for (i = 0; i < track->chunks; i++) {
  172.         if (version == 1) {
  173.             track->offsets[i].time   = avio_rb64(f);
  174.             track->offsets[i].offset = avio_rb64(f);
  175.         } else {
  176.             track->offsets[i].time   = avio_rb32(f);
  177.             track->offsets[i].offset = avio_rb32(f);
  178.         }
  179.         for (j = 0; j < ((fieldlength >> 4) & 3) + 1; j++)
  180.             avio_r8(f);
  181.         for (j = 0; j < ((fieldlength >> 2) & 3) + 1; j++)
  182.             avio_r8(f);
  183.         for (j = 0; j < ((fieldlength >> 0) & 3) + 1; j++)
  184.             avio_r8(f);
  185.         if (i > 0)
  186.             track->offsets[i - 1].duration = track->offsets[i].time -
  187.                                              track->offsets[i - 1].time;
  188.     }
  189.     if (track->chunks > 0)
  190.         track->offsets[track->chunks - 1].duration = track->duration -
  191.                                                      track->offsets[track->chunks - 1].time;
  192.     ret = 0;
  193.  
  194. fail:
  195.     avio_seek(f, pos + size, SEEK_SET);
  196.     return ret;
  197. }
  198.  
  199. static int read_mfra(struct Tracks *tracks, int start_index,
  200.                      const char *file, int split)
  201. {
  202.     int err = 0;
  203.     AVIOContext *f = NULL;
  204.     int32_t mfra_size;
  205.  
  206.     if ((err = avio_open2(&f, file, AVIO_FLAG_READ, NULL, NULL)) < 0)
  207.         goto fail;
  208.     avio_seek(f, avio_size(f) - 4, SEEK_SET);
  209.     mfra_size = avio_rb32(f);
  210.     avio_seek(f, -mfra_size, SEEK_CUR);
  211.     if (avio_rb32(f) != mfra_size) {
  212.         err = AVERROR_INVALIDDATA;
  213.         goto fail;
  214.     }
  215.     if (avio_rb32(f) != MKBETAG('m', 'f', 'r', 'a')) {
  216.         err = AVERROR_INVALIDDATA;
  217.         goto fail;
  218.     }
  219.     while (!read_tfra(tracks, start_index, f)) {
  220.         /* Empty */
  221.     }
  222.  
  223.     if (split)
  224.         err = write_fragments(tracks, start_index, f);
  225.  
  226. fail:
  227.     if (f)
  228.         avio_close(f);
  229.     if (err)
  230.         fprintf(stderr, "Unable to read the MFRA atom in %s\n", file);
  231.     return err;
  232. }
  233.  
  234. static int get_private_data(struct Track *track, AVCodecContext *codec)
  235. {
  236.     track->codec_private_size = codec->extradata_size;
  237.     track->codec_private      = av_mallocz(codec->extradata_size);
  238.     if (!track->codec_private)
  239.         return AVERROR(ENOMEM);
  240.     memcpy(track->codec_private, codec->extradata, codec->extradata_size);
  241.     return 0;
  242. }
  243.  
  244. static int get_video_private_data(struct Track *track, AVCodecContext *codec)
  245. {
  246.     AVIOContext *io = NULL;
  247.     uint16_t sps_size, pps_size;
  248.     int err = AVERROR(EINVAL);
  249.  
  250.     if (codec->codec_id == AV_CODEC_ID_VC1)
  251.         return get_private_data(track, codec);
  252.  
  253.     if (avio_open_dyn_buf(&io) < 0)  {
  254.         err = AVERROR(ENOMEM);
  255.         goto fail;
  256.     }
  257.     if (codec->extradata_size < 11 || codec->extradata[0] != 1)
  258.         goto fail;
  259.     sps_size = AV_RB16(&codec->extradata[6]);
  260.     if (11 + sps_size > codec->extradata_size)
  261.         goto fail;
  262.     avio_wb32(io, 0x00000001);
  263.     avio_write(io, &codec->extradata[8], sps_size);
  264.     pps_size = AV_RB16(&codec->extradata[9 + sps_size]);
  265.     if (11 + sps_size + pps_size > codec->extradata_size)
  266.         goto fail;
  267.     avio_wb32(io, 0x00000001);
  268.     avio_write(io, &codec->extradata[11 + sps_size], pps_size);
  269.     err = 0;
  270.  
  271. fail:
  272.     track->codec_private_size = avio_close_dyn_buf(io, &track->codec_private);
  273.     return err;
  274. }
  275.  
  276. static int handle_file(struct Tracks *tracks, const char *file, int split)
  277. {
  278.     AVFormatContext *ctx = NULL;
  279.     int err = 0, i, orig_tracks = tracks->nb_tracks;
  280.     char errbuf[50], *ptr;
  281.     struct Track *track;
  282.  
  283.     err = avformat_open_input(&ctx, file, NULL, NULL);
  284.     if (err < 0) {
  285.         av_strerror(err, errbuf, sizeof(errbuf));
  286.         fprintf(stderr, "Unable to open %s: %s\n", file, errbuf);
  287.         return 1;
  288.     }
  289.  
  290.     err = avformat_find_stream_info(ctx, NULL);
  291.     if (err < 0) {
  292.         av_strerror(err, errbuf, sizeof(errbuf));
  293.         fprintf(stderr, "Unable to identify %s: %s\n", file, errbuf);
  294.         goto fail;
  295.     }
  296.  
  297.     if (ctx->nb_streams < 1) {
  298.         fprintf(stderr, "No streams found in %s\n", file);
  299.         goto fail;
  300.     }
  301.  
  302.     for (i = 0; i < ctx->nb_streams; i++) {
  303.         struct Track **temp;
  304.         AVStream *st = ctx->streams[i];
  305.         track = av_mallocz(sizeof(*track));
  306.         if (!track) {
  307.             err = AVERROR(ENOMEM);
  308.             goto fail;
  309.         }
  310.         temp = av_realloc(tracks->tracks,
  311.                           sizeof(*tracks->tracks) * (tracks->nb_tracks + 1));
  312.         if (!temp) {
  313.             av_free(track);
  314.             err = AVERROR(ENOMEM);
  315.             goto fail;
  316.         }
  317.         tracks->tracks = temp;
  318.         tracks->tracks[tracks->nb_tracks] = track;
  319.  
  320.         track->name = file;
  321.         if ((ptr = strrchr(file, '/')) != NULL)
  322.             track->name = ptr + 1;
  323.  
  324.         track->bitrate   = st->codec->bit_rate;
  325.         track->track_id  = st->id;
  326.         track->timescale = st->time_base.den;
  327.         track->duration  = st->duration;
  328.         track->is_audio  = st->codec->codec_type == AVMEDIA_TYPE_AUDIO;
  329.         track->is_video  = st->codec->codec_type == AVMEDIA_TYPE_VIDEO;
  330.  
  331.         if (!track->is_audio && !track->is_video) {
  332.             fprintf(stderr,
  333.                     "Track %d in %s is neither video nor audio, skipping\n",
  334.                     track->track_id, file);
  335.             av_freep(&tracks->tracks[tracks->nb_tracks]);
  336.             continue;
  337.         }
  338.  
  339.         tracks->duration = FFMAX(tracks->duration,
  340.                                  av_rescale_rnd(track->duration, AV_TIME_BASE,
  341.                                                 track->timescale, AV_ROUND_UP));
  342.  
  343.         if (track->is_audio) {
  344.             if (tracks->audio_track < 0)
  345.                 tracks->audio_track = tracks->nb_tracks;
  346.             tracks->nb_audio_tracks++;
  347.             track->channels    = st->codec->channels;
  348.             track->sample_rate = st->codec->sample_rate;
  349.             if (st->codec->codec_id == AV_CODEC_ID_AAC) {
  350.                 track->fourcc    = "AACL";
  351.                 track->tag       = 255;
  352.                 track->blocksize = 4;
  353.             } else if (st->codec->codec_id == AV_CODEC_ID_WMAPRO) {
  354.                 track->fourcc    = "WMAP";
  355.                 track->tag       = st->codec->codec_tag;
  356.                 track->blocksize = st->codec->block_align;
  357.             }
  358.             get_private_data(track, st->codec);
  359.         }
  360.         if (track->is_video) {
  361.             if (tracks->video_track < 0)
  362.                 tracks->video_track = tracks->nb_tracks;
  363.             tracks->nb_video_tracks++;
  364.             track->width  = st->codec->width;
  365.             track->height = st->codec->height;
  366.             if (st->codec->codec_id == AV_CODEC_ID_H264)
  367.                 track->fourcc = "H264";
  368.             else if (st->codec->codec_id == AV_CODEC_ID_VC1)
  369.                 track->fourcc = "WVC1";
  370.             get_video_private_data(track, st->codec);
  371.         }
  372.  
  373.         tracks->nb_tracks++;
  374.     }
  375.  
  376.     avformat_close_input(&ctx);
  377.  
  378.     err = read_mfra(tracks, orig_tracks, file, split);
  379.  
  380. fail:
  381.     if (ctx)
  382.         avformat_close_input(&ctx);
  383.     return err;
  384. }
  385.  
  386. static void output_server_manifest(struct Tracks *tracks,
  387.                                    const char *basename)
  388. {
  389.     char filename[1000];
  390.     FILE *out;
  391.     int i;
  392.  
  393.     snprintf(filename, sizeof(filename), "%s.ism", basename);
  394.     out = fopen(filename, "w");
  395.     if (!out) {
  396.         perror(filename);
  397.         return;
  398.     }
  399.     fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
  400.     fprintf(out, "<smil xmlns=\"http://www.w3.org/2001/SMIL20/Language\">\n");
  401.     fprintf(out, "\t<head>\n");
  402.     fprintf(out, "\t\t<meta name=\"clientManifestRelativePath\" "
  403.                  "content=\"%s.ismc\" />\n", basename);
  404.     fprintf(out, "\t</head>\n");
  405.     fprintf(out, "\t<body>\n");
  406.     fprintf(out, "\t\t<switch>\n");
  407.     for (i = 0; i < tracks->nb_tracks; i++) {
  408.         struct Track *track = tracks->tracks[i];
  409.         const char *type    = track->is_video ? "video" : "audio";
  410.         fprintf(out, "\t\t\t<%s src=\"%s\" systemBitrate=\"%d\">\n",
  411.                 type, track->name, track->bitrate);
  412.         fprintf(out, "\t\t\t\t<param name=\"trackID\" value=\"%d\" "
  413.                      "valueType=\"data\" />\n", track->track_id);
  414.         fprintf(out, "\t\t\t</%s>\n", type);
  415.     }
  416.     fprintf(out, "\t\t</switch>\n");
  417.     fprintf(out, "\t</body>\n");
  418.     fprintf(out, "</smil>\n");
  419.     fclose(out);
  420. }
  421.  
  422. static void print_track_chunks(FILE *out, struct Tracks *tracks, int main,
  423.                                const char *type)
  424. {
  425.     int i, j;
  426.     struct Track *track = tracks->tracks[main];
  427.     for (i = 0; i < track->chunks; i++) {
  428.         for (j = main + 1; j < tracks->nb_tracks; j++) {
  429.             if (tracks->tracks[j]->is_audio == track->is_audio &&
  430.                 track->offsets[i].duration != tracks->tracks[j]->offsets[i].duration)
  431.                 fprintf(stderr, "Mismatched duration of %s chunk %d in %s and %s\n",
  432.                         type, i, track->name, tracks->tracks[j]->name);
  433.         }
  434.         fprintf(out, "\t\t<c n=\"%d\" d=\"%"PRId64"\" />\n",
  435.                 i, track->offsets[i].duration);
  436.     }
  437. }
  438.  
  439. static void output_client_manifest(struct Tracks *tracks,
  440.                                    const char *basename, int split)
  441. {
  442.     char filename[1000];
  443.     FILE *out;
  444.     int i, j;
  445.  
  446.     if (split)
  447.         snprintf(filename, sizeof(filename), "Manifest");
  448.     else
  449.         snprintf(filename, sizeof(filename), "%s.ismc", basename);
  450.     out = fopen(filename, "w");
  451.     if (!out) {
  452.         perror(filename);
  453.         return;
  454.     }
  455.     fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
  456.     fprintf(out, "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" "
  457.                  "Duration=\"%"PRId64 "\">\n", tracks->duration * 10);
  458.     if (tracks->video_track >= 0) {
  459.         struct Track *track = tracks->tracks[tracks->video_track];
  460.         struct Track *first_track = track;
  461.         int index = 0;
  462.         fprintf(out,
  463.                 "\t<StreamIndex Type=\"video\" QualityLevels=\"%d\" "
  464.                 "Chunks=\"%d\" "
  465.                 "Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n",
  466.                 tracks->nb_video_tracks, track->chunks);
  467.         for (i = 0; i < tracks->nb_tracks; i++) {
  468.             track = tracks->tracks[i];
  469.             if (!track->is_video)
  470.                 continue;
  471.             fprintf(out,
  472.                     "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
  473.                     "FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" "
  474.                     "CodecPrivateData=\"",
  475.                     index, track->bitrate, track->fourcc, track->width, track->height);
  476.             for (j = 0; j < track->codec_private_size; j++)
  477.                 fprintf(out, "%02X", track->codec_private[j]);
  478.             fprintf(out, "\" />\n");
  479.             index++;
  480.             if (track->chunks != first_track->chunks)
  481.                 fprintf(stderr, "Mismatched number of video chunks in %s and %s\n",
  482.                         track->name, first_track->name);
  483.         }
  484.         print_track_chunks(out, tracks, tracks->video_track, "video");
  485.         fprintf(out, "\t</StreamIndex>\n");
  486.     }
  487.     if (tracks->audio_track >= 0) {
  488.         struct Track *track = tracks->tracks[tracks->audio_track];
  489.         struct Track *first_track = track;
  490.         int index = 0;
  491.         fprintf(out,
  492.                 "\t<StreamIndex Type=\"audio\" QualityLevels=\"%d\" "
  493.                 "Chunks=\"%d\" "
  494.                 "Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n",
  495.                 tracks->nb_audio_tracks, track->chunks);
  496.         for (i = 0; i < tracks->nb_tracks; i++) {
  497.             track = tracks->tracks[i];
  498.             if (!track->is_audio)
  499.                 continue;
  500.             fprintf(out,
  501.                     "\t\t<QualityLevel Index=\"%d\" Bitrate=\"%d\" "
  502.                     "FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" "
  503.                     "BitsPerSample=\"16\" PacketSize=\"%d\" "
  504.                     "AudioTag=\"%d\" CodecPrivateData=\"",
  505.                     index, track->bitrate, track->fourcc, track->sample_rate,
  506.                     track->channels, track->blocksize, track->tag);
  507.             for (j = 0; j < track->codec_private_size; j++)
  508.                 fprintf(out, "%02X", track->codec_private[j]);
  509.             fprintf(out, "\" />\n");
  510.             index++;
  511.             if (track->chunks != first_track->chunks)
  512.                 fprintf(stderr, "Mismatched number of audio chunks in %s and %s\n",
  513.                         track->name, first_track->name);
  514.         }
  515.         print_track_chunks(out, tracks, tracks->audio_track, "audio");
  516.         fprintf(out, "\t</StreamIndex>\n");
  517.     }
  518.     fprintf(out, "</SmoothStreamingMedia>\n");
  519.     fclose(out);
  520. }
  521.  
  522. static void clean_tracks(struct Tracks *tracks)
  523. {
  524.     int i;
  525.     for (i = 0; i < tracks->nb_tracks; i++) {
  526.         av_freep(&tracks->tracks[i]->codec_private);
  527.         av_freep(&tracks->tracks[i]->offsets);
  528.         av_freep(&tracks->tracks[i]);
  529.     }
  530.     av_freep(&tracks->tracks);
  531.     tracks->nb_tracks = 0;
  532. }
  533.  
  534. int main(int argc, char **argv)
  535. {
  536.     const char *basename = NULL;
  537.     int split = 0, i;
  538.     struct Tracks tracks = { 0, .video_track = -1, .audio_track = -1 };
  539.  
  540.     av_register_all();
  541.  
  542.     for (i = 1; i < argc; i++) {
  543.         if (!strcmp(argv[i], "-n")) {
  544.             basename = argv[i + 1];
  545.             i++;
  546.         } else if (!strcmp(argv[i], "-split")) {
  547.             split = 1;
  548.         } else if (argv[i][0] == '-') {
  549.             return usage(argv[0], 1);
  550.         } else {
  551.             if (handle_file(&tracks, argv[i], split))
  552.                 return 1;
  553.         }
  554.     }
  555.     if (!tracks.nb_tracks || (!basename && !split))
  556.         return usage(argv[0], 1);
  557.  
  558.     if (!split)
  559.         output_server_manifest(&tracks, basename);
  560.     output_client_manifest(&tracks, basename, split);
  561.  
  562.     clean_tracks(&tracks);
  563.  
  564.     return 0;
  565. }
  566.