diff --git a/packager/app/test/testdata/hls-segmented-webvtt/output.m3u8 b/packager/app/test/testdata/hls-segmented-webvtt/output.m3u8 index 63d5fdfc9c..0fc9ccce34 100644 --- a/packager/app/test/testdata/hls-segmented-webvtt/output.m3u8 +++ b/packager/app/test/testdata/hls-segmented-webvtt/output.m3u8 @@ -5,5 +5,5 @@ #EXT-X-MEDIA:TYPE=SUBTITLES,URI="stream_0.m3u8",GROUP-ID="default-text-group",NAME="stream_0",AUTOSELECT=YES -#EXT-X-STREAM-INF:BANDWIDTH=1105163,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group",SUBTITLES="default-text-group" +#EXT-X-STREAM-INF:BANDWIDTH=1105163,CODECS="avc1.64001e,mp4a.40.2,wvtt",RESOLUTION=640x360,AUDIO="default-audio-group",SUBTITLES="default-text-group" stream_2.m3u8 diff --git a/packager/hls/base/master_playlist.cc b/packager/hls/base/master_playlist.cc index 27224dca17..9cbdf10dfa 100644 --- a/packager/hls/base/master_playlist.cc +++ b/packager/hls/base/master_playlist.cc @@ -12,6 +12,7 @@ #include "packager/base/files/file_path.h" #include "packager/base/strings/string_number_conversions.h" +#include "packager/base/strings/string_util.h" #include "packager/base/strings/stringprintf.h" #include "packager/file/file.h" #include "packager/hls/base/media_playlist.h" @@ -34,7 +35,8 @@ void AppendVersionString(std::string* content) { } struct Variant { - std::string audio_codec; + std::set audio_codecs; + std::set text_codecs; const std::string* audio_group_id = nullptr; const std::string* text_group_id = nullptr; uint64_t audio_bitrate = 0; @@ -48,10 +50,15 @@ uint64_t MaxBitrate(const std::list playlists) { return max; } -std::string GetAudioGroupCodecString( +std::set GetGroupCodecString( const std::list& group) { - // TODO(vaage): Should be a concatenation of all the codecs in the group. - return group.front()->codec(); + std::set codecs; + + for (const MediaPlaylist* playlist : group) { + codecs.insert(playlist->codec()); + } + + return codecs; } std::list AudioGroupsToVariants( @@ -60,9 +67,9 @@ std::list AudioGroupsToVariants( for (const auto& group : groups) { Variant variant; - variant.audio_codec = GetAudioGroupCodecString(group.second); variant.audio_group_id = &group.first; variant.audio_bitrate = MaxBitrate(group.second); + variant.audio_codecs = GetGroupCodecString(group.second); variants.push_back(variant); } @@ -102,6 +109,7 @@ std::list SubtitleGroupsToVariants( for (const auto& group : groups) { Variant variant; variant.text_group_id = &group.first; + variant.text_codecs = GetGroupCodecString(group.second); variants.push_back(variant); } @@ -131,7 +139,8 @@ std::list BuildVariants( for (const auto& audio_variant : audio_variants) { for (const auto& subtitle_variant : subtitle_variants) { Variant variant; - variant.audio_codec = audio_variant.audio_codec; + variant.audio_codecs = audio_variant.audio_codecs; + variant.text_codecs = subtitle_variant.text_codecs; variant.audio_group_id = audio_variant.audio_group_id; variant.text_group_id = subtitle_variant.text_group_id; variant.audio_bitrate = audio_variant.audio_bitrate; @@ -151,17 +160,6 @@ void BuildStreamInfTag(const MediaPlaylist& playlist, std::string* out) { DCHECK(out); - const uint64_t bitrate = playlist.Bitrate() + variant.audio_bitrate; - - uint32_t width; - uint32_t height; - CHECK(playlist.GetDisplayResolution(&width, &height)); - - std::string codecs = playlist.codec(); - if (!variant.audio_codec.empty()) { - base::StringAppendF(&codecs, ",%s", variant.audio_codec.c_str()); - } - std::string tag_name; switch (playlist.stream_type()) { case MediaPlaylist::MediaPlaylistStreamType::kVideo: @@ -177,8 +175,20 @@ void BuildStreamInfTag(const MediaPlaylist& playlist, } Tag tag(tag_name, out); + const uint64_t bitrate = playlist.Bitrate() + variant.audio_bitrate; tag.AddNumber("BANDWIDTH", bitrate); - tag.AddQuotedString("CODECS", codecs); + + std::vector all_codecs; + all_codecs.push_back(playlist.codec()); + all_codecs.insert(all_codecs.end(), variant.audio_codecs.begin(), + variant.audio_codecs.end()); + all_codecs.insert(all_codecs.end(), variant.text_codecs.begin(), + variant.text_codecs.end()); + tag.AddQuotedString("CODECS", base::JoinString(all_codecs, ",")); + + uint32_t width; + uint32_t height; + CHECK(playlist.GetDisplayResolution(&width, &height)); tag.AddNumberPair("RESOLUTION", width, 'x', height); if (variant.audio_group_id) { diff --git a/packager/hls/base/master_playlist_unittest.cc b/packager/hls/base/master_playlist_unittest.cc index 748fe3cad3..c1ef3dbd8c 100644 --- a/packager/hls/base/master_playlist_unittest.cc +++ b/packager/hls/base/master_playlist_unittest.cc @@ -97,6 +97,7 @@ std::unique_ptr CreateTextPlaylist( const std::string& filename, const std::string& name, const std::string& group, + const std::string& codec, const std::string& language) { std::unique_ptr playlist( new MockMediaPlaylist(kVodPlaylist, filename, name, group)); @@ -104,6 +105,7 @@ std::unique_ptr CreateTextPlaylist( EXPECT_CALL(*playlist, GetLanguage()).WillRepeatedly(Return(language)); playlist->SetStreamTypeForTesting( MediaPlaylist::MediaPlaylistStreamType::kSubtitle); + playlist->SetCodecForTesting(codec); return playlist; } @@ -336,11 +338,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) { // Text, eng.m3u8. std::unique_ptr text_eng = - CreateTextPlaylist("eng.m3u8", "english", "textgroup", "en"); + CreateTextPlaylist("eng.m3u8", "english", "textgroup", "textcodec", "en"); // Text, fr.m3u8. std::unique_ptr text_fr = - CreateTextPlaylist("fr.m3u8", "french", "textgroup", "fr"); + CreateTextPlaylist("fr.m3u8", "french", "textgroup", "textcodec", "fr"); const char kBaseUrl[] = "http://playlists.org/"; EXPECT_TRUE(master_playlist_.WriteMasterPlaylist( @@ -361,10 +363,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) { "#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/fr.m3u8\"," "GROUP-ID=\"textgroup\",LANGUAGE=\"fr\",NAME=\"french\",AUTOSELECT=YES\n" "\n" - "#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec\"," + "#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec,textcodec\"," "RESOLUTION=800x600,SUBTITLES=\"textgroup\"\n" "http://playlists.org/sd.m3u8\n" - "#EXT-X-STREAM-INF:BANDWIDTH=600000,CODECS=\"sdvideocodec\"," + "#EXT-X-STREAM-INF:BANDWIDTH=600000,CODECS=\"sdvideocodec,textcodec\"," "RESOLUTION=800x600,SUBTITLES=\"textgroup\"\n" "http://playlists.org/hd.m3u8\n"; @@ -377,12 +379,12 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) { CreateVideoPlaylist("sd.m3u8", "sdvideocodec", 300000); // Text, eng.m3u8. - std::unique_ptr text_eng = - CreateTextPlaylist("eng.m3u8", "english", "en-text-group", "en"); + std::unique_ptr text_eng = CreateTextPlaylist( + "eng.m3u8", "english", "en-text-group", "textcodec", "en"); // Text, fr.m3u8. - std::unique_ptr text_fr = - CreateTextPlaylist("fr.m3u8", "french", "fr-text-group", "fr"); + std::unique_ptr text_fr = CreateTextPlaylist( + "fr.m3u8", "french", "fr-text-group", "textcodec", "fr"); const char kBaseUrl[] = "http://playlists.org/"; EXPECT_TRUE(master_playlist_.WriteMasterPlaylist( @@ -404,11 +406,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) { "GROUP-ID=\"fr-text-group\",LANGUAGE=\"fr\",NAME=\"french\"," "AUTOSELECT=YES\n" "\n" - "#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec\"," + "#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec,textcodec\"," "RESOLUTION=800x600,SUBTITLES=\"en-text-group\"\n" "http://playlists.org/sd.m3u8\n" "\n" - "#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec\"," + "#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec,textcodec\"," "RESOLUTION=800x600,SUBTITLES=\"fr-text-group\"\n" "http://playlists.org/sd.m3u8\n"; @@ -426,7 +428,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) { // Text, english.m3u8. std::unique_ptr text = - CreateTextPlaylist("eng.m3u8", "english", "textgroup", "en"); + CreateTextPlaylist("eng.m3u8", "english", "textgroup", "textcodec", "en"); const char kBaseUrl[] = "http://playlists.org/"; EXPECT_TRUE(master_playlist_.WriteMasterPlaylist( @@ -448,8 +450,9 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) { "GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\",DEFAULT=YES," "AUTOSELECT=YES\n" "\n" - "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," - "RESOLUTION=800x600,AUDIO=\"audiogroup\",SUBTITLES=\"textgroup\"\n" + "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec," + "textcodec\",RESOLUTION=800x600,AUDIO=\"audiogroup\",SUBTITLES=" + "\"textgroup\"\n" "http://playlists.org/sd.m3u8\n"; ASSERT_EQ(expected, actual); @@ -469,8 +472,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) { "audiocodec", "en", kAudioChannels, kAudioBitRate), // SUBTITLES - CreateTextPlaylist("text-1.m3u8", "text 1", "text-group-1", "en"), - CreateTextPlaylist("text-2.m3u8", "text 2", "text-group-2", "en"), + CreateTextPlaylist("text-1.m3u8", "text 1", "text-group-1", "textcodec", + "en"), + CreateTextPlaylist("text-2.m3u8", "text 2", "text-group-2", "textcodec", + "en"), // VIDEO CreateVideoPlaylist("video-1.m3u8", "sdvideocodec", kVideoBitRate), @@ -513,32 +518,40 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) { "GROUP-ID=\"text-group-2\",LANGUAGE=\"en\",NAME=\"text 2\"," "DEFAULT=YES,AUTOSELECT=YES\n" "\n" - "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," - "RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-1\"\n" + "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec," + "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-" + "group-1\"\n" "http://playlists.org/video-1.m3u8\n" - "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," - "RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-1\"\n" + "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec," + "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-" + "group-1\"\n" "http://playlists.org/video-2.m3u8\n" "\n" - "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," - "RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-2\"\n" + "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec," + "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-" + "group-2\"\n" "http://playlists.org/video-1.m3u8\n" - "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," - "RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-2\"\n" + "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec," + "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-" + "group-2\"\n" "http://playlists.org/video-2.m3u8\n" "\n" - "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," - "RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-1\"\n" + "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec," + "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-" + "group-1\"\n" "http://playlists.org/video-1.m3u8\n" - "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," - "RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-1\"\n" + "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec," + "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-" + "group-1\"\n" "http://playlists.org/video-2.m3u8\n" "\n" - "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," - "RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-2\"\n" + "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec," + "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-" + "group-2\"\n" "http://playlists.org/video-1.m3u8\n" - "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," - "RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-2\"\n" + "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec," + "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-" + "group-2\"\n" "http://playlists.org/video-2.m3u8\n" "\n" "#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=100000,CODECS=\"sdvideocodec\"," diff --git a/packager/hls/base/media_playlist.cc b/packager/hls/base/media_playlist.cc index ffddeaeca3..e82bb2d562 100644 --- a/packager/hls/base/media_playlist.cc +++ b/packager/hls/base/media_playlist.cc @@ -342,7 +342,7 @@ bool MediaPlaylist::SetMediaInfo(const MediaInfo& media_info) { codec_ = media_info.audio_info().codec(); } else { stream_type_ = MediaPlaylistStreamType::kSubtitle; - codec_ = media_info.text_info().format(); + codec_ = media_info.text_info().codec(); } time_scale_ = time_scale; diff --git a/packager/hls/base/media_playlist_unittest.cc b/packager/hls/base/media_playlist_unittest.cc index a481a57c76..07363d87cb 100644 --- a/packager/hls/base/media_playlist_unittest.cc +++ b/packager/hls/base/media_playlist_unittest.cc @@ -103,7 +103,7 @@ TEST_F(MediaPlaylistMultiSegmentTest, SetMediaInfoText) { MediaInfo media_info; media_info.set_reference_time_scale(kTimeScale); MediaInfo::TextInfo* text_info = media_info.mutable_text_info(); - text_info->set_format("vtt"); + text_info->set_codec("wvtt"); EXPECT_TRUE(media_playlist_.SetMediaInfo(media_info)); } @@ -181,7 +181,8 @@ TEST_F(MediaPlaylistSingleSegmentTest, InitRangeWithOffset) { } // Closest to the normal use case where there is an init range and then -// subsegment ranges. There is index range between the subsegment and init range. +// subsegment ranges. There is index range between the subsegment and init +// range. TEST_F(MediaPlaylistSingleSegmentTest, AddSegmentByteRange) { const std::string kExpectedOutput = "#EXTM3U\n" @@ -203,8 +204,7 @@ TEST_F(MediaPlaylistSingleSegmentTest, AddSegmentByteRange) { valid_video_media_info_.mutable_init_range()->set_end(500); ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_)); - media_playlist_.AddSegment("file.mp4", 0, 10 * kTimeScale, 1000, - 1 * kMBytes); + media_playlist_.AddSegment("file.mp4", 0, 10 * kTimeScale, 1000, 1 * kMBytes); media_playlist_.AddSegment("file.mp4", 10 * kTimeScale, 10 * kTimeScale, 1001000, 2 * kMBytes); @@ -381,8 +381,8 @@ TEST_F(MediaPlaylistMultiSegmentTest, WriteToFileWithEncryptionInfoEmptyIv) { ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_)); media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes, - "http://example.com", "", "", "com.widevine", - ""); + "http://example.com", "", "", + "com.widevine", ""); media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset, kMBytes); media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale, @@ -489,7 +489,8 @@ TEST_F(MediaPlaylistMultiSegmentTest, InitSegment) { const char kExpectedOutput[] = "#EXTM3U\n" "#EXT-X-VERSION:6\n" - "## Generated with https://github.com/google/shaka-packager version test\n" + "## Generated with https://github.com/google/shaka-packager version " + "test\n" "#EXT-X-TARGETDURATION:30\n" "#EXT-X-PLAYLIST-TYPE:VOD\n" "#EXT-X-MAP:URI=\"init_segment.mp4\"\n" diff --git a/packager/media/event/muxer_listener_internal.cc b/packager/media/event/muxer_listener_internal.cc index 04a4e1a19e..85dd06d5f2 100644 --- a/packager/media/event/muxer_listener_internal.cc +++ b/packager/media/event/muxer_listener_internal.cc @@ -128,31 +128,22 @@ void AddAudioInfo(const AudioStreamInfo* audio_stream_info, void AddTextInfo(const TextStreamInfo& text_stream_info, MediaInfo* media_info) { - MediaInfo::TextInfo* text_info = media_info->mutable_text_info(); // For now, set everything as subtitle. + MediaInfo::TextInfo* text_info = media_info->mutable_text_info(); text_info->set_type(MediaInfo::TextInfo::SUBTITLE); - if (text_stream_info.codec_string() == "wvtt") { - text_info->set_format("vtt"); - } else { - LOG(WARNING) << "Unhandled codec " << text_stream_info.codec_string() - << " copying it as format."; - text_info->set_format(text_stream_info.codec_string()); - } - + text_info->set_codec(text_stream_info.codec_string()); text_info->set_language(text_stream_info.language()); } void SetMediaInfoStreamInfo(const StreamInfo& stream_info, MediaInfo* media_info) { if (stream_info.stream_type() == kStreamAudio) { - AddAudioInfo(static_cast(&stream_info), - media_info); + AddAudioInfo(static_cast(&stream_info), media_info); } else if (stream_info.stream_type() == kStreamText) { AddTextInfo(static_cast(stream_info), media_info); } else { DCHECK_EQ(stream_info.stream_type(), kStreamVideo); - AddVideoInfo(static_cast(&stream_info), - media_info); + AddVideoInfo(static_cast(&stream_info), media_info); } if (stream_info.duration() > 0) { // |stream_info.duration()| contains the media duration from the original @@ -204,7 +195,6 @@ bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges, return false; } - if (media_ranges.init_range) { SetRange(media_ranges.init_range->start, media_ranges.init_range->end, media_info->mutable_init_range()); diff --git a/packager/mpd/base/adaptation_set_unittest.cc b/packager/mpd/base/adaptation_set_unittest.cc index bce79e37be..a453e8596e 100644 --- a/packager/mpd/base/adaptation_set_unittest.cc +++ b/packager/mpd/base/adaptation_set_unittest.cc @@ -109,7 +109,7 @@ TEST_F(AdaptationSetTest, CheckAdaptationSetAudioContentType) { TEST_F(AdaptationSetTest, CheckAdaptationSetTextContentType) { const char kTextMediaInfo[] = "text_info {\n" - " format: 'ttml'\n" + " codec: 'ttml'\n" " language: 'en'\n" "}\n" "container_type: CONTAINER_TEXT\n"; @@ -1072,7 +1072,7 @@ TEST_F(OnDemandAdaptationSetTest, TEST_F(OnDemandAdaptationSetTest, Text) { const char kTextMediaInfo[] = "text_info {\n" - " format: 'ttml'\n" + " codec: 'ttml'\n" " language: 'en'\n" " type: SUBTITLE\n" "}\n" diff --git a/packager/mpd/base/media_info.proto b/packager/mpd/base/media_info.proto index b6c0a28982..97720d27f6 100644 --- a/packager/mpd/base/media_info.proto +++ b/packager/mpd/base/media_info.proto @@ -68,7 +68,7 @@ message MediaInfo { CAPTION = 1; SUBTITLE = 2; } - optional string format = 1; + optional string codec = 1; optional string language = 2; optional TextType type = 3; } diff --git a/packager/mpd/base/mpd_utils.cc b/packager/mpd/base/mpd_utils.cc index a86c7f1c39..afe6bb42a5 100644 --- a/packager/mpd/base/mpd_utils.cc +++ b/packager/mpd/base/mpd_utils.cc @@ -30,20 +30,22 @@ bool IsKeyRotationDefaultKeyId(const std::string& key_id) { std::string TextCodecString(const MediaInfo& media_info) { CHECK(media_info.has_text_info()); - const std::string& format = media_info.text_info().format(); - // DASH IOP mentions that the codec for ttml in mp4 is stpp. - if (format == "ttml" && - (media_info.container_type() == MediaInfo::CONTAINER_MP4)) { - return "stpp"; - } - if (format == "vtt" && - (media_info.container_type() == MediaInfo::CONTAINER_MP4)) { - return "wvtt"; + const auto container_type = media_info.container_type(); + + // Codecs are not needed when mimeType is "text/*". Having a codec would be + // redundant. + if (container_type == MediaInfo::CONTAINER_TEXT) { + return ""; } - // Otherwise codec doesn't need to be specified, e.g. vtt and ttml+xml are - // obvious from the mime type. - return ""; + // DASH IOP mentions that the codec for ttml in mp4 is stpp, so override + // the default codec value. + const std::string& codec = media_info.text_info().codec(); + if (codec == "ttml" && container_type == MediaInfo::CONTAINER_MP4) { + return "stpp"; + } + + return codec; } } // namespace @@ -118,7 +120,7 @@ std::string GetBaseCodec(const MediaInfo& media_info) { } else if (media_info.has_audio_info()) { codec = media_info.audio_info().codec(); } else if (media_info.has_text_info()) { - codec = media_info.text_info().format(); + codec = media_info.text_info().codec(); } // Convert, for example, "mp4a.40.2" to simply "mp4a". // "mp4a.40.2" and "mp4a.40.5" can exist in the same AdaptationSet. @@ -186,10 +188,12 @@ bool MoreThanOneTrue(bool b1, bool b2, bool b3) { return (b1 && b2) || (b2 && b3) || (b3 && b1); } -bool AtLeastOneTrue(bool b1, bool b2, bool b3) { return b1 || b2 || b3; } +bool AtLeastOneTrue(bool b1, bool b2, bool b3) { + return b1 || b2 || b3; +} bool OnlyOneTrue(bool b1, bool b2, bool b3) { - return !MoreThanOneTrue(b1, b2, b3) && AtLeastOneTrue(b1, b2, b3); + return !MoreThanOneTrue(b1, b2, b3) && AtLeastOneTrue(b1, b2, b3); } // Implement our own DoubleToString as base::DoubleToString uses third_party @@ -375,5 +379,4 @@ void AddContentProtectionElements(const MediaInfo& media_info, AddContentProtectionElementsHelperTemplated(media_info, parent); } - } // namespace shaka diff --git a/packager/mpd/base/period_unittest.cc b/packager/mpd/base/period_unittest.cc index 649f6fbf14..9813f040fb 100644 --- a/packager/mpd/base/period_unittest.cc +++ b/packager/mpd/base/period_unittest.cc @@ -213,7 +213,7 @@ TEST_P(PeriodTest, SetDurationAndGetXml) { TEST_P(PeriodTest, Text) { const char kTextMediaInfo[] = "text_info {\n" - " format: 'ttml'\n" + " codec: 'ttml'\n" " language: 'en'\n" "}\n" "container_type: CONTAINER_TEXT\n"; diff --git a/packager/mpd/base/representation.cc b/packager/mpd/base/representation.cc index edd04ab4a3..8870c0a862 100644 --- a/packager/mpd/base/representation.cc +++ b/packager/mpd/base/representation.cc @@ -452,7 +452,7 @@ std::string Representation::GetAudioMimeType() const { std::string Representation::GetTextMimeType() const { CHECK(media_info_.has_text_info()); - if (media_info_.text_info().format() == "ttml") { + if (media_info_.text_info().codec() == "ttml") { switch (media_info_.container_type()) { case MediaInfo::CONTAINER_TEXT: return "application/ttml+xml"; @@ -464,7 +464,7 @@ std::string Representation::GetTextMimeType() const { return ""; } } - if (media_info_.text_info().format() == "vtt") { + if (media_info_.text_info().codec() == "wvtt") { if (media_info_.container_type() == MediaInfo::CONTAINER_TEXT) { return "text/vtt"; } else if (media_info_.container_type() == MediaInfo::CONTAINER_MP4) { @@ -476,7 +476,7 @@ std::string Representation::GetTextMimeType() const { } LOG(ERROR) << "Cannot determine MIME type for format: " - << media_info_.text_info().format() + << media_info_.text_info().codec() << " container: " << media_info_.container_type(); return ""; } diff --git a/packager/mpd/base/representation_unittest.cc b/packager/mpd/base/representation_unittest.cc index 537a40f327..aa0a9bad06 100644 --- a/packager/mpd/base/representation_unittest.cc +++ b/packager/mpd/base/representation_unittest.cc @@ -291,7 +291,7 @@ TEST_F(RepresentationTest, TEST_F(RepresentationTest, TtmlXmlMimeType) { const char kTtmlXmlMediaInfo[] = "text_info {\n" - " format: 'ttml'\n" + " codec: 'ttml'\n" "}\n" "container_type: CONTAINER_TEXT\n"; @@ -306,7 +306,7 @@ TEST_F(RepresentationTest, TtmlXmlMimeType) { TEST_F(RepresentationTest, TtmlMp4MimeType) { const char kTtmlMp4MediaInfo[] = "text_info {\n" - " format: 'ttml'\n" + " codec: 'ttml'\n" "}\n" "container_type: CONTAINER_MP4\n"; @@ -321,7 +321,7 @@ TEST_F(RepresentationTest, TtmlMp4MimeType) { TEST_F(RepresentationTest, WebVttMimeType) { const char kWebVttMediaInfo[] = "text_info {\n" - " format: 'vtt'\n" + " codec: 'wvtt'\n" "}\n" "container_type: CONTAINER_TEXT\n"; diff --git a/packager/packager.cc b/packager/packager.cc index 6afad69d72..4b5b0e5846 100644 --- a/packager/packager.cc +++ b/packager/packager.cc @@ -88,22 +88,32 @@ MuxerListenerFactory::StreamData ToMuxerListenerData( // TODO(rkuroiwa): Write TTML and WebVTT parser (demuxing) for a better check // and for supporting live/segmenting (muxing). With a demuxer and a muxer, // CreateAllJobs() shouldn't treat text as a special case. -std::string DetermineTextFileFormat(const std::string& file) { +bool DetermineTextFileCodec(const std::string& file, std::string* out) { + CHECK(out); + std::string content; if (!File::ReadFileToString(file.c_str(), &content)) { LOG(ERROR) << "Failed to open file " << file << " to determine file format."; - return ""; - } - MediaContainerName container_name = DetermineContainer( - reinterpret_cast(content.data()), content.size()); - if (container_name == CONTAINER_WEBVTT) { - return "vtt"; - } else if (container_name == CONTAINER_TTML) { - return "ttml"; + return false; } - return ""; + const uint8_t* content_data = + reinterpret_cast(content.data()); + MediaContainerName container_name = + DetermineContainer(content_data, content.size()); + + if (container_name == CONTAINER_WEBVTT) { + *out = "wvtt"; + return true; + } + + if (container_name == CONTAINER_TTML) { + *out = "ttml"; + return true; + } + + return false; } MediaContainerName GetOutputFormat(const StreamDescriptor& descriptor) { @@ -278,14 +288,21 @@ class FakeClock : public base::Clock { bool StreamInfoToTextMediaInfo(const StreamDescriptor& stream_descriptor, MediaInfo* text_media_info) { - const std::string& language = stream_descriptor.language; - const std::string format = DetermineTextFileFormat(stream_descriptor.input); - if (format.empty()) { + std::string codec; + if (!DetermineTextFileCodec(stream_descriptor.input, &codec)) { LOG(ERROR) << "Failed to determine the text file format for " << stream_descriptor.input; return false; } + MediaInfo::TextInfo* text_info = text_media_info->mutable_text_info(); + text_info->set_codec(codec); + + const std::string& language = stream_descriptor.language; + if (!language.empty()) { + text_info->set_language(language); + } + text_media_info->set_media_file_name(stream_descriptor.output); text_media_info->set_container_type(MediaInfo::CONTAINER_TEXT); @@ -299,11 +316,6 @@ bool StreamInfoToTextMediaInfo(const StreamDescriptor& stream_descriptor, text_media_info->set_bandwidth(kDefaultTextBandwidth); } - MediaInfo::TextInfo* text_info = text_media_info->mutable_text_info(); - text_info->set_format(format); - if (!language.empty()) - text_info->set_language(language); - return true; }