Add Text Codec To Codec String

This change has the hls master playlist add the text codec string
to the list of codecs in a variant.

Bug: 72942756

Change-Id: Ib25bb2064a291d10d7b1d261a4307991df62220c
This commit is contained in:
Aaron Vaage 2018-02-26 12:13:28 -08:00
parent c7929b1505
commit 30b5fdf19f
13 changed files with 145 additions and 116 deletions

View File

@ -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-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 stream_2.m3u8

View File

@ -12,6 +12,7 @@
#include "packager/base/files/file_path.h" #include "packager/base/files/file_path.h"
#include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/string_util.h"
#include "packager/base/strings/stringprintf.h" #include "packager/base/strings/stringprintf.h"
#include "packager/file/file.h" #include "packager/file/file.h"
#include "packager/hls/base/media_playlist.h" #include "packager/hls/base/media_playlist.h"
@ -34,7 +35,8 @@ void AppendVersionString(std::string* content) {
} }
struct Variant { struct Variant {
std::string audio_codec; std::set<std::string> audio_codecs;
std::set<std::string> text_codecs;
const std::string* audio_group_id = nullptr; const std::string* audio_group_id = nullptr;
const std::string* text_group_id = nullptr; const std::string* text_group_id = nullptr;
uint64_t audio_bitrate = 0; uint64_t audio_bitrate = 0;
@ -48,10 +50,15 @@ uint64_t MaxBitrate(const std::list<const MediaPlaylist*> playlists) {
return max; return max;
} }
std::string GetAudioGroupCodecString( std::set<std::string> GetGroupCodecString(
const std::list<const MediaPlaylist*>& group) { const std::list<const MediaPlaylist*>& group) {
// TODO(vaage): Should be a concatenation of all the codecs in the group. std::set<std::string> codecs;
return group.front()->codec();
for (const MediaPlaylist* playlist : group) {
codecs.insert(playlist->codec());
}
return codecs;
} }
std::list<Variant> AudioGroupsToVariants( std::list<Variant> AudioGroupsToVariants(
@ -60,9 +67,9 @@ std::list<Variant> AudioGroupsToVariants(
for (const auto& group : groups) { for (const auto& group : groups) {
Variant variant; Variant variant;
variant.audio_codec = GetAudioGroupCodecString(group.second);
variant.audio_group_id = &group.first; variant.audio_group_id = &group.first;
variant.audio_bitrate = MaxBitrate(group.second); variant.audio_bitrate = MaxBitrate(group.second);
variant.audio_codecs = GetGroupCodecString(group.second);
variants.push_back(variant); variants.push_back(variant);
} }
@ -102,6 +109,7 @@ std::list<Variant> SubtitleGroupsToVariants(
for (const auto& group : groups) { for (const auto& group : groups) {
Variant variant; Variant variant;
variant.text_group_id = &group.first; variant.text_group_id = &group.first;
variant.text_codecs = GetGroupCodecString(group.second);
variants.push_back(variant); variants.push_back(variant);
} }
@ -131,7 +139,8 @@ std::list<Variant> BuildVariants(
for (const auto& audio_variant : audio_variants) { for (const auto& audio_variant : audio_variants) {
for (const auto& subtitle_variant : subtitle_variants) { for (const auto& subtitle_variant : subtitle_variants) {
Variant variant; 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.audio_group_id = audio_variant.audio_group_id;
variant.text_group_id = subtitle_variant.text_group_id; variant.text_group_id = subtitle_variant.text_group_id;
variant.audio_bitrate = audio_variant.audio_bitrate; variant.audio_bitrate = audio_variant.audio_bitrate;
@ -151,17 +160,6 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
std::string* out) { std::string* out) {
DCHECK(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; std::string tag_name;
switch (playlist.stream_type()) { switch (playlist.stream_type()) {
case MediaPlaylist::MediaPlaylistStreamType::kVideo: case MediaPlaylist::MediaPlaylistStreamType::kVideo:
@ -177,8 +175,20 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
} }
Tag tag(tag_name, out); Tag tag(tag_name, out);
const uint64_t bitrate = playlist.Bitrate() + variant.audio_bitrate;
tag.AddNumber("BANDWIDTH", bitrate); tag.AddNumber("BANDWIDTH", bitrate);
tag.AddQuotedString("CODECS", codecs);
std::vector<std::string> 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); tag.AddNumberPair("RESOLUTION", width, 'x', height);
if (variant.audio_group_id) { if (variant.audio_group_id) {

View File

@ -97,6 +97,7 @@ std::unique_ptr<MockMediaPlaylist> CreateTextPlaylist(
const std::string& filename, const std::string& filename,
const std::string& name, const std::string& name,
const std::string& group, const std::string& group,
const std::string& codec,
const std::string& language) { const std::string& language) {
std::unique_ptr<MockMediaPlaylist> playlist( std::unique_ptr<MockMediaPlaylist> playlist(
new MockMediaPlaylist(kVodPlaylist, filename, name, group)); new MockMediaPlaylist(kVodPlaylist, filename, name, group));
@ -104,6 +105,7 @@ std::unique_ptr<MockMediaPlaylist> CreateTextPlaylist(
EXPECT_CALL(*playlist, GetLanguage()).WillRepeatedly(Return(language)); EXPECT_CALL(*playlist, GetLanguage()).WillRepeatedly(Return(language));
playlist->SetStreamTypeForTesting( playlist->SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kSubtitle); MediaPlaylist::MediaPlaylistStreamType::kSubtitle);
playlist->SetCodecForTesting(codec);
return playlist; return playlist;
} }
@ -336,11 +338,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) {
// Text, eng.m3u8. // Text, eng.m3u8.
std::unique_ptr<MockMediaPlaylist> text_eng = std::unique_ptr<MockMediaPlaylist> text_eng =
CreateTextPlaylist("eng.m3u8", "english", "textgroup", "en"); CreateTextPlaylist("eng.m3u8", "english", "textgroup", "textcodec", "en");
// Text, fr.m3u8. // Text, fr.m3u8.
std::unique_ptr<MockMediaPlaylist> text_fr = std::unique_ptr<MockMediaPlaylist> text_fr =
CreateTextPlaylist("fr.m3u8", "french", "textgroup", "fr"); CreateTextPlaylist("fr.m3u8", "french", "textgroup", "textcodec", "fr");
const char kBaseUrl[] = "http://playlists.org/"; const char kBaseUrl[] = "http://playlists.org/";
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist( EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(
@ -361,10 +363,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) {
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/fr.m3u8\"," "#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/fr.m3u8\","
"GROUP-ID=\"textgroup\",LANGUAGE=\"fr\",NAME=\"french\",AUTOSELECT=YES\n" "GROUP-ID=\"textgroup\",LANGUAGE=\"fr\",NAME=\"french\",AUTOSELECT=YES\n"
"\n" "\n"
"#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"sdvideocodec,textcodec\","
"RESOLUTION=800x600,SUBTITLES=\"textgroup\"\n" "RESOLUTION=800x600,SUBTITLES=\"textgroup\"\n"
"http://playlists.org/sd.m3u8\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" "RESOLUTION=800x600,SUBTITLES=\"textgroup\"\n"
"http://playlists.org/hd.m3u8\n"; "http://playlists.org/hd.m3u8\n";
@ -377,12 +379,12 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) {
CreateVideoPlaylist("sd.m3u8", "sdvideocodec", 300000); CreateVideoPlaylist("sd.m3u8", "sdvideocodec", 300000);
// Text, eng.m3u8. // Text, eng.m3u8.
std::unique_ptr<MockMediaPlaylist> text_eng = std::unique_ptr<MockMediaPlaylist> text_eng = CreateTextPlaylist(
CreateTextPlaylist("eng.m3u8", "english", "en-text-group", "en"); "eng.m3u8", "english", "en-text-group", "textcodec", "en");
// Text, fr.m3u8. // Text, fr.m3u8.
std::unique_ptr<MockMediaPlaylist> text_fr = std::unique_ptr<MockMediaPlaylist> text_fr = CreateTextPlaylist(
CreateTextPlaylist("fr.m3u8", "french", "fr-text-group", "fr"); "fr.m3u8", "french", "fr-text-group", "textcodec", "fr");
const char kBaseUrl[] = "http://playlists.org/"; const char kBaseUrl[] = "http://playlists.org/";
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist( EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(
@ -404,11 +406,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) {
"GROUP-ID=\"fr-text-group\",LANGUAGE=\"fr\",NAME=\"french\"," "GROUP-ID=\"fr-text-group\",LANGUAGE=\"fr\",NAME=\"french\","
"AUTOSELECT=YES\n" "AUTOSELECT=YES\n"
"\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" "RESOLUTION=800x600,SUBTITLES=\"en-text-group\"\n"
"http://playlists.org/sd.m3u8\n" "http://playlists.org/sd.m3u8\n"
"\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" "RESOLUTION=800x600,SUBTITLES=\"fr-text-group\"\n"
"http://playlists.org/sd.m3u8\n"; "http://playlists.org/sd.m3u8\n";
@ -426,7 +428,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) {
// Text, english.m3u8. // Text, english.m3u8.
std::unique_ptr<MockMediaPlaylist> text = std::unique_ptr<MockMediaPlaylist> text =
CreateTextPlaylist("eng.m3u8", "english", "textgroup", "en"); CreateTextPlaylist("eng.m3u8", "english", "textgroup", "textcodec", "en");
const char kBaseUrl[] = "http://playlists.org/"; const char kBaseUrl[] = "http://playlists.org/";
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist( EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(
@ -448,8 +450,9 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) {
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\",DEFAULT=YES," "GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\",DEFAULT=YES,"
"AUTOSELECT=YES\n" "AUTOSELECT=YES\n"
"\n" "\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
"RESOLUTION=800x600,AUDIO=\"audiogroup\",SUBTITLES=\"textgroup\"\n" "textcodec\",RESOLUTION=800x600,AUDIO=\"audiogroup\",SUBTITLES="
"\"textgroup\"\n"
"http://playlists.org/sd.m3u8\n"; "http://playlists.org/sd.m3u8\n";
ASSERT_EQ(expected, actual); ASSERT_EQ(expected, actual);
@ -469,8 +472,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
"audiocodec", "en", kAudioChannels, kAudioBitRate), "audiocodec", "en", kAudioChannels, kAudioBitRate),
// SUBTITLES // SUBTITLES
CreateTextPlaylist("text-1.m3u8", "text 1", "text-group-1", "en"), CreateTextPlaylist("text-1.m3u8", "text 1", "text-group-1", "textcodec",
CreateTextPlaylist("text-2.m3u8", "text 2", "text-group-2", "en"), "en"),
CreateTextPlaylist("text-2.m3u8", "text 2", "text-group-2", "textcodec",
"en"),
// VIDEO // VIDEO
CreateVideoPlaylist("video-1.m3u8", "sdvideocodec", kVideoBitRate), CreateVideoPlaylist("video-1.m3u8", "sdvideocodec", kVideoBitRate),
@ -513,32 +518,40 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
"GROUP-ID=\"text-group-2\",LANGUAGE=\"en\",NAME=\"text 2\"," "GROUP-ID=\"text-group-2\",LANGUAGE=\"en\",NAME=\"text 2\","
"DEFAULT=YES,AUTOSELECT=YES\n" "DEFAULT=YES,AUTOSELECT=YES\n"
"\n" "\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
"RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-1\"\n" "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-"
"group-1\"\n"
"http://playlists.org/video-1.m3u8\n" "http://playlists.org/video-1.m3u8\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
"RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-1\"\n" "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-"
"group-1\"\n"
"http://playlists.org/video-2.m3u8\n" "http://playlists.org/video-2.m3u8\n"
"\n" "\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
"RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-2\"\n" "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-"
"group-2\"\n"
"http://playlists.org/video-1.m3u8\n" "http://playlists.org/video-1.m3u8\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
"RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-group-2\"\n" "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-1\",SUBTITLES=\"text-"
"group-2\"\n"
"http://playlists.org/video-2.m3u8\n" "http://playlists.org/video-2.m3u8\n"
"\n" "\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
"RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-1\"\n" "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-"
"group-1\"\n"
"http://playlists.org/video-1.m3u8\n" "http://playlists.org/video-1.m3u8\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
"RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-1\"\n" "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-"
"group-1\"\n"
"http://playlists.org/video-2.m3u8\n" "http://playlists.org/video-2.m3u8\n"
"\n" "\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
"RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-2\"\n" "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-"
"group-2\"\n"
"http://playlists.org/video-1.m3u8\n" "http://playlists.org/video-1.m3u8\n"
"#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=350000,CODECS=\"sdvideocodec,audiocodec,"
"RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-group-2\"\n" "textcodec\",RESOLUTION=800x600,AUDIO=\"audio-group-2\",SUBTITLES=\"text-"
"group-2\"\n"
"http://playlists.org/video-2.m3u8\n" "http://playlists.org/video-2.m3u8\n"
"\n" "\n"
"#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=100000,CODECS=\"sdvideocodec\"," "#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=100000,CODECS=\"sdvideocodec\","

View File

@ -342,7 +342,7 @@ bool MediaPlaylist::SetMediaInfo(const MediaInfo& media_info) {
codec_ = media_info.audio_info().codec(); codec_ = media_info.audio_info().codec();
} else { } else {
stream_type_ = MediaPlaylistStreamType::kSubtitle; stream_type_ = MediaPlaylistStreamType::kSubtitle;
codec_ = media_info.text_info().format(); codec_ = media_info.text_info().codec();
} }
time_scale_ = time_scale; time_scale_ = time_scale;

View File

@ -103,7 +103,7 @@ TEST_F(MediaPlaylistMultiSegmentTest, SetMediaInfoText) {
MediaInfo media_info; MediaInfo media_info;
media_info.set_reference_time_scale(kTimeScale); media_info.set_reference_time_scale(kTimeScale);
MediaInfo::TextInfo* text_info = media_info.mutable_text_info(); 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)); 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 // 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) { TEST_F(MediaPlaylistSingleSegmentTest, AddSegmentByteRange) {
const std::string kExpectedOutput = const std::string kExpectedOutput =
"#EXTM3U\n" "#EXTM3U\n"
@ -203,8 +204,7 @@ TEST_F(MediaPlaylistSingleSegmentTest, AddSegmentByteRange) {
valid_video_media_info_.mutable_init_range()->set_end(500); valid_video_media_info_.mutable_init_range()->set_end(500);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_)); ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddSegment("file.mp4", 0, 10 * kTimeScale, 1000, media_playlist_.AddSegment("file.mp4", 0, 10 * kTimeScale, 1000, 1 * kMBytes);
1 * kMBytes);
media_playlist_.AddSegment("file.mp4", 10 * kTimeScale, 10 * kTimeScale, media_playlist_.AddSegment("file.mp4", 10 * kTimeScale, 10 * kTimeScale,
1001000, 2 * kMBytes); 1001000, 2 * kMBytes);
@ -381,8 +381,8 @@ TEST_F(MediaPlaylistMultiSegmentTest, WriteToFileWithEncryptionInfoEmptyIv) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_)); ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes, 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, media_playlist_.AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
kMBytes); kMBytes);
media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale, media_playlist_.AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
@ -489,7 +489,8 @@ TEST_F(MediaPlaylistMultiSegmentTest, InitSegment) {
const char kExpectedOutput[] = const char kExpectedOutput[] =
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:6\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-TARGETDURATION:30\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n" "#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-MAP:URI=\"init_segment.mp4\"\n" "#EXT-X-MAP:URI=\"init_segment.mp4\"\n"

View File

@ -128,31 +128,22 @@ void AddAudioInfo(const AudioStreamInfo* audio_stream_info,
void AddTextInfo(const TextStreamInfo& text_stream_info, void AddTextInfo(const TextStreamInfo& text_stream_info,
MediaInfo* media_info) { MediaInfo* media_info) {
MediaInfo::TextInfo* text_info = media_info->mutable_text_info();
// For now, set everything as subtitle. // For now, set everything as subtitle.
MediaInfo::TextInfo* text_info = media_info->mutable_text_info();
text_info->set_type(MediaInfo::TextInfo::SUBTITLE); text_info->set_type(MediaInfo::TextInfo::SUBTITLE);
if (text_stream_info.codec_string() == "wvtt") { text_info->set_codec(text_stream_info.codec_string());
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_language(text_stream_info.language()); text_info->set_language(text_stream_info.language());
} }
void SetMediaInfoStreamInfo(const StreamInfo& stream_info, void SetMediaInfoStreamInfo(const StreamInfo& stream_info,
MediaInfo* media_info) { MediaInfo* media_info) {
if (stream_info.stream_type() == kStreamAudio) { if (stream_info.stream_type() == kStreamAudio) {
AddAudioInfo(static_cast<const AudioStreamInfo*>(&stream_info), AddAudioInfo(static_cast<const AudioStreamInfo*>(&stream_info), media_info);
media_info);
} else if (stream_info.stream_type() == kStreamText) { } else if (stream_info.stream_type() == kStreamText) {
AddTextInfo(static_cast<const TextStreamInfo&>(stream_info), media_info); AddTextInfo(static_cast<const TextStreamInfo&>(stream_info), media_info);
} else { } else {
DCHECK_EQ(stream_info.stream_type(), kStreamVideo); DCHECK_EQ(stream_info.stream_type(), kStreamVideo);
AddVideoInfo(static_cast<const VideoStreamInfo*>(&stream_info), AddVideoInfo(static_cast<const VideoStreamInfo*>(&stream_info), media_info);
media_info);
} }
if (stream_info.duration() > 0) { if (stream_info.duration() > 0) {
// |stream_info.duration()| contains the media duration from the original // |stream_info.duration()| contains the media duration from the original
@ -204,7 +195,6 @@ bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges,
return false; return false;
} }
if (media_ranges.init_range) { if (media_ranges.init_range) {
SetRange(media_ranges.init_range->start, media_ranges.init_range->end, SetRange(media_ranges.init_range->start, media_ranges.init_range->end,
media_info->mutable_init_range()); media_info->mutable_init_range());

View File

@ -109,7 +109,7 @@ TEST_F(AdaptationSetTest, CheckAdaptationSetAudioContentType) {
TEST_F(AdaptationSetTest, CheckAdaptationSetTextContentType) { TEST_F(AdaptationSetTest, CheckAdaptationSetTextContentType) {
const char kTextMediaInfo[] = const char kTextMediaInfo[] =
"text_info {\n" "text_info {\n"
" format: 'ttml'\n" " codec: 'ttml'\n"
" language: 'en'\n" " language: 'en'\n"
"}\n" "}\n"
"container_type: CONTAINER_TEXT\n"; "container_type: CONTAINER_TEXT\n";
@ -1072,7 +1072,7 @@ TEST_F(OnDemandAdaptationSetTest,
TEST_F(OnDemandAdaptationSetTest, Text) { TEST_F(OnDemandAdaptationSetTest, Text) {
const char kTextMediaInfo[] = const char kTextMediaInfo[] =
"text_info {\n" "text_info {\n"
" format: 'ttml'\n" " codec: 'ttml'\n"
" language: 'en'\n" " language: 'en'\n"
" type: SUBTITLE\n" " type: SUBTITLE\n"
"}\n" "}\n"

View File

@ -68,7 +68,7 @@ message MediaInfo {
CAPTION = 1; CAPTION = 1;
SUBTITLE = 2; SUBTITLE = 2;
} }
optional string format = 1; optional string codec = 1;
optional string language = 2; optional string language = 2;
optional TextType type = 3; optional TextType type = 3;
} }

View File

@ -30,20 +30,22 @@ bool IsKeyRotationDefaultKeyId(const std::string& key_id) {
std::string TextCodecString(const MediaInfo& media_info) { std::string TextCodecString(const MediaInfo& media_info) {
CHECK(media_info.has_text_info()); CHECK(media_info.has_text_info());
const std::string& format = media_info.text_info().format(); const auto container_type = media_info.container_type();
// DASH IOP mentions that the codec for ttml in mp4 is stpp.
if (format == "ttml" && // Codecs are not needed when mimeType is "text/*". Having a codec would be
(media_info.container_type() == MediaInfo::CONTAINER_MP4)) { // redundant.
return "stpp"; if (container_type == MediaInfo::CONTAINER_TEXT) {
} return "";
if (format == "vtt" &&
(media_info.container_type() == MediaInfo::CONTAINER_MP4)) {
return "wvtt";
} }
// Otherwise codec doesn't need to be specified, e.g. vtt and ttml+xml are // DASH IOP mentions that the codec for ttml in mp4 is stpp, so override
// obvious from the mime type. // the default codec value.
return ""; const std::string& codec = media_info.text_info().codec();
if (codec == "ttml" && container_type == MediaInfo::CONTAINER_MP4) {
return "stpp";
}
return codec;
} }
} // namespace } // namespace
@ -118,7 +120,7 @@ std::string GetBaseCodec(const MediaInfo& media_info) {
} else if (media_info.has_audio_info()) { } else if (media_info.has_audio_info()) {
codec = media_info.audio_info().codec(); codec = media_info.audio_info().codec();
} else if (media_info.has_text_info()) { } 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". // Convert, for example, "mp4a.40.2" to simply "mp4a".
// "mp4a.40.2" and "mp4a.40.5" can exist in the same AdaptationSet. // "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); 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) { 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 // Implement our own DoubleToString as base::DoubleToString uses third_party
@ -375,5 +379,4 @@ void AddContentProtectionElements(const MediaInfo& media_info,
AddContentProtectionElementsHelperTemplated(media_info, parent); AddContentProtectionElementsHelperTemplated(media_info, parent);
} }
} // namespace shaka } // namespace shaka

View File

@ -213,7 +213,7 @@ TEST_P(PeriodTest, SetDurationAndGetXml) {
TEST_P(PeriodTest, Text) { TEST_P(PeriodTest, Text) {
const char kTextMediaInfo[] = const char kTextMediaInfo[] =
"text_info {\n" "text_info {\n"
" format: 'ttml'\n" " codec: 'ttml'\n"
" language: 'en'\n" " language: 'en'\n"
"}\n" "}\n"
"container_type: CONTAINER_TEXT\n"; "container_type: CONTAINER_TEXT\n";

View File

@ -452,7 +452,7 @@ std::string Representation::GetAudioMimeType() const {
std::string Representation::GetTextMimeType() const { std::string Representation::GetTextMimeType() const {
CHECK(media_info_.has_text_info()); 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()) { switch (media_info_.container_type()) {
case MediaInfo::CONTAINER_TEXT: case MediaInfo::CONTAINER_TEXT:
return "application/ttml+xml"; return "application/ttml+xml";
@ -464,7 +464,7 @@ std::string Representation::GetTextMimeType() const {
return ""; return "";
} }
} }
if (media_info_.text_info().format() == "vtt") { if (media_info_.text_info().codec() == "wvtt") {
if (media_info_.container_type() == MediaInfo::CONTAINER_TEXT) { if (media_info_.container_type() == MediaInfo::CONTAINER_TEXT) {
return "text/vtt"; return "text/vtt";
} else if (media_info_.container_type() == MediaInfo::CONTAINER_MP4) { } 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: " LOG(ERROR) << "Cannot determine MIME type for format: "
<< media_info_.text_info().format() << media_info_.text_info().codec()
<< " container: " << media_info_.container_type(); << " container: " << media_info_.container_type();
return ""; return "";
} }

View File

@ -291,7 +291,7 @@ TEST_F(RepresentationTest,
TEST_F(RepresentationTest, TtmlXmlMimeType) { TEST_F(RepresentationTest, TtmlXmlMimeType) {
const char kTtmlXmlMediaInfo[] = const char kTtmlXmlMediaInfo[] =
"text_info {\n" "text_info {\n"
" format: 'ttml'\n" " codec: 'ttml'\n"
"}\n" "}\n"
"container_type: CONTAINER_TEXT\n"; "container_type: CONTAINER_TEXT\n";
@ -306,7 +306,7 @@ TEST_F(RepresentationTest, TtmlXmlMimeType) {
TEST_F(RepresentationTest, TtmlMp4MimeType) { TEST_F(RepresentationTest, TtmlMp4MimeType) {
const char kTtmlMp4MediaInfo[] = const char kTtmlMp4MediaInfo[] =
"text_info {\n" "text_info {\n"
" format: 'ttml'\n" " codec: 'ttml'\n"
"}\n" "}\n"
"container_type: CONTAINER_MP4\n"; "container_type: CONTAINER_MP4\n";
@ -321,7 +321,7 @@ TEST_F(RepresentationTest, TtmlMp4MimeType) {
TEST_F(RepresentationTest, WebVttMimeType) { TEST_F(RepresentationTest, WebVttMimeType) {
const char kWebVttMediaInfo[] = const char kWebVttMediaInfo[] =
"text_info {\n" "text_info {\n"
" format: 'vtt'\n" " codec: 'wvtt'\n"
"}\n" "}\n"
"container_type: CONTAINER_TEXT\n"; "container_type: CONTAINER_TEXT\n";

View File

@ -88,22 +88,32 @@ MuxerListenerFactory::StreamData ToMuxerListenerData(
// TODO(rkuroiwa): Write TTML and WebVTT parser (demuxing) for a better check // TODO(rkuroiwa): Write TTML and WebVTT parser (demuxing) for a better check
// and for supporting live/segmenting (muxing). With a demuxer and a muxer, // and for supporting live/segmenting (muxing). With a demuxer and a muxer,
// CreateAllJobs() shouldn't treat text as a special case. // 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; std::string content;
if (!File::ReadFileToString(file.c_str(), &content)) { if (!File::ReadFileToString(file.c_str(), &content)) {
LOG(ERROR) << "Failed to open file " << file LOG(ERROR) << "Failed to open file " << file
<< " to determine file format."; << " to determine file format.";
return ""; return false;
}
MediaContainerName container_name = DetermineContainer(
reinterpret_cast<const uint8_t*>(content.data()), content.size());
if (container_name == CONTAINER_WEBVTT) {
return "vtt";
} else if (container_name == CONTAINER_TTML) {
return "ttml";
} }
return ""; const uint8_t* content_data =
reinterpret_cast<const uint8_t*>(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) { MediaContainerName GetOutputFormat(const StreamDescriptor& descriptor) {
@ -278,14 +288,21 @@ class FakeClock : public base::Clock {
bool StreamInfoToTextMediaInfo(const StreamDescriptor& stream_descriptor, bool StreamInfoToTextMediaInfo(const StreamDescriptor& stream_descriptor,
MediaInfo* text_media_info) { MediaInfo* text_media_info) {
const std::string& language = stream_descriptor.language; std::string codec;
const std::string format = DetermineTextFileFormat(stream_descriptor.input); if (!DetermineTextFileCodec(stream_descriptor.input, &codec)) {
if (format.empty()) {
LOG(ERROR) << "Failed to determine the text file format for " LOG(ERROR) << "Failed to determine the text file format for "
<< stream_descriptor.input; << stream_descriptor.input;
return false; 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_media_file_name(stream_descriptor.output);
text_media_info->set_container_type(MediaInfo::CONTAINER_TEXT); 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); 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; return true;
} }