[FFmpeg-devel] [PATCH] MP4 Subtitle support

Benjamin Rainer benjamin.rainer at itec.aau.at
Wed Oct 3 16:29:53 CEST 2012


	modified:   libavformat/isom.c
	modified:   libavformat/isom.h
	modified:   libavformat/mov.c
	modified:   libavformat/riff.h
	modified:   libavformat/utils.c

Added support for subtitles in MP4 (other than tx3g). Added support for the TextMetadataSampleEntry box ('mett') and XMLMetadataSampleEntry box ('metx'). Codecs are detected by the mime type.
---
 libavformat/isom.c  |    9 ++++++++
 libavformat/isom.h  |    1 +
 libavformat/mov.c   |   63 +++++++++++++++++++++++++++++++++++++++++++--------
 libavformat/riff.h  |    1 +
 libavformat/utils.c |   13 +++++++++++
 5 files changed, 77 insertions(+), 10 deletions(-)

diff --git a/libavformat/isom.c b/libavformat/isom.c
index caa4d17..1396c9e 100644
--- a/libavformat/isom.c
+++ b/libavformat/isom.c
@@ -302,6 +302,15 @@ const AVCodecTag ff_codec_movsubtitle_tags[] = {
     { AV_CODEC_ID_NONE, 0 },
 };
 
+const CodecMime ff_codec_movsubtitle_mimetypes[] = {
+    { "text/srt", 	AV_CODEC_ID_SRT },	/* extend */
+    { "text/subip",	AV_CODEC_ID_SUBRIP },
+    { "text/webvtt", 	AV_CODEC_ID_WEBVTT },
+    { "text/rtext", 	AV_CODEC_ID_REALTEXT },
+    { "text/subV",  	AV_CODEC_ID_SUBVIEWER },
+    { "",		AV_CODEC_ID_NONE },
+};
+
 /* map numeric codes from mdhd atom to ISO 639 */
 /* cf. QTFileFormat.pdf p253, qtff.pdf p205 */
 /* http://developer.apple.com/documentation/mac/Text/Text-368.html */
diff --git a/libavformat/isom.h b/libavformat/isom.h
index 370ba43..4a52a28 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -33,6 +33,7 @@ extern const AVCodecTag ff_mp4_obj_type[];
 extern const AVCodecTag ff_codec_movvideo_tags[];
 extern const AVCodecTag ff_codec_movaudio_tags[];
 extern const AVCodecTag ff_codec_movsubtitle_tags[];
+extern const CodecMime	ff_codec_movsubtitlte_mime_types[];
 
 int ff_mov_iso639_to_lang(const char lang[4], int mp4);
 int ff_mov_lang_to_iso639(unsigned code, char to[4]);
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 0639ddb..129dcdf 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -394,6 +394,25 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     return 0;
 }
 
