Commit 4ddd54da authored by Martin Storsjö's avatar Martin Storsjö

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

Signed-off-by: 's avatarMartin Storsjö <martin@martin.st>
parent b613ff5e
......@@ -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:
......
......@@ -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);
......
......@@ -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
......@@ -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
......
......@@ -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, \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment