Browse code

movenc: Add a separate ismv/isma (smooth streaming) muxer

Signed-off-by: Martin Storsjö <martin@martin.st>

Martin Storsjö authored on 2012/01/10 00:58:26
Showing 5 changed files
... ...
@@ -5,6 +5,7 @@ version <next>:
5 5
 
6 6
 - XWD encoder and decoder
7 7
 - Support for fragmentation in the mov/mp4 muxer
8
+- ISMV (Smooth Streaming) muxer
8 9
 
9 10
 
10 11
 version 0.8:
... ...
@@ -111,6 +111,7 @@ void av_register_all(void)
111 111
     REGISTER_DEMUXER  (INGENIENT, ingenient);
112 112
     REGISTER_DEMUXER  (IPMOVIE, ipmovie);
113 113
     REGISTER_MUXER    (IPOD, ipod);
114
+    REGISTER_MUXER    (ISMV, ismv);
114 115
     REGISTER_DEMUXER  (ISS, iss);
115 116
     REGISTER_DEMUXER  (IV8, iv8);
116 117
     REGISTER_MUXDEMUX (IVF, ivf);
... ...
@@ -55,6 +55,7 @@ static const AVOption options[] = {
55 55
     { "iods_video_profile", "iods video profile atom.", offsetof(MOVMuxContext, iods_video_profile), AV_OPT_TYPE_INT, {.dbl = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM},
56 56
     { "frag_duration", "Maximum fragment duration", offsetof(MOVMuxContext, max_fragment_duration), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
57 57
     { "frag_size", "Maximum fragment size", offsetof(MOVMuxContext, max_fragment_size), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
58
+    { "ism_lookahead", "Number of lookahead entries for ISM files", offsetof(MOVMuxContext, ism_lookahead), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
58 59
     { NULL },
59 60
 };
60 61
 
... ...
@@ -761,7 +762,7 @@ static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track)
761 761
 {
762 762
     int tag = track->enc->codec_tag;
763 763
 
764
-    if (track->mode == MODE_MP4 || track->mode == MODE_PSP)
764
+    if (track->mode == MODE_MP4 || track->mode == MODE_PSP || track->mode == MODE_ISM)
765 765
         tag = mp4_get_codec_tag(s, track);
766 766
     else if (track->mode == MODE_IPOD)
767 767
         tag = ipod_get_codec_tag(s, track);
... ...
@@ -1940,6 +1941,11 @@ static int mov_write_tfhd_tag(AVIOContext *pb, MOVTrack *track,
1940 1940
         flags |= 0x20; /* default-sample-flags-present */
1941 1941
     }
1942 1942
 
1943
+    /* Don't set a default sample size when creating data for silverlight,
1944
+     * the player refuses to play files with that set. */
1945
+    if (track->mode == MODE_ISM)
1946
+        flags &= ~0x10;
1947
+
1943 1948
     avio_wb32(pb, 0); /* size placeholder */
1944 1949
     ffio_wfourcc(pb, "tfhd");
1945 1950
     avio_w8(pb, 0); /* version */
... ...
@@ -2023,7 +2029,78 @@ static int mov_write_trun_tag(AVIOContext *pb, MOVTrack *track)
2023 2023
     return updateSize(pb, pos);
2024 2024
 }
2025 2025
 
2026
-static int mov_write_traf_tag(AVIOContext *pb, MOVTrack *track, int64_t moof_offset)
2026
+static int mov_write_tfxd_tag(AVIOContext *pb, MOVTrack *track)
2027
+{
2028
+    int64_t pos = avio_tell(pb);
2029
+    const uint8_t uuid[] = {
2030
+        0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
2031
+        0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
2032
+    };
2033
+
2034
+    avio_wb32(pb, 0); /* size placeholder */
2035
+    ffio_wfourcc(pb, "uuid");
2036
+    avio_write(pb, uuid, sizeof(uuid));
2037
+    avio_w8(pb, 1);
2038
+    avio_wb24(pb, 0);
2039
+    avio_wb64(pb, track->frag_start);
2040
+    avio_wb64(pb, track->start_dts + track->trackDuration -
2041
+                  track->cluster[0].dts);
2042
+
2043
+    return updateSize(pb, pos);
2044
+}
2045
+
2046
+static int mov_write_tfrf_tag(AVIOContext *pb, MOVMuxContext *mov,
2047
+                              MOVTrack *track, int entry)
2048
+{
2049
+    int n = track->nb_frag_info - 1 - entry, i;
2050
+    int size = 8 + 16 + 4 + 1 + 16*n;
2051
+    const uint8_t uuid[] = {
2052
+        0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95,
2053
+        0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f
2054
+    };
2055
+
2056
+    if (entry < 0)
2057
+        return 0;
2058
+
2059
+    avio_seek(pb, track->frag_info[entry].tfrf_offset, SEEK_SET);
2060
+    avio_wb32(pb, size);
2061
+    ffio_wfourcc(pb, "uuid");
2062
+    avio_write(pb, uuid, sizeof(uuid));
2063
+    avio_w8(pb, 1);
2064
+    avio_wb24(pb, 0);
2065
+    avio_w8(pb, n);
2066
+    for (i = 0; i < n; i++) {
2067
+        int index = entry + 1 + i;
2068
+        avio_wb64(pb, track->frag_info[index].time);
2069
+        avio_wb64(pb, track->frag_info[index].duration);
2070
+    }
2071
+    if (n < mov->ism_lookahead) {
2072
+        int free_size = 16*(mov->ism_lookahead - n);
2073
+        avio_wb32(pb, free_size);
2074
+        ffio_wfourcc(pb, "free");
2075
+        for (i = 0; i < free_size - 8; i++)
2076
+            avio_w8(pb, 0);
2077
+    }
2078
+
2079
+    return 0;
2080
+}
2081
+
2082
+static int mov_write_tfrf_tags(AVIOContext *pb, MOVMuxContext *mov,
2083
+                               MOVTrack *track)
2084
+{
2085
+    int64_t pos = avio_tell(pb);
2086
+    int i;
2087
+    for (i = 0; i < mov->ism_lookahead; i++) {
2088
+        /* Update the tfrf tag for the last ism_lookahead fragments,
2089
+         * nb_frag_info - 1 is the next fragment to be written. */
2090
+        mov_write_tfrf_tag(pb, mov, track, track->nb_frag_info - 2 - i);
2091
+    }
2092
+    avio_seek(pb, pos, SEEK_SET);
2093
+    return 0;
2094
+}
2095
+
2096
+static int mov_write_traf_tag(AVIOContext *pb, MOVMuxContext *mov,
2097
+                              MOVTrack *track, int64_t moof_offset)
2027 2098
 {
2028 2099
     int64_t pos = avio_tell(pb);
2029 2100
     avio_wb32(pb, 0); /* size placeholder */
... ...
@@ -2031,6 +2108,19 @@ static int mov_write_traf_tag(AVIOContext *pb, MOVTrack *track, int64_t moof_off
2031 2031
 
2032 2032
     mov_write_tfhd_tag(pb, track, moof_offset);
2033 2033
     mov_write_trun_tag(pb, track);
2034
+    if (mov->mode == MODE_ISM) {
2035
+        mov_write_tfxd_tag(pb, track);
2036
+
2037
+        if (mov->ism_lookahead) {
2038
+            int i, size = 16 + 4 + 1 + 16*mov->ism_lookahead;
2039
+
2040
+            track->tfrf_offset = avio_tell(pb);
2041
+            avio_wb32(pb, 8 + size);
2042
+            ffio_wfourcc(pb, "free");
2043
+            for (i = 0; i < size; i++)
2044
+                avio_w8(pb, 0);
2045
+        }
2046
+    }
2034 2047
 
2035 2048
     return updateSize(pb, pos);
2036 2049
 }
... ...
@@ -2050,7 +2140,7 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks)
2050 2050
             continue;
2051 2051
         if (!track->entry)
2052 2052
             continue;
2053
-        mov_write_traf_tag(pb, track, pos);
2053
+        mov_write_traf_tag(pb, mov, track, pos);
2054 2054
     }
2055 2055
 
2056 2056
     end = avio_tell(pb);
... ...
@@ -2158,6 +2248,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
2158 2158
         ffio_wfourcc(pb, "isom");
2159 2159
     else if (mov->mode == MODE_IPOD)
2160 2160
         ffio_wfourcc(pb, has_video ? "M4V ":"M4A ");
2161
+    else if (mov->mode == MODE_ISM)
2162
+        ffio_wfourcc(pb, "isml");
2161 2163
     else
2162 2164
         ffio_wfourcc(pb, "qt  ");
2163 2165
 
... ...
@@ -2165,7 +2257,10 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
2165 2165
 
2166 2166
     if(mov->mode == MODE_MOV)
2167 2167
         ffio_wfourcc(pb, "qt  ");
2168
-    else{
2168
+    else if (mov->mode == MODE_ISM) {
2169
+        ffio_wfourcc(pb, "piff");
2170
+        ffio_wfourcc(pb, "iso2");
2171
+    } else {
2169 2172
         ffio_wfourcc(pb, "isom");
2170 2173
         ffio_wfourcc(pb, "iso2");
2171 2174
         if(has_h264)
... ...
@@ -2342,8 +2437,11 @@ static int mov_flush_fragment(AVFormatContext *s)
2342 2342
             info = &track->frag_info[track->nb_frag_info - 1];
2343 2343
             info->offset   = avio_tell(s->pb);
2344 2344
             info->time     = mov->tracks[i].frag_start;
2345
+            info->duration = duration;
2346
+            mov_write_tfrf_tags(s->pb, mov, track);
2345 2347
 
2346 2348
             mov_write_moof_tag(s->pb, mov, moof_tracks);
2349
+            info->tfrf_offset = track->tfrf_offset;
2347 2350
             mov->fragments++;
2348 2351
 
2349 2352
             avio_wb32(s->pb, mdat_size + 8);
... ...
@@ -2571,6 +2669,7 @@ static int mov_write_header(AVFormatContext *s)
2571 2571
         else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV;
2572 2572
         else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP;
2573 2573
         else if (!strcmp("ipod",s->oformat->name)) mov->mode = MODE_IPOD;
2574
+        else if (!strcmp("ismv",s->oformat->name)) mov->mode = MODE_ISM;
2574 2575
 
2575 2576
         mov_write_ftyp_tag(pb,s);
2576 2577
         if (mov->mode == MODE_PSP) {
... ...
@@ -2681,6 +2780,10 @@ static int mov_write_header(AVFormatContext *s)
2681 2681
         }
2682 2682
         if (!track->height)
2683 2683
             track->height = st->codec->height;
2684
+        /* The ism specific timescale isn't mandatory, but is assumed by
2685
+         * some tools, such as mp4split. */
2686
+        if (mov->mode == MODE_ISM)
2687
+            track->timescale = 10000000;
2684 2688
 
2685 2689
         avpriv_set_pts_info(st, 64, 1, track->timescale);
2686 2690
 
... ...
@@ -2692,6 +2795,15 @@ static int mov_write_header(AVFormatContext *s)
2692 2692
         }
2693 2693
     }
2694 2694
 
2695
+    if (mov->mode == MODE_ISM) {
2696
+        /* If no fragmentation options have been set, set a default. */
2697
+        if (!(mov->flags & (FF_MOV_FLAG_FRAG_KEYFRAME |
2698
+                            FF_MOV_FLAG_FRAG_CUSTOM)) &&
2699
+            !mov->max_fragment_duration && !mov->max_fragment_size)
2700
+            mov->max_fragment_duration = 5000000;
2701
+        mov->flags |= FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_SEPARATE_MOOF;
2702
+    }
2703
+
2695 2704
     /* Set the FRAGMENT flag if any of the fragmentation methods are
2696 2705
      * enabled. */
2697 2706
     if (mov->max_fragment_duration || mov->max_fragment_size ||
... ...
@@ -2906,3 +3018,21 @@ AVOutputFormat ff_ipod_muxer = {
2906 2906
     .priv_class = &ipod_muxer_class,
2907 2907
 };
2908 2908
 #endif
2909
+#if CONFIG_ISMV_MUXER
2910
+MOV_CLASS(ismv)
2911
+AVOutputFormat ff_ismv_muxer = {
2912
+    .name              = "ismv",
2913
+    .long_name         = NULL_IF_CONFIG_SMALL("ISMV/ISMA (Smooth Streaming) format"),
2914
+    .mime_type         = "application/mp4",
2915
+    .extensions        = "ismv,isma",
2916
+    .priv_data_size    = sizeof(MOVMuxContext),
2917
+    .audio_codec       = CODEC_ID_AAC,
2918
+    .video_codec       = CODEC_ID_H264,
2919
+    .write_header      = mov_write_header,
2920
+    .write_packet      = ff_mov_write_packet,
2921
+    .write_trailer     = mov_write_trailer,
2922
+    .flags             = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH,
2923
+    .codec_tag         = (const AVCodecTag* const []){ff_mp4_obj_type, 0},
2924
+    .priv_class        = &ismv_muxer_class,
2925
+};
2926
+#endif
... ...
@@ -38,6 +38,7 @@
38 38
 // avconv -i testinput.avi  -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4
39 39
 #define MODE_3G2  0x10
40 40
 #define MODE_IPOD 0x20
41
+#define MODE_ISM  0x40
41 42
 
42 43
 typedef struct MOVIentry {
43 44
     uint64_t     pos;
... ...
@@ -68,6 +69,8 @@ typedef struct {
68 68
 typedef struct {
69 69
     int64_t offset;
70 70
     int64_t time;
71
+    int64_t duration;
72
+    int64_t tfrf_offset;
71 73
 } MOVFragmentInfo;
72 74
 
73 75
 typedef struct MOVIndex {
... ...
@@ -113,6 +116,7 @@ typedef struct MOVIndex {
113 113
     int64_t     moof_size_offset;
114 114
     int64_t     data_offset;
115 115
     int64_t     frag_start;
116
+    int64_t     tfrf_offset;
116 117
 
117 118
     int         nb_frag_info;
118 119
     MOVFragmentInfo *frag_info;
... ...
@@ -137,6 +141,7 @@ typedef struct MOVMuxContext {
137 137
     int fragments;
138 138
     int max_fragment_duration;
139 139
     int max_fragment_size;
140
+    int ism_lookahead;
140 141
 } MOVMuxContext;
141 142
 
142 143
 #define FF_MOV_FLAG_RTP_HINT 1
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/avutil.h"
31 31
 
32 32
 #define LIBAVFORMAT_VERSION_MAJOR 53
33
-#define LIBAVFORMAT_VERSION_MINOR 22
33
+#define LIBAVFORMAT_VERSION_MINOR 23
34 34
 #define LIBAVFORMAT_VERSION_MICRO  0
35 35
 
36 36
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \