summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Storsjö <martin@martin.st>2012-01-20 13:02:18 +0200
committerMartin Storsjö <martin@martin.st>2012-01-27 21:12:28 +0200
commitf53221049933627a31fb4a530c290802715ccf65 (patch)
tree89752902ffb3e8d12527bac8be7f1755aaaa9dc2
parent9a7dc618c50902e7a171f2deda6430d52c277a95 (diff)
movenc: Support adding isml (smooth streaming live) metadata
This metadata is required for pushing a live stream to an IIS publishing point. Signed-off-by: Martin Storsjö <martin@martin.st>
-rw-r--r--libavformat/movenc.c130
-rw-r--r--libavformat/movenc.h1
2 files changed, 131 insertions, 0 deletions
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index eee426f692..8a40345716 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -49,6 +49,7 @@ static const AVOption options[] = {
{ "frag_keyframe", "Fragment at video keyframes", 0, AV_OPT_TYPE_CONST, {.dbl = FF_MOV_FLAG_FRAG_KEYFRAME}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
{ "separate_moof", "Write separate moof/mdat atoms for each track", 0, AV_OPT_TYPE_CONST, {.dbl = FF_MOV_FLAG_SEPARATE_MOOF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
{ "frag_custom", "Flush fragments on caller requests", 0, AV_OPT_TYPE_CONST, {.dbl = FF_MOV_FLAG_FRAG_CUSTOM}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+ { "isml", "Create a live smooth streaming feed (for pushing to a publishing point)", 0, AV_OPT_TYPE_CONST, {.dbl = FF_MOV_FLAG_ISML}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
FF_RTP_FLAG_OPTS(MOVMuxContext, rtp_flags),
{ "skip_iods", "Skip writing iods atom.", offsetof(MOVMuxContext, iods_skip), AV_OPT_TYPE_INT, {.dbl = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
{ "iods_audio_profile", "iods audio profile atom.", offsetof(MOVMuxContext, iods_audio_profile), AV_OPT_TYPE_INT, {.dbl = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM},
@@ -1920,6 +1921,128 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
return updateSize(pb, pos);
}
+static void param_write_int(AVIOContext *pb, const char *name, int value)
+{
+ avio_printf(pb, "<param name=\"%s\" value=\"%d\" valuetype=\"data\"/>\n", name, value);
+}
+
+static void param_write_string(AVIOContext *pb, const char *name, const char *value)
+{
+ avio_printf(pb, "<param name=\"%s\" value=\"%s\" valuetype=\"data\"/>\n", name, value);
+}
+
+static void param_write_hex(AVIOContext *pb, const char *name, const uint8_t *value, int len)
+{
+ char buf[150];
+ len = FFMIN(sizeof(buf)/2 - 1, len);
+ ff_data_to_hex(buf, value, len, 0);
+ buf[2*len] = '\0';
+ avio_printf(pb, "<param name=\"%s\" value=\"%s\" valuetype=\"data\"/>\n", name, buf);
+}
+
+static void write_h264_extradata(AVIOContext *pb, AVCodecContext *enc)
+{
+ uint16_t sps_size, pps_size, len;
+ char buf[150];
+ sps_size = AV_RB16(&enc->extradata[6]);
+ if (11 + sps_size > enc->extradata_size)
+ return;
+ pps_size = AV_RB16(&enc->extradata[9 + sps_size]);
+ if (11 + sps_size + pps_size > enc->extradata_size)
+ return;
+ len = FFMIN(sizeof(buf)/2 - 1, sps_size);
+ ff_data_to_hex(buf, &enc->extradata[8], len, 0);
+ buf[2*len] = '\0';
+ avio_printf(pb, "<param name=\"CodecPrivateData\" value=\"00000001%s", buf);
+ len = FFMIN(sizeof(buf)/2 - 1, pps_size);
+ ff_data_to_hex(buf, &enc->extradata[11 + sps_size], len, 0);
+ buf[2*len] = '\0';
+ avio_printf(pb, "00000001%s\" valuetype=\"data\"/>\n", buf);
+}
+
+static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov)
+{
+ int64_t pos = avio_tell(pb);
+ int i;
+ const uint8_t uuid[] = {
+ 0xa5, 0xd4, 0x0b, 0x30, 0xe8, 0x14, 0x11, 0xdd,
+ 0xba, 0x2f, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66
+ };
+
+ avio_wb32(pb, 0);
+ ffio_wfourcc(pb, "uuid");
+ avio_write(pb, uuid, sizeof(uuid));
+ avio_wb32(pb, 0);
+
+ avio_printf(pb, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+ avio_printf(pb, "<smil xmlns=\"http://www.w3.org/2001/SMIL20/Language\">\n");
+ avio_printf(pb, "<head>\n");
+ avio_printf(pb, "<meta name=\"creator\" content=\"%s\" />\n",
+ LIBAVFORMAT_IDENT);
+ avio_printf(pb, "</head>\n");
+ avio_printf(pb, "<body>\n");
+ avio_printf(pb, "<switch>\n");
+ for (i = 0; i < mov->nb_streams; i++) {
+ MOVTrack *track = &mov->tracks[i];
+ const char *type;
+ /* track->trackID is initialized in write_moov, and thus isn't known
+ * here yet */
+ int track_id = i + 1;
+
+ if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) {
+ type = "video";
+ } else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) {
+ type = "audio";
+ } else {
+ continue;
+ }
+ avio_printf(pb, "<%s systemBitrate=\"%d\">\n", type,
+ track->enc->bit_rate);
+ param_write_int(pb, "systemBitrate", track->enc->bit_rate);
+ param_write_int(pb, "trackID", track_id);
+ if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (track->enc->codec_id == CODEC_ID_H264 &&
+ track->enc->extradata_size >= 11 &&
+ track->enc->extradata[0] == 1) {
+ write_h264_extradata(pb, track->enc);
+ } else {
+ param_write_hex(pb, "CodecPrivateData", track->enc->extradata,
+ track->enc->extradata_size);
+ }
+ if (track->enc->codec_id == CODEC_ID_H264) {
+ param_write_string(pb, "FourCC", "H264");
+ } else if (track->enc->codec_id == CODEC_ID_VC1) {
+ param_write_string(pb, "FourCC", "WVC1");
+ }
+ param_write_int(pb, "MaxWidth", track->enc->width);
+ param_write_int(pb, "MaxHeight", track->enc->height);
+ param_write_int(pb, "DisplayWidth", track->enc->width);
+ param_write_int(pb, "DisplayHeight", track->enc->height);
+ } else {
+ if (track->enc->codec_id == CODEC_ID_AAC) {
+ param_write_string(pb, "FourCC", "AACL");
+ } else if (track->enc->codec_id == CODEC_ID_WMAPRO) {
+ param_write_string(pb, "FourCC", "WMAP");
+ }
+ param_write_hex(pb, "CodecPrivateData", track->enc->extradata,
+ track->enc->extradata_size);
+ param_write_int(pb, "AudioTag", ff_codec_get_tag(ff_codec_wav_tags,
+ track->enc->codec_id));
+ param_write_int(pb, "Channels", track->enc->channels);
+ param_write_int(pb, "SamplingRate", track->enc->sample_rate);
+ param_write_int(pb, "BitsPerSample", 16);
+ param_write_int(pb, "PacketSize", track->enc->block_align ?
+ track->enc->block_align : 4);
+ }
+ avio_printf(pb, "</%s>\n", type);
+ }
+ avio_printf(pb, "</switch>\n");
+ avio_printf(pb, "</body>\n");
+ avio_printf(pb, "</smil>\n");
+
+ return updateSize(pb, pos);
+}
+
static int mov_write_mfhd_tag(AVIOContext *pb, MOVMuxContext *mov)
{
avio_wb32(pb, 16);
@@ -2190,6 +2313,10 @@ static int mov_write_mfra_tag(AVIOContext *pb, MOVMuxContext *mov)
avio_wb32(pb, 0); /* size placeholder */
ffio_wfourcc(pb, "mfra");
+ /* An empty mfra atom is enough to indicate to the publishing point that
+ * the stream has ended. */
+ if (mov->flags & FF_MOV_FLAG_ISML)
+ return updateSize(pb, pos);
for (i = 0; i < mov->nb_streams; i++) {
MOVTrack *track = &mov->tracks[i];
@@ -2829,6 +2956,9 @@ static int mov_write_header(AVFormatContext *s)
avio_flush(pb);
+ if (mov->flags & FF_MOV_FLAG_ISML)
+ mov_write_isml_manifest(pb, mov);
+
if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV) {
mov_write_moov_tag(pb, mov, s);
mov->fragments++;
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index a1e504dca5..0b74a61d06 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -150,6 +150,7 @@ typedef struct MOVMuxContext {
#define FF_MOV_FLAG_FRAG_KEYFRAME 8
#define FF_MOV_FLAG_SEPARATE_MOOF 16
#define FF_MOV_FLAG_FRAG_CUSTOM 32
+#define FF_MOV_FLAG_ISML 64
int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt);