Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright (c) 2014 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. #include <stdio.h>
  22. #include <string.h>
  23.  
  24. #include "libavformat/avformat.h"
  25. #include "libavutil/avstring.h"
  26. #include "libavutil/intreadwrite.h"
  27. #include "libavutil/mathematics.h"
  28.  
  29. static int usage(const char *argv0, int ret)
  30. {
  31.     fprintf(stderr, "%s -out foo.mpd file1\n", argv0);
  32.     return ret;
  33. }
  34.  
  35. struct Track {
  36.     const char *name;
  37.     int64_t duration;
  38.     int bitrate;
  39.     int track_id;
  40.     int is_audio, is_video;
  41.     int width, height;
  42.     int sample_rate, channels;
  43.     int timescale;
  44.     char codec_str[30];
  45.     int64_t sidx_start, sidx_length;
  46. };
  47.  
  48. struct Tracks {
  49.     int nb_tracks;
  50.     int64_t duration;
  51.     struct Track **tracks;
  52.     int multiple_tracks_per_file;
  53. };
  54.  
  55. static void set_codec_str(AVCodecContext *codec, char *str, int size)
  56. {
  57.     switch (codec->codec_id) {
  58.     case AV_CODEC_ID_H264:
  59.         snprintf(str, size, "avc1");
  60.         if (codec->extradata_size >= 4 && codec->extradata[0] == 1) {
  61.             av_strlcatf(str, size, ".%02x%02x%02x",
  62.                         codec->extradata[1], codec->extradata[2], codec->extradata[3]);
  63.         }
  64.         break;
  65.     case AV_CODEC_ID_AAC:
  66.         snprintf(str, size, "mp4a.40"); // 0x40 is the mp4 object type for AAC
  67.         if (codec->extradata_size >= 2) {
  68.             int aot = codec->extradata[0] >> 3;
  69.             if (aot == 31)
  70.                 aot = ((AV_RB16(codec->extradata) >> 5) & 0x3f) + 32;
  71.             av_strlcatf(str, size, ".%d", aot);
  72.         }
  73.         break;
  74.     }
  75. }
  76.  
  77. static int find_sidx(struct Tracks *tracks, int start_index,
  78.                      const char *file)
  79. {
  80.     int err = 0;
  81.     AVIOContext *f = NULL;
  82.     int i;
  83.  
  84.     if ((err = avio_open2(&f, file, AVIO_FLAG_READ, NULL, NULL)) < 0)
  85.         goto fail;
  86.  
  87.     while (!f->eof_reached) {
  88.         int64_t pos = avio_tell(f);
  89.         int32_t size, tag;
  90.  
  91.         size = avio_rb32(f);
  92.         tag  = avio_rb32(f);
  93.         if (size < 8)
  94.             break;
  95.         if (tag == MKBETAG('s', 'i', 'd', 'x')) {
  96.             for (i = start_index; i < tracks->nb_tracks; i++) {
  97.                 struct Track *track = tracks->tracks[i];
  98.                 if (!track->sidx_start) {
  99.                     track->sidx_start  = pos;
  100.                     track->sidx_length = size;
  101.                 } else if (pos == track->sidx_start + track->sidx_length) {
  102.                     track->sidx_length = pos + size - track->sidx_start;
  103.                 }
  104.             }
  105.         }
  106.         if (avio_seek(f, pos + size, SEEK_SET) != pos + size)
  107.             break;
  108.     }
  109.  
  110. fail:
  111.     if (f)
  112.         avio_close(f);
  113.     return err;
  114. }
  115.  
  116. static int handle_file(struct Tracks *tracks, const char *file)
  117. {
  118.     AVFormatContext *ctx = NULL;
  119.     int err = 0, i, orig_tracks = tracks->nb_tracks;
  120.     char errbuf[50], *ptr;
  121.     struct Track *track;
  122.  
  123.     err = avformat_open_input(&ctx, file, NULL, NULL);
  124.     if (err < 0) {
  125.         av_strerror(err, errbuf, sizeof(errbuf));
  126.         fprintf(stderr, "Unable to open %s: %s\n", file, errbuf);
  127.         return 1;
  128.     }
  129.  
  130.     err = avformat_find_stream_info(ctx, NULL);
  131.     if (err < 0) {
  132.         av_strerror(err, errbuf, sizeof(errbuf));
  133.         fprintf(stderr, "Unable to identify %s: %s\n", file, errbuf);
  134.         goto fail;
  135.     }
  136.  
  137.     if (ctx->nb_streams < 1) {
  138.         fprintf(stderr, "No streams found in %s\n", file);
  139.         goto fail;
  140.     }
  141.     if (ctx->nb_streams > 1)
  142.         tracks->multiple_tracks_per_file = 1;
  143.  
  144.     for (i = 0; i < ctx->nb_streams; i++) {
  145.         struct Track **temp;
  146.         AVStream *st = ctx->streams[i];
  147.  
  148.         if (st->codec->bit_rate == 0) {
  149.             fprintf(stderr, "Skipping track %d in %s as it has zero bitrate\n",
  150.                     st->id, file);
  151.             continue;
  152.         }
  153.  
  154.         track = av_mallocz(sizeof(*track));
  155.         if (!track) {
  156.             err = AVERROR(ENOMEM);
  157.             goto fail;
  158.         }
  159.         temp = av_realloc_array(tracks->tracks, tracks->nb_tracks + 1,
  160.                                 sizeof(*tracks->tracks));
  161.         if (!temp) {
  162.             av_free(track);
  163.             err = AVERROR(ENOMEM);
  164.             goto fail;
  165.         }
  166.         tracks->tracks = temp;
  167.         tracks->tracks[tracks->nb_tracks] = track;
  168.  
  169.         track->name = file;
  170.         if ((ptr = strrchr(file, '/')))
  171.             track->name = ptr + 1;
  172.  
  173.         track->bitrate   = st->codec->bit_rate;
  174.         track->track_id  = st->id;
  175.         track->timescale = st->time_base.den;
  176.         track->duration  = st->duration;
  177.         track->is_audio  = st->codec->codec_type == AVMEDIA_TYPE_AUDIO;
  178.         track->is_video  = st->codec->codec_type == AVMEDIA_TYPE_VIDEO;
  179.  
  180.         if (!track->is_audio && !track->is_video) {
  181.             fprintf(stderr,
  182.                     "Track %d in %s is neither video nor audio, skipping\n",
  183.                     track->track_id, file);
  184.             av_freep(&tracks->tracks[tracks->nb_tracks]);
  185.             continue;
  186.         }
  187.  
  188.         tracks->duration = FFMAX(tracks->duration,
  189.                                  av_rescale_rnd(track->duration, AV_TIME_BASE,
  190.                                                 track->timescale, AV_ROUND_UP));
  191.  
  192.         if (track->is_audio) {
  193.             track->channels    = st->codec->channels;
  194.             track->sample_rate = st->codec->sample_rate;
  195.         }
  196.         if (track->is_video) {
  197.             track->width  = st->codec->width;
  198.             track->height = st->codec->height;
  199.         }
  200.         set_codec_str(st->codec, track->codec_str, sizeof(track->codec_str));
  201.  
  202.         tracks->nb_tracks++;
  203.     }
  204.  
  205.     avformat_close_input(&ctx);
  206.  
  207.     err = find_sidx(tracks, orig_tracks, file);
  208.  
  209. fail:
  210.     if (ctx)
  211.         avformat_close_input(&ctx);
  212.     return err;
  213. }
  214.  
  215. static void write_time(FILE *out, int64_t time, int decimals, enum AVRounding round)
  216. {
  217.     int seconds = time / AV_TIME_BASE;
  218.     int fractions = time % AV_TIME_BASE;
  219.     int minutes = seconds / 60;
  220.     int hours = minutes / 60;
  221.     fractions = av_rescale_rnd(fractions, pow(10, decimals), AV_TIME_BASE, round);
  222.     seconds %= 60;
  223.     minutes %= 60;
  224.     fprintf(out, "PT");
  225.     if (hours)
  226.         fprintf(out, "%dH", hours);
  227.     if (hours || minutes)
  228.         fprintf(out, "%dM", minutes);
  229.     fprintf(out, "%d.%0*dS", seconds, decimals, fractions);
  230. }
  231.  
  232. static int output_mpd(struct Tracks *tracks, const char *filename)
  233. {
  234.     FILE *out;
  235.     int i, j, ret = 0;
  236.     struct Track **adaptation_sets_buf[2] = { NULL };
  237.     struct Track ***adaptation_sets;
  238.     int nb_tracks_buf[2] = { 0 };
  239.     int *nb_tracks;
  240.     int set, nb_sets;
  241.  
  242.     if (!tracks->multiple_tracks_per_file) {
  243.         adaptation_sets = adaptation_sets_buf;
  244.         nb_tracks = nb_tracks_buf;
  245.         nb_sets = 2;
  246.         for (i = 0; i < 2; i++) {
  247.             adaptation_sets[i] = av_malloc_array(tracks->nb_tracks, sizeof(*adaptation_sets[i]));
  248.             if (!adaptation_sets[i]) {
  249.                 ret = AVERROR(ENOMEM);
  250.                 goto err;
  251.             }
  252.         }
  253.         for (i = 0; i < tracks->nb_tracks; i++) {
  254.             int set_index = -1;
  255.             if (tracks->tracks[i]->is_video)
  256.                 set_index = 0;
  257.             else if (tracks->tracks[i]->is_audio)
  258.                 set_index = 1;
  259.             else
  260.                 continue;
  261.             adaptation_sets[set_index][nb_tracks[set_index]++] = tracks->tracks[i];
  262.         }
  263.     } else {
  264.         adaptation_sets = &tracks->tracks;
  265.         nb_tracks = &tracks->nb_tracks;
  266.         nb_sets = 1;
  267.     }
  268.  
  269.     out = fopen(filename, "w");
  270.     if (!out) {
  271.         ret = AVERROR(errno);
  272.         perror(filename);
  273.         return ret;
  274.     }
  275.     fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
  276.     fprintf(out, "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
  277.                 "\txmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n"
  278.                 "\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
  279.                 "\txsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd\"\n"
  280.                 "\tprofiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\"\n"
  281.                 "\ttype=\"static\"\n");
  282.     fprintf(out, "\tmediaPresentationDuration=\"");
  283.     write_time(out, tracks->duration, 1, AV_ROUND_DOWN);
  284.     fprintf(out, "\"\n");
  285.     fprintf(out, "\tminBufferTime=\"PT5S\">\n");
  286.  
  287.     fprintf(out, "\t<Period start=\"PT0.0S\">\n");
  288.  
  289.     for (set = 0; set < nb_sets; set++) {
  290.         if (nb_tracks[set] == 0)
  291.             continue;
  292.         fprintf(out, "\t\t<AdaptationSet segmentAlignment=\"true\">\n");
  293.         if (nb_sets == 1) {
  294.             for (i = 0; i < nb_tracks[set]; i++) {
  295.                 struct Track *track = adaptation_sets[set][i];
  296.                 if (strcmp(track->name, adaptation_sets[set][0]->name))
  297.                     break;
  298.                 fprintf(out, "\t\t\t<ContentComponent id=\"%d\" contentType=\"%s\" />\n", track->track_id, track->is_audio ? "audio" : "video");
  299.             }
  300.         }
  301.  
  302.         for (i = 0; i < nb_tracks[set]; ) {
  303.             struct Track *first_track = adaptation_sets[set][i];
  304.             int width = 0, height = 0, sample_rate = 0, channels = 0, bitrate = 0;
  305.             fprintf(out, "\t\t\t<Representation id=\"%d\" codecs=\"", i);
  306.             for (j = i; j < nb_tracks[set]; j++) {
  307.                 struct Track *track = adaptation_sets[set][j];
  308.                 if (strcmp(track->name, first_track->name))
  309.                     break;
  310.                 if (track->is_audio) {
  311.                     sample_rate = track->sample_rate;
  312.                     channels = track->channels;
  313.                 }
  314.                 if (track->is_video) {
  315.                     width = track->width;
  316.                     height = track->height;
  317.                 }
  318.                 bitrate += track->bitrate;
  319.                 if (j > i)
  320.                     fprintf(out, ",");
  321.                 fprintf(out, "%s", track->codec_str);
  322.             }
  323.             fprintf(out, "\" mimeType=\"%s/mp4\" bandwidth=\"%d\"",
  324.                     width ? "video" : "audio", bitrate);
  325.             if (width > 0 && height > 0)
  326.                 fprintf(out, " width=\"%d\" height=\"%d\"", width, height);
  327.             if (sample_rate > 0)
  328.                 fprintf(out, " audioSamplingRate=\"%d\"", sample_rate);
  329.             fprintf(out, ">\n");
  330.             if (channels > 0)
  331.                 fprintf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n", channels);
  332.             fprintf(out, "\t\t\t\t<BaseURL>%s</BaseURL>\n", first_track->name);
  333.             fprintf(out, "\t\t\t\t<SegmentBase indexRange=\"%"PRId64"-%"PRId64"\" />\n", first_track->sidx_start, first_track->sidx_start + first_track->sidx_length - 1);
  334.             fprintf(out, "\t\t\t</Representation>\n");
  335.             i = j;
  336.         }
  337.         fprintf(out, "\t\t</AdaptationSet>\n");
  338.     }
  339.     fprintf(out, "\t</Period>\n");
  340.     fprintf(out, "</MPD>\n");
  341.  
  342.     fclose(out);
  343. err:
  344.     for (i = 0; i < 2; i++)
  345.         av_free(adaptation_sets_buf[i]);
  346.     return ret;
  347. }
  348.  
  349. static void clean_tracks(struct Tracks *tracks)
  350. {
  351.     int i;
  352.     for (i = 0; i < tracks->nb_tracks; i++) {
  353.         av_freep(&tracks->tracks[i]);
  354.     }
  355.     av_freep(&tracks->tracks);
  356.     tracks->nb_tracks = 0;
  357. }
  358.  
  359. int main(int argc, char **argv)
  360. {
  361.     const char *out = NULL;
  362.     struct Tracks tracks = { 0 };
  363.     int i;
  364.  
  365.     av_register_all();
  366.  
  367.     for (i = 1; i < argc; i++) {
  368.         if (!strcmp(argv[i], "-out")) {
  369.             out = argv[i + 1];
  370.             i++;
  371.         } else if (argv[i][0] == '-') {
  372.             return usage(argv[0], 1);
  373.         } else {
  374.             if (handle_file(&tracks, argv[i]))
  375.                 return 1;
  376.         }
  377.     }
  378.     if (!tracks.nb_tracks || !out)
  379.         return usage(argv[0], 1);
  380.  
  381.     output_mpd(&tracks, out);
  382.  
  383.     clean_tracks(&tracks);
  384.  
  385.     return 0;
  386. }
  387.