diff --git a/Changelog b/Changelog index e3c10f6e51..161f09349f 100644 --- a/Changelog +++ b/Changelog @@ -16,6 +16,7 @@ version : - Cook multichannel decoding support - introduced avlanguage helpers in libavformat - 8088flex TMV demuxer and decoder +- per-stream language-tags extraction in asfdec version 0.5: diff --git a/libavformat/Makefile b/libavformat/Makefile index b9e5eb7229..c0be725f86 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -18,7 +18,7 @@ OBJS-$(CONFIG_AMR_DEMUXER) += amr.o OBJS-$(CONFIG_AMR_MUXER) += amr.o OBJS-$(CONFIG_APC_DEMUXER) += apc.o OBJS-$(CONFIG_APE_DEMUXER) += ape.o -OBJS-$(CONFIG_ASF_DEMUXER) += asfdec.o asf.o asfcrypt.o riff.o +OBJS-$(CONFIG_ASF_DEMUXER) += asfdec.o asf.o asfcrypt.o riff.o avlanguage.o OBJS-$(CONFIG_ASF_MUXER) += asfenc.o asf.o riff.o OBJS-$(CONFIG_ASF_STREAM_MUXER) += asfenc.o asf.o riff.o OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o diff --git a/libavformat/asf.c b/libavformat/asf.c index 2e17554f03..79ef61444b 100644 --- a/libavformat/asf.c +++ b/libavformat/asf.c @@ -112,6 +112,9 @@ const ff_asf_guid ff_asf_my_guid = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +const ff_asf_guid ff_asf_language_guid = { + 0xa9, 0x46, 0x43, 0x7c, 0xe0, 0xef, 0xfc, 0x4b, 0xb2, 0x29, 0x39, 0x3e, 0xde, 0x41, 0x5c, 0x85 +}; const AVMetadataConv ff_asf_metadata_conv[] = { { "AlbumArtist", "artist" }, diff --git a/libavformat/asf.h b/libavformat/asf.h index 6d6619dcc8..f322ee2a58 100644 --- a/libavformat/asf.h +++ b/libavformat/asf.h @@ -42,6 +42,8 @@ typedef struct { int64_t packet_pos; + uint16_t stream_language_index; + } ASFStream; typedef uint8_t ff_asf_guid[16]; @@ -85,6 +87,7 @@ typedef struct { int asfid2avid[128]; ///< conversion table from asf ID 2 AVStream ID ASFStream streams[128]; ///< it's max number and it's not that big uint32_t stream_bitrates[128]; ///< max number of streams, bitrate for each (for streaming) + char stream_languages[128][6]; ///< max number of streams, language for each (RFC1766, e.g. en-US) /* 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 @@ -157,6 +160,7 @@ extern const ff_asf_guid ff_asf_ext_stream_embed_stream_header; extern const ff_asf_guid ff_asf_ext_stream_audio_stream; extern const ff_asf_guid ff_asf_metadata_header; extern const ff_asf_guid ff_asf_my_guid; +extern const ff_asf_guid ff_asf_language_guid; extern const AVMetadataConv ff_asf_metadata_conv[]; diff --git a/libavformat/asfdec.c b/libavformat/asfdec.c index e76f98beb2..3810663c2f 100644 --- a/libavformat/asfdec.c +++ b/libavformat/asfdec.c @@ -20,11 +20,13 @@ */ #include "libavutil/common.h" +#include "libavutil/avstring.h" #include "libavcodec/mpegaudio.h" #include "avformat.h" #include "riff.h" #include "asf.h" #include "asfcrypt.h" +#include "avlanguage.h" void ff_mms_set_stream_selection(URLContext *h, AVFormatContext *format); @@ -76,6 +78,7 @@ static void print_guid(const ff_asf_guid *g) else PRINT_IF_GUID(g, ff_asf_ext_stream_audio_stream); else PRINT_IF_GUID(g, ff_asf_metadata_header); else PRINT_IF_GUID(g, stream_bitrate_guid); + else PRINT_IF_GUID(g, ff_asf_language_guid); else dprintf(NULL, "(GUID: unknown) "); for(i=0;i<16;i++) @@ -238,6 +241,8 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap) st->priv_data = asf_st; start_time = asf->hdr.preroll; + asf_st->stream_language_index = 128; // invalid stream index means no language info + if(!(asf->hdr.flags & 0x01)) { // if we aren't streaming... st->duration = asf->hdr.send_time / (10000000 / 1000) - start_time; @@ -403,6 +408,16 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap) // av_log(s, AV_LOG_ERROR, "flags: 0x%x stream id %d, bitrate %d\n", flags, stream_id, bitrate); asf->stream_bitrates[stream_id]= bitrate; } + } else if (!guidcmp(&g, &ff_asf_language_guid)) { + int j; + int stream_count = get_le16(pb); + for(j = 0; j < stream_count; j++) { + char lang[6]; + unsigned int lang_len = get_byte(pb); + get_str16_nolen(pb, lang_len, lang, sizeof(lang)); + if (j < 128) + av_strlcpy(asf->stream_languages[j], lang, sizeof(*asf->stream_languages)); + } } else if (!guidcmp(&g, &ff_asf_extended_content_header)) { int desc_count, i; @@ -443,6 +458,7 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap) } else if (!guidcmp(&g, &ff_asf_ext_stream_header)) { int ext_len, payload_ext_ct, stream_ct; uint32_t ext_d, leak_rate, stream_num; + unsigned int stream_languageid_index; get_le64(pb); // starttime get_le64(pb); // endtime @@ -455,7 +471,11 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap) get_le32(pb); // max-object-size get_le32(pb); // flags (reliable,seekable,no_cleanpoints?,resend-live-cleanpoints, rest of bits reserved) stream_num = get_le16(pb); // stream-num - get_le16(pb); // stream-language-id-index + + stream_languageid_index = get_le16(pb); // stream-language-id-index + if (stream_num < 128) + asf->streams[stream_num].stream_language_index = stream_languageid_index; + get_le64(pb); // avg frametime in 100ns units stream_ct = get_le16(pb); //stream-name-count payload_ext_ct = get_le16(pb); //payload-extension-system-count @@ -535,6 +555,17 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap) &st->sample_aspect_ratio.den, dar[i].num, dar[i].den, INT_MAX); //av_log(s, AV_LOG_ERROR, "dar %d:%d sar=%d:%d\n", dar[i].num, dar[i].den, st->sample_aspect_ratio.num, st->sample_aspect_ratio.den); + + // copy and convert language codes to the frontend + if (asf->streams[i].stream_language_index < 128) { + const char *rfc1766 = asf->stream_languages[asf->streams[i].stream_language_index]; + if (rfc1766 && strlen(rfc1766) > 1) { + const char primary_tag[3] = { rfc1766[0], rfc1766[1], '\0' }; // ignore country code if any + const char *iso6392 = av_convert_lang_to(primary_tag, AV_LANG_ISO639_2_BIBL); + if (iso6392) + av_metadata_set(&st->metadata, "language", iso6392); + } + } } }