summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changelog1
-rw-r--r--libavformat/allformats.c1
-rw-r--r--libavformat/movenc.c138
-rw-r--r--libavformat/movenc.h5
-rw-r--r--libavformat/version.h2
5 files changed, 142 insertions, 5 deletions
diff --git a/Changelog b/Changelog
index 6a32f3f522..cc7420cb45 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 481d2c1e31..66a8cfec48 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 29ba129372..da110ddda0 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 892324948e..a1e504dca5 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 5cdf1c881a..cdea318eba 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, \