lavf/asfenc: add support for storing languages
authorMarton Balint <cus@passwd.hu>
Wed, 3 Feb 2016 02:42:50 +0000 (03:42 +0100)
committerMarton Balint <cus@passwd.hu>
Fri, 5 Feb 2016 20:32:14 +0000 (21:32 +0100)
Checked compatiblity with VLC, Windows Media Player 12 and Windows Media ASF
Viewer 9 series.

Reviewed-by: Michael Niedermayer <michael@niedermayer.cc>
Signed-off-by: Marton Balint <cus@passwd.hu>
libavformat/asf.c
libavformat/asf.h
libavformat/asfenc.c

index 80d24db..455ca4d 100644 (file)
@@ -143,6 +143,10 @@ const ff_asf_guid ff_asf_digital_signature = {
     0xfc, 0xb3, 0x11, 0x22, 0x23, 0xbd, 0xd2, 0x11, 0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e
 };
 
+const ff_asf_guid ff_asf_extended_stream_properties_object = {
+    0xcb, 0xa5, 0xe6, 0x14, 0x72, 0xc6, 0x32, 0x43, 0x83, 0x99, 0xa9, 0x69, 0x52, 0x06, 0x5b, 0x5a
+};
+
 /* List of official tags at http://msdn.microsoft.com/en-us/library/dd743066(VS.85).aspx */
 const AVMetadataConv ff_asf_metadata_conv[] = {
     { "WM/AlbumArtist",          "album_artist"     },
index f98fc46..1070293 100644 (file)
@@ -101,6 +101,7 @@ extern const ff_asf_guid ff_asf_language_guid;
 extern const ff_asf_guid ff_asf_content_encryption;
 extern const ff_asf_guid ff_asf_ext_content_encryption;
 extern const ff_asf_guid ff_asf_digital_signature;
+extern const ff_asf_guid ff_asf_extended_stream_properties_object;
 
 extern const AVMetadataConv ff_asf_metadata_conv[];
 
index 6932c11..e2aa2e0 100644 (file)
@@ -23,6 +23,7 @@
 #include "libavutil/dict.h"
 #include "libavutil/mathematics.h"
 #include "avformat.h"
+#include "avlanguage.h"
 #include "avio_internal.h"
 #include "internal.h"
 #include "riff.h"
@@ -220,6 +221,8 @@ typedef struct ASFContext {
     uint32_t seqno;
     int is_streamed;
     ASFStream streams[128];              ///< it's max number and it's not that big
+    const char *languages[128];
+    int nb_languages;
     /* non streamed additonnal info */
     uint64_t nb_packets;                 ///< how many packets are there in the file, invalid if broadcasting
     int64_t duration;                    ///< in 100ns units
@@ -403,6 +406,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
 
     bit_rate = 0;
     for (n = 0; n < s->nb_streams; n++) {
+        AVDictionaryEntry *entry;
         enc = s->streams[n]->codec;
 
         avpriv_set_pts_info(s->streams[n], 32, 1, 1000); /* 32 bit pts in ms */
@@ -412,6 +416,27 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
             && enc->sample_aspect_ratio.num > 0
             && enc->sample_aspect_ratio.den > 0)
             has_aspect_ratio++;
+
+        entry = av_dict_get(s->streams[n]->metadata, "language", NULL, 0);
+        if (entry) {
+            const char *iso6391lang = av_convert_lang_to(entry->value, AV_LANG_ISO639_1);
+            if (iso6391lang) {
+                int i;
+                for (i = 0; i < asf->nb_languages; i++) {
+                    if (!strcmp(asf->languages[i], iso6391lang)) {
+                        asf->streams[n].stream_language_index = i;
+                        break;
+                    }
+                }
+                if (i >= asf->nb_languages) {
+                    asf->languages[asf->nb_languages] = iso6391lang;
+                    asf->streams[n].stream_language_index = asf->nb_languages;
+                    asf->nb_languages++;
+                }
+            }
+        } else {
+            asf->streams[n].stream_language_index = 128;
+        }
     }
 
     if (asf->is_streamed) {
@@ -441,13 +466,48 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
     avio_wl32(pb, bit_rate ? bit_rate : -1); /* Maximum data rate in bps */
     end_header(pb, hpos);
 
-    /* unknown headers */
+    /* header_extension */
     hpos = put_header(pb, &ff_asf_head1_guid);
     ff_put_guid(pb, &ff_asf_head2_guid);
     avio_wl16(pb, 6);
+    avio_wl32(pb, 0); /* length, to be filled later */
+    if (asf->nb_languages) {
+        int64_t hpos2;
+        int i;
+
+        hpos2 = put_header(pb, &ff_asf_language_guid);
+        avio_wl16(pb, asf->nb_languages);
+        for (i = 0; i < asf->nb_languages; i++) {
+            avio_w8(pb, 6);
+            avio_put_str16le(pb, asf->languages[i]);
+        }
+        end_header(pb, hpos2);
+
+        for (n = 0; n < s->nb_streams; n++) {
+            int64_t es_pos;
+            if (asf->streams[n].stream_language_index > 127)
+                continue;
+            es_pos = put_header(pb, &ff_asf_extended_stream_properties_object);
+            avio_wl64(pb, 0); /* start time */
+            avio_wl64(pb, 0); /* end time */
+            avio_wl32(pb, s->streams[n]->codec->bit_rate); /* data bitrate bps */
+            avio_wl32(pb, 5000); /* buffer size ms */
+            avio_wl32(pb, 0); /* initial buffer fullness */
+            avio_wl32(pb, s->streams[n]->codec->bit_rate); /* peak data bitrate */
+            avio_wl32(pb, 5000); /* maximum buffer size ms */
+            avio_wl32(pb, 0); /* max initial buffer fullness */
+            avio_wl32(pb, 0); /* max object size */
+            avio_wl32(pb, (!asf->is_streamed && pb->seekable) << 1); /* flags - seekable */
+            avio_wl16(pb, n + 1); /* stream number */
+            avio_wl16(pb, asf->streams[n].stream_language_index); /* language id index */
+            avio_wl64(pb, 0); /* avg time per frame */
+            avio_wl16(pb, 0); /* stream name count */
+            avio_wl16(pb, 0); /* payload extension system count */
+            end_header(pb, es_pos);
+        }
+    }
     if (has_aspect_ratio) {
         int64_t hpos2;
-        avio_wl32(pb, 26 + has_aspect_ratio * 84);
         hpos2 = put_header(pb, &ff_asf_metadata_header);
         avio_wl16(pb, 2 * has_aspect_ratio);
         for (n = 0; n < s->nb_streams; n++) {
@@ -475,8 +535,13 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size,
             }
         }
         end_header(pb, hpos2);
-    } else {
-        avio_wl32(pb, 0);
+    }
+    {
+        int64_t pos1;
+        pos1 = avio_tell(pb);
+        avio_seek(pb, hpos + 42, SEEK_SET);
+        avio_wl32(pb, pos1 - hpos - 46);
+        avio_seek(pb, pos1, SEEK_SET);
     }
     end_header(pb, hpos);