From f620488654700dfc06ce5548baa15c4344662969 Mon Sep 17 00:00:00 2001 From: David Conrad Date: Sun, 11 Jan 2009 10:26:44 +0000 Subject: [PATCH] Add support for muxing mov/mp4/3gp timed text streams Originally committed as revision 16531 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavformat/isom.c | 1 + libavformat/movenc.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/libavformat/isom.c b/libavformat/isom.c index 7d1da7c..b5e09c5 100644 --- a/libavformat/isom.c +++ b/libavformat/isom.c @@ -28,6 +28,7 @@ /* http://www.mp4ra.org */ /* ordered by muxing preference */ const AVCodecTag ff_mp4_obj_type[] = { + { CODEC_ID_MOV_TEXT , 0x08 }, { CODEC_ID_MPEG4 , 0x20 }, { CODEC_ID_H264 , 0x21 }, { CODEC_ID_AAC , 0x40 }, diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 6ddbed1..bb900e3 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -550,6 +550,7 @@ static const AVCodecTag codec_3gp_tags[] = { { CODEC_ID_AAC, MKTAG('m','p','4','a') }, { CODEC_ID_AMR_NB, MKTAG('s','a','m','r') }, { CODEC_ID_AMR_WB, MKTAG('s','a','w','b') }, + { CODEC_ID_MOV_TEXT, MKTAG('t', 'x', '3', 'g') }, { CODEC_ID_NONE, 0 }, }; @@ -567,6 +568,7 @@ static const AVCodecTag codec_ipod_tags[] = { { CODEC_ID_AAC, MKTAG('m','p','4','a') }, { CODEC_ID_ALAC, MKTAG('a','l','a','c') }, { CODEC_ID_AC3, MKTAG('a','c','-','3') }, + { CODEC_ID_MOV_TEXT, MKTAG('t', 'x', '3', 'g') }, { CODEC_ID_NONE, 0 }, }; @@ -579,6 +581,7 @@ static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track) if (track->enc->codec_id == CODEC_ID_H264) tag = MKTAG('a','v','c','1'); else if (track->enc->codec_id == CODEC_ID_AC3) tag = MKTAG('a','c','-','3'); else if (track->enc->codec_id == CODEC_ID_DIRAC) tag = MKTAG('d','r','a','c'); + else if (track->enc->codec_id == CODEC_ID_MOV_TEXT) tag = MKTAG('t','x','3','g'); else if (track->enc->codec_type == CODEC_TYPE_VIDEO) tag = MKTAG('m','p','4','v'); else if (track->enc->codec_type == CODEC_TYPE_AUDIO) tag = MKTAG('m','p','4','a'); } else if (track->mode == MODE_IPOD) { @@ -621,6 +624,8 @@ static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track) "the file may be unplayable!\n"); } } + } else if (track->enc->codec_type == CODEC_TYPE_SUBTITLE) { + tag = codec_get_tag(ff_codec_movsubtitle_tags, track->enc->codec_id); } } } @@ -643,6 +648,21 @@ static int mov_write_uuid_tag_ipod(ByteIOContext *pb) return 28; } +static int mov_write_subtitle_tag(ByteIOContext *pb, MOVTrack *track) +{ + int64_t pos = url_ftell(pb); + put_be32(pb, 0); /* size */ + put_le32(pb, track->tag); // store it byteswapped + put_be32(pb, 0); /* Reserved */ + put_be16(pb, 0); /* Reserved */ + put_be16(pb, 1); /* Data-reference index */ + + if (track->enc->extradata_size) + put_buffer(pb, track->enc->extradata, track->enc->extradata_size); + + return updateSize(pb, pos); +} + static int mov_write_video_tag(ByteIOContext *pb, MOVTrack *track) { int64_t pos = url_ftell(pb); @@ -718,6 +738,8 @@ static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack *track) mov_write_video_tag(pb, track); else if (track->enc->codec_type == CODEC_TYPE_AUDIO) mov_write_audio_tag(pb, track); + else if (track->enc->codec_type == CODEC_TYPE_SUBTITLE) + mov_write_subtitle_tag(pb, track); return updateSize(pb, pos); } @@ -838,6 +860,30 @@ static int mov_write_dinf_tag(ByteIOContext *pb) return updateSize(pb, pos); } +static int mov_write_nmhd_tag(ByteIOContext *pb) +{ + put_be32(pb, 12); + put_tag(pb, "nmhd"); + put_be32(pb, 0); + return 12; +} + +static int mov_write_gmhd_tag(ByteIOContext *pb) +{ + put_be32(pb, 0x20); /* size */ + put_tag(pb, "gmhd"); + put_be32(pb, 0x18); /* gmin size */ + put_tag(pb, "gmin"); /* generic media info */ + put_be32(pb, 0); /* version & flags */ + put_be16(pb, 0x40); /* graphics mode = */ + put_be16(pb, 0x8000); /* opColor (r?) */ + put_be16(pb, 0x8000); /* opColor (g?) */ + put_be16(pb, 0x8000); /* opColor (b?) */ + put_be16(pb, 0); /* balance */ + put_be16(pb, 0); /* reserved */ + return 0x20; +} + static int mov_write_smhd_tag(ByteIOContext *pb) { put_be32(pb, 16); /* size */ @@ -871,9 +917,13 @@ static int mov_write_hdlr_tag(ByteIOContext *pb, MOVTrack *track) if (track->enc->codec_type == CODEC_TYPE_VIDEO) { hdlr_type = "vide"; descr = "VideoHandler"; - } else { + } else if (track->enc->codec_type == CODEC_TYPE_AUDIO){ hdlr_type = "soun"; descr = "SoundHandler"; + } else if (track->enc->codec_type == CODEC_TYPE_SUBTITLE){ + if (track->mode == MODE_IPOD) hdlr_type = "sbtl"; + else hdlr_type = "text"; + descr = "SubtitleHandler"; } } @@ -897,8 +947,14 @@ static int mov_write_minf_tag(ByteIOContext *pb, MOVTrack *track) put_tag(pb, "minf"); if(track->enc->codec_type == CODEC_TYPE_VIDEO) mov_write_vmhd_tag(pb); - else + else if (track->enc->codec_type == CODEC_TYPE_AUDIO) mov_write_smhd_tag(pb); + else if (track->enc->codec_type == CODEC_TYPE_SUBTITLE) { + if (track->mode == MODE_MOV) + mov_write_gmhd_tag(pb); + else + mov_write_nmhd_tag(pb); + } if (track->mode == MODE_MOV) /* FIXME: Why do it for MODE_MOV only ? */ mov_write_hdlr_tag(pb, NULL); mov_write_dinf_tag(pb); @@ -989,7 +1045,8 @@ static int mov_write_tkhd_tag(ByteIOContext *pb, MOVTrack *track, AVStream *st) put_be32(pb, 0x40000000); /* reserved */ /* Track width and height, for visual only */ - if(track->enc->codec_type == CODEC_TYPE_VIDEO) { + if(track->enc->codec_type == CODEC_TYPE_VIDEO || + track->enc->codec_type == CODEC_TYPE_SUBTITLE) { double sample_aspect_ratio = av_q2d(st->sample_aspect_ratio); if(!sample_aspect_ratio) sample_aspect_ratio = 1; put_be32(pb, sample_aspect_ratio * track->enc->width*0x10000); @@ -1624,6 +1681,9 @@ static int mov_write_header(AVFormatContext *s) i, track->enc->sample_rate); return -1; } + }else if(st->codec->codec_type == CODEC_TYPE_SUBTITLE){ + track->timescale = st->codec->time_base.den; + av_set_pts_info(st, 64, 1, st->codec->time_base.den); } } -- 2.7.4