[FFmpeg-cvslog] movenc: Add a separate ismv/isma (smooth streaming) muxer

Martin Storsjö git at videolan.org
Thu Jan 26 03:20:20 CET 2012


ffmpeg | branch: master | Martin Storsjö <martin at martin.st> | Mon Jan  9 17:58:26 2012 +0200| [4ddd54dab40fd84391d1edaec319353f986119b0] | committer: Martin Storsjö

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

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

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=4ddd54dab40fd84391d1edaec319353f986119b0
---

 Changelog                |    1 +
 libavformat/allformats.c |    1 +
 libavformat/movenc.c     |  138 ++++++++++++++++++++++++++++++++++++++++++++-
 libavformat/movenc.h     |    5 ++
 libavformat/version.h    |    2 +-
 5 files changed, 142 insertions(+), 5 deletions(-)

diff --git a/Changelog b/Changelog
index 6a32f3f..cc7420c 100644
--- a/Changelog
+++ b/Changelog
@@ -5,6 +5,7 @@ version <next>:
 
 - XWD encoder and decoder
 - Support for fragmentation in the mov/mp4 muxer
+- ISMV (Smooth Streaming) muxer
 
 
 version 0.8:
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 481d2c1..66a8cfe 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -111,6 +111,7 @@ void av_register_all(void)
     REGISTER_DEMUXER  (INGENIENT, ingenient);
     REGISTER_DEMUXER  (IPMOVIE, ipmovie);
     REGISTER_MUXER    (IPOD, ipod);
+    REGISTER_MUXER    (ISMV, ismv);
     REGISTER_DEMUXER  (ISS, iss);
     REGISTER_DEMUXER  (IV8, iv8);
     REGISTER_MUXDEMUX (IVF, ivf);
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 29ba129..da110dd 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -55,6 +55,7 @@ static const AVOption options[] = {
     { "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},
     { "frag_duration", "Maximum fragment duration", offsetof(MOVMuxContext, max_fragment_duration), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
     { "frag_size", "Maximum fragment size", offsetof(MOVMuxContext, max_fragment_size), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
+    { "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},
     { NULL },
 };
 
@@ -761,7 +762,7 @@ static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track)
 {
     int tag = track->enc->codec_tag;
 
-    if (track->mode == MODE_MP4 || track->mode == MODE_PSP)
+    if (track->mode == MODE_MP4 || track->mode == MODE_PSP || track->mode == MODE_ISM)
         tag = mp4_get_codec_tag(s, track);
     else if (track->mode == MODE_IPOD)
         tag = ipod_get_codec_tag(s, track);
@@ -1940,6 +1941,11 @@ static int mov_write_tfhd_tag(AVIOContext *pb, MOVTrack *track,
         flags |= 0x20; /* default-sample-flags-present */
     }
 
+    /* Don't set a default sample size when creating data for silverlight,
+     * the player refuses to play files with that set. */
+    if (track->mode == MODE_ISM)
+        flags &= ~0x10;
+
     avio_wb32(pb, 0); /* size placeholder */
     ffio_wfourcc(pb, "tfhd");
     avio_w8(pb, 0); /* version */
@@ -2023,7 +2029,78 @@ static int mov_write_trun_tag(AVIOContext *pb, MOVTrack *track)
     return updateSize(pb, pos);
 }
 
-static int mov_write_traf_tag(AVIOContext *pb, MOVTrack *track, int64_t moof_offset)
+static int mov_write_tfxd_tag(AVIOContext *pb, MOVTrack *track)
+{
+    int64_t pos = avio_tell(pb);
+    const uint8_t uuid[] = {
+        0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
+        0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
+    };
+
+    avio_wb32(pb, 0); /* size placeholder */
+    ffio_wfourcc(pb, "uuid");
+    avio_write(pb, uuid, sizeof(uuid));
+    avio_w8(pb, 1);
+    avio_wb24(pb, 0);
+    avio_wb64(pb, track->frag_start);
+    avio_wb64(pb, track->start_dts + track->trackDuration -
+                  track->cluster[0].dts);
+
+    return updateSize(pb, pos);
+}
+
+static int mov_write_tfrf_tag(AVIOContext *pb, MOVMuxContext *mov,
+                              MOVTrack *track, int entry)
+{
+    int n = track->nb_frag_info - 1 - entry, i;
+    int size = 8 + 16 + 4 + 1 + 16*n;
+    const uint8_t uuid[] = {
+        0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95,
+        0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f
+    };
+
+    if (entry < 0)
+        return 0;
+
+    avio_seek(pb, track->frag_info[entry].tfrf_offset, SEEK_SET);
+    avio_wb32(pb, size);
+    ffio_wfourcc(pb, "uuid");
+    avio_write(pb, uuid, sizeof(uuid));
+    avio_w8(pb, 1);
+    avio_wb24(pb, 0);
+    avio_w8(pb, n);
+    for (i = 0; i < n; i++) {
+        int index = entry + 1 + i;
+        avio_wb64(pb, track->frag_info[index].time);
+        avio_wb64(pb, track->frag_info[index].duration);
+    }
+    if (n < mov->ism_lookahead) {
+        int free_size = 16*(mov->ism_lookahead - n);
+        avio_wb32(pb, free_size);
+        ffio_wfourcc(pb, "free");
+        for (i = 0; i < free_size - 8; i++)
+            avio_w8(pb, 0);
+    }
+
+    return 0;
+}
+
+static int mov_write_tfrf_tags(AVIOContext *pb, MOVMuxContext *mov,
+                               MOVTrack *track)
+{
+    int64_t pos = avio_tell(pb);
+    int i;
+    for (i = 0; i < mov->ism_lookahead; i++) {
+        /* Update the tfrf tag for the last ism_lookahead fragments,
+         * nb_frag_info - 1 is the next fragment to be written. */
+        mov_write_tfrf_tag(pb, mov, track, track->nb_frag_info - 2 - i);
+    }
+    avio_seek(pb, pos, SEEK_SET);
+    return 0;
+}
+
+static int mov_write_traf_tag(AVIOContext *pb, MOVMuxContext *mov,
+                              MOVTrack *track, int64_t moof_offset)
 {
     int64_t pos = avio_tell(pb);
     avio_wb32(pb, 0); /* size placeholder */
@@ -2031,6 +2108,19 @@ static int mov_write_traf_tag(AVIOContext *pb, MOVTrack *track, int64_t moof_off
 
     mov_write_tfhd_tag(pb, track, moof_offset);
     mov_write_trun_tag(pb, track);
+    if (mov->mode == MODE_ISM) {
+        mov_write_tfxd_tag(pb, track);
+
+        if (mov->ism_lookahead) {
+            int i, size = 16 + 4 + 1 + 16*mov->ism_lookahead;
+
+            track->tfrf_offset = avio_tell(pb);
+            avio_wb32(pb, 8 + size);
+            ffio_wfourcc(pb, "free");
+            for (i = 0; i < size; i++)
+                avio_w8(pb, 0);
+        }
+    }
 
     return updateSize(pb, pos);
 }
@@ -2050,7 +2140,7 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks)
             continue;
         if (!track->entry)
             continue;
-        mov_write_traf_tag(pb, track, pos);
+        mov_write_traf_tag(pb, mov, track, pos);
     }
 
     end = avio_tell(pb);
@@ -2158,6 +2248,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
         ffio_wfourcc(pb, "isom");
     else if (mov->mode == MODE_IPOD)
         ffio_wfourcc(pb, has_video ? "M4V ":"M4A ");
+    else if (mov->mode == MODE_ISM)
+        ffio_wfourcc(pb, "isml");
     else
         ffio_wfourcc(pb, "qt  ");
 
@@ -2165,7 +2257,10 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
 
     if(mov->mode == MODE_MOV)
         ffio_wfourcc(pb, "qt  ");
-    else{
+    else if (mov->mode == MODE_ISM) {
+        ffio_wfourcc(pb, "piff");
+        ffio_wfourcc(pb, "iso2");
+    } else {
         ffio_wfourcc(pb, "isom");
         ffio_wfourcc(pb, "iso2");
         if(has_h264)
@@ -2342,8 +2437,11 @@ static int mov_flush_fragment(AVFormatContext *s)
             info = &track->frag_info[track->nb_frag_info - 1];
             info->offset   = avio_tell(s->pb);
             info->time     = mov->tracks[i].frag_start;
+            info->duration = duration;
+            mov_write_tfrf_tags(s->pb, mov, track);
 
             mov_write_moof_tag(s->pb, mov, moof_tracks);
+            info->tfrf_offset = track->tfrf_offset;
             mov->fragments++;
 
             avio_wb32(s->pb, mdat_size + 8);
@@ -2571,6 +2669,7 @@ static int mov_write_header(AVFormatContext *s)
         else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV;
         else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP;
         else if (!strcmp("ipod",s->oformat->name)) mov->mode = MODE_IPOD;
+        else if (!strcmp("ismv",s->oformat->name)) mov->mode = MODE_ISM;
 
         mov_write_ftyp_tag(pb,s);
         if (mov->mode == MODE_PSP) {
@@ -2681,6 +2780,10 @@ static int mov_write_header(AVFormatContext *s)
         }
         if (!track->height)
             track->height = st->codec->height;
+        /* The ism specific timescale isn't mandatory, but is assumed by
+         * some tools, such as mp4split. */
+        if (mov->mode == MODE_ISM)
+            track->timescale = 10000000;
 
         avpriv_set_pts_info(st, 64, 1, track->timescale);
 
@@ -2692,6 +2795,15 @@ static int mov_write_header(AVFormatContext *s)
         }
     }
 
+    if (mov->mode == MODE_ISM) {
+        /* If no fragmentation options have been set, set a default. */
+        if (!(mov->flags & (FF_MOV_FLAG_FRAG_KEYFRAME |
+                            FF_MOV_FLAG_FRAG_CUSTOM)) &&
+            !mov->max_fragment_duration && !mov->max_fragment_size)
+            mov->max_fragment_duration = 5000000;
+        mov->flags |= FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_SEPARATE_MOOF;
+    }
+
     /* Set the FRAGMENT flag if any of the fragmentation methods are
      * enabled. */
     if (mov->max_fragment_duration || mov->max_fragment_size ||
@@ -2906,3 +3018,21 @@ AVOutputFormat ff_ipod_muxer = {
     .priv_class = &ipod_muxer_class,
 };
 #endif
+#if CONFIG_ISMV_MUXER
+MOV_CLASS(ismv)
+AVOutputFormat ff_ismv_muxer = {
+    .name              = "ismv",
+    .long_name         = NULL_IF_CONFIG_SMALL("ISMV/ISMA (Smooth Streaming) format"),
+    .mime_type         = "application/mp4",
+    .extensions        = "ismv,isma",
+    .priv_data_size    = sizeof(MOVMuxContext),
+    .audio_codec       = CODEC_ID_AAC,
+    .video_codec       = CODEC_ID_H264,
+    .write_header      = mov_write_header,
+    .write_packet      = ff_mov_write_packet,
+    .write_trailer     = mov_write_trailer,
+    .flags             = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH,
+    .codec_tag         = (const AVCodecTag* const []){ff_mp4_obj_type, 0},
+    .priv_class        = &ismv_muxer_class,
+};
+#endif
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index 8923249..a1e504d 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -38,6 +38,7 @@
 // avconv -i testinput.avi  -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4
 #define MODE_3G2  0x10
 #define MODE_IPOD 0x20
+#define MODE_ISM  0x40
 
 typedef struct MOVIentry {
     uint64_t     pos;
@@ -68,6 +69,8 @@ typedef struct {
 typedef struct {
     int64_t offset;
     int64_t time;
+    int64_t duration;
+    int64_t tfrf_offset;
 } MOVFragmentInfo;
 
 typedef struct MOVIndex {
@@ -113,6 +116,7 @@ typedef struct MOVIndex {
     int64_t     moof_size_offset;
     int64_t     data_offset;
     int64_t     frag_start;
+    int64_t     tfrf_offset;
 
     int         nb_frag_info;
     MOVFragmentInfo *frag_info;
@@ -137,6 +141,7 @@ typedef struct MOVMuxContext {
     int fragments;
     int max_fragment_duration;
     int max_fragment_size;
+    int ism_lookahead;
 } MOVMuxContext;
 
 #define FF_MOV_FLAG_RTP_HINT 1
diff --git a/libavformat/version.h b/libavformat/version.h
index 5cdf1c8..cdea318 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -30,7 +30,7 @@
 #include "libavutil/avutil.h"
 
 #define LIBAVFORMAT_VERSION_MAJOR 53
-#define LIBAVFORMAT_VERSION_MINOR 22
+#define LIBAVFORMAT_VERSION_MINOR 23
 #define LIBAVFORMAT_VERSION_MICRO  0
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \



More information about the ffmpeg-cvslog mailing list