+static int mov_read_metadata_string(AVDictionary **metadata, AVIOContext *pb, MOVAtom atom, const char *dict_entry_name)
+{
+    char *str,*p;
+    
+    p = str = (char *)av_mallocz(atom.size); /* we do not know the size of the string, size of the box should be enough */
+  
+    if(!str)
+	return AVERROR(ENOMEM);   
+
+    while((*str = avio_r8(pb)) != (int)NULL) str++;
+    
+    av_dict_set(metadata, dict_entry_name, p,0); /* add the string into our dictonary */
+    
+    av_free(p);
+
+    return 0;
+
+}
+
 static int mov_read_chpl(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 {
     int64_t start;
@@ -563,7 +582,7 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
         st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
     else if (type == MKTAG('m','1','a',' '))
         st->codec->codec_id = AV_CODEC_ID_MP2;
-    else if ((type == MKTAG('s','u','b','p')) || (type == MKTAG('c','l','c','p')))
+    else if ((type == MKTAG('s','u','b','p')) || (type == MKTAG('c','l','c','p')) || (type == MKTAG('s','u','b','m')) || (type == MKTAG('s','u','b','r')))
         st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
 
     avio_rb32(pb); /* component  manufacture */
@@ -1237,7 +1256,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
         }
         /* we cannot demux concatenated h264 streams because of different extradata */
         if (st->codec->codec_tag && st->codec->codec_tag == AV_RL32("avc1"))
-            av_log(c->fc, AV_LOG_WARNING, "Concatenated H.264 might not play corrently.\n");
+            av_log(c->fc, AV_LOG_WARNING, "Concatenated H.264 might not play correctly.\n");
         sc->pseudo_stream_id = st->codec->codec_tag ? -1 : pseudo_stream_id;
         sc->dref_id= dref_id;
 
@@ -1455,14 +1474,38 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
                 sc->sample_size = (bits_per_sample >> 3) * st->codec->channels;
             }
         } else if (st->codec->codec_type==AVMEDIA_TYPE_SUBTITLE){
-            // ttxt stsd contains display flags, justification, background
-            // color, fonts, and default styles, so fake an atom to read it
-            MOVAtom fake_atom = { .size = size - (avio_tell(pb) - start_pos) };
-            if (format != AV_RL32("mp4s")) // mp4s contains a regular esds atom
-                mov_read_glbl(c, pb, fake_atom);
-            st->codec->codec_id= id;
-            st->codec->width = sc->width;
-            st->codec->height = sc->height;
+            /* if we got the mett box there has to be the mime_format set, so we can select the appropriate codec for subtitles*/
+	    if(format == MKTAG('m','e','t','t'))
+	    {
+		MOVAtom mett_atom = { .size = size - (avio_tell(pb) - start_pos) };
+
+		mov_read_metadata_string(&st->metadata, pb, mett_atom, "content_encoding"); /* optional */
+		mov_read_metadata_string(&st->metadata, pb, mett_atom, "mime_format");
+
+		st->codec->codec_id = ff_codec_get_id_from_mime(ff_codec_movsubtitle_mimetypes,  ((AVDictionaryEntry *)av_dict_get(st->metadata, "mime_format", NULL, 0))->value);
+
+	    }else if(format == MKTAG('m','e','t','x'))
+	    {
+		MOVAtom metx_atom = { .size = size - (avio_tell(pb) - start_pos) };
+
+		mov_read_metadata_string(&st->metadata, pb, metx_atom, "content_encoding"); /* optional */
+		mov_read_metadata_string(&st->metadata, pb, metx_atom, "namespace");
+		mov_read_metadata_string(&st->metadata, pb, metx_atom, "schema_location");  /* optional */
+		
+		/* should identify codec by the namespace ... */
+
+	    }else
+	    {
+
+   	       // ttxt stsd contains display flags, justification, background
+               // color, fonts, and default styles, so fake an atom to read it
+       	       MOVAtom fake_atom = { .size = size - (avio_tell(pb) - start_pos) };
+               if (format != AV_RL32("mp4s")) // mp4s contains a regular esds atom
+                   mov_read_glbl(c, pb, fake_atom);
+               st->codec->codec_id= id;
+               st->codec->width = sc->width;
+               st->codec->height = sc->height;
+	   }
         } else {
             if (st->codec->codec_tag == MKTAG('t','m','c','d')) {
                 MOVStreamContext *tmcd_ctx = st->priv_data;
diff --git a/libavformat/riff.h b/libavformat/riff.h
index 9126e40..7f2466d 100644
--- a/libavformat/riff.h
+++ b/libavformat/riff.h
@@ -56,6 +56,7 @@ extern const AVCodecTag ff_codec_wav_tags[];
 
 unsigned int ff_codec_get_tag(const AVCodecTag *tags, enum AVCodecID id);
 enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag);
+enum AVCodecID ff_codec_get_id_from_mime(const CodecMime *mimes, char *mime);
 void ff_parse_specific_params(AVCodecContext *stream, int *au_rate, int *au_ssize, int *au_scale);
 
 typedef uint8_t ff_asf_guid[16];
diff --git a/libavformat/utils.c b/libavformat/utils.c
index 69ad761..130a7a9 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -2416,6 +2416,19 @@ enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag)
     return AV_CODEC_ID_NONE;
 }
 
+enum AVCodecID ff_codec_get_id_from_mime(const CodecMime *mimes, char *mime)
+{
+    int i;
+    for (i=0; mimes[i].id != AV_CODEC_ID_NONE; i++) {
+
+	if(av_strstart(mime, mimes[i].str, NULL) != 0) 
+         	return mimes[i].id;
+
+    }
+
+    return AV_CODEC_ID_NONE;
+}
+
 unsigned int av_codec_get_tag(const AVCodecTag * const *tags, enum AVCodecID id)
 {
     int i;
-- 
1.7.9.5



More information about the ffmpeg-devel mailing list