Add support for --default_text_language
It allows users to override the default language for text tracks. If not specified, --default_language applies to both audio and text tracks. Issue #430. Change-Id: I86a9baba2072be27b6661fa7b65a8bc8b6adb3cc
This commit is contained in:
parent
273ab09f05
commit
4b97a6d8a2
|
@ -64,6 +64,14 @@ DASH options
|
|||
<Role ... value=\"main\" /> in the manifest. This allows the player to
|
||||
choose the correct default language for the content.
|
||||
|
||||
This applies to both audio and text tracks. The default language for text
|
||||
tracks can be overriden by 'default_text_language'.
|
||||
|
||||
--default_text_language <text_language>
|
||||
|
||||
Same as above, but this applies to text tracks only, and overrides the
|
||||
default language for text tracks.
|
||||
|
||||
--allow_approximate_segment_timeline
|
||||
|
||||
For live profile only.
|
||||
|
|
|
@ -43,3 +43,11 @@ HLS options
|
|||
The first audio/text rendition in a group tagged with this language will
|
||||
have 'DEFAULT' attribute set to 'YES'. This allows the player to choose the
|
||||
correct default language for the content.
|
||||
|
||||
This applies to both audio and text tracks. The default language for text
|
||||
tracks can be overriden by 'default_text_language'.
|
||||
|
||||
--default_text_language <text_language>
|
||||
|
||||
Same as above, but this applies to text tracks only, and overrides the
|
||||
default language for text tracks.
|
||||
|
|
|
@ -25,4 +25,11 @@ DEFINE_string(default_language,
|
|||
"have <Role ... value=\"main\" /> in the manifest; For HLS, the "
|
||||
"first audio/text rendition in a group tagged with this language "
|
||||
"will have 'DEFAULT' attribute set to 'YES'. This allows the "
|
||||
"player to choose the correct default language for the content.");
|
||||
"player to choose the correct default language for the content."
|
||||
"This applies to both audio and text tracks. The default "
|
||||
"language for text tracks can be overriden by "
|
||||
"'--default_text_language'.");
|
||||
DEFINE_string(default_text_language,
|
||||
"",
|
||||
"Same as above, but this applies to text tracks only, and "
|
||||
"overrides the default language for text tracks.");
|
||||
|
|
|
@ -14,5 +14,6 @@
|
|||
DECLARE_double(time_shift_buffer_depth);
|
||||
DECLARE_uint64(preserved_segments_outside_live_window);
|
||||
DECLARE_string(default_language);
|
||||
DECLARE_string(default_text_language);
|
||||
|
||||
#endif // PACKAGER_APP_MANIFEST_FLAGS_H_
|
||||
|
|
|
@ -443,6 +443,7 @@ base::Optional<PackagingParams> GetPackagingParams() {
|
|||
}
|
||||
|
||||
mpd_params.default_language = FLAGS_default_language;
|
||||
mpd_params.default_text_language = FLAGS_default_text_language;
|
||||
mpd_params.generate_static_live_mpd = FLAGS_generate_static_mpd;
|
||||
mpd_params.generate_dash_if_iop_compliant_mpd =
|
||||
FLAGS_generate_dash_if_iop_compliant_mpd;
|
||||
|
@ -460,6 +461,7 @@ base::Optional<PackagingParams> GetPackagingParams() {
|
|||
hls_params.preserved_segments_outside_live_window =
|
||||
FLAGS_preserved_segments_outside_live_window;
|
||||
hls_params.default_language = FLAGS_default_language;
|
||||
hls_params.default_text_language = FLAGS_default_text_language;
|
||||
|
||||
TestParams& test_params = packaging_params.test_params;
|
||||
test_params.dump_stream_info = FLAGS_dump_stream_info;
|
||||
|
|
|
@ -343,7 +343,8 @@ void BuildMediaTags(
|
|||
}
|
||||
}
|
||||
|
||||
void AppendPlaylists(const std::string& default_language,
|
||||
void AppendPlaylists(const std::string& default_audio_language,
|
||||
const std::string& default_text_language,
|
||||
const std::string& base_url,
|
||||
const std::list<MediaPlaylist*>& playlists,
|
||||
std::string* content) {
|
||||
|
@ -374,12 +375,13 @@ void AppendPlaylists(const std::string& default_language,
|
|||
|
||||
if (!audio_playlist_groups.empty()) {
|
||||
content->append("\n");
|
||||
BuildMediaTags(audio_playlist_groups, default_language, base_url, content);
|
||||
BuildMediaTags(audio_playlist_groups, default_audio_language, base_url,
|
||||
content);
|
||||
}
|
||||
|
||||
if (!subtitle_playlist_groups.empty()) {
|
||||
content->append("\n");
|
||||
BuildMediaTags(subtitle_playlist_groups, default_language, base_url,
|
||||
BuildMediaTags(subtitle_playlist_groups, default_text_language, base_url,
|
||||
content);
|
||||
}
|
||||
|
||||
|
@ -406,8 +408,12 @@ void AppendPlaylists(const std::string& default_language,
|
|||
} // namespace
|
||||
|
||||
MasterPlaylist::MasterPlaylist(const std::string& file_name,
|
||||
const std::string& default_language)
|
||||
: file_name_(file_name), default_language_(default_language) {}
|
||||
const std::string& default_audio_language,
|
||||
const std::string& default_text_language)
|
||||
: file_name_(file_name),
|
||||
default_audio_language_(default_audio_language),
|
||||
default_text_language_(default_text_language) {}
|
||||
|
||||
MasterPlaylist::~MasterPlaylist() {}
|
||||
|
||||
bool MasterPlaylist::WriteMasterPlaylist(
|
||||
|
@ -416,7 +422,8 @@ bool MasterPlaylist::WriteMasterPlaylist(
|
|||
const std::list<MediaPlaylist*>& playlists) {
|
||||
std::string content = "#EXTM3U\n";
|
||||
AppendVersionString(&content);
|
||||
AppendPlaylists(default_language_, base_url, playlists, &content);
|
||||
AppendPlaylists(default_audio_language_, default_text_language_, base_url,
|
||||
playlists, &content);
|
||||
|
||||
// Skip if the playlist is already written.
|
||||
if (content == written_playlist_)
|
||||
|
|
|
@ -20,10 +20,13 @@ class MediaPlaylist;
|
|||
class MasterPlaylist {
|
||||
public:
|
||||
/// @param file_name is the file name of the master playlist.
|
||||
/// @param default_language determines the rendition that should be tagged
|
||||
/// with 'DEFAULT'.
|
||||
/// @param default_audio_language determines the audio rendition that should
|
||||
/// be tagged with 'DEFAULT'.
|
||||
/// @param default_text_language determines the text rendition that should be
|
||||
/// tagged with 'DEFAULT'.
|
||||
MasterPlaylist(const std::string& file_name,
|
||||
const std::string& default_language);
|
||||
const std::string& default_audio_language,
|
||||
const std::string& default_text_language);
|
||||
virtual ~MasterPlaylist();
|
||||
|
||||
/// Writes Master Playlist to output_dir + <name of playlist>.
|
||||
|
@ -45,7 +48,8 @@ class MasterPlaylist {
|
|||
|
||||
std::string written_playlist_;
|
||||
const std::string file_name_;
|
||||
const std::string default_language_;
|
||||
const std::string default_audio_language_;
|
||||
const std::string default_text_language_;
|
||||
};
|
||||
|
||||
} // namespace hls
|
||||
|
|
|
@ -28,7 +28,8 @@ using ::testing::StrEq;
|
|||
|
||||
namespace {
|
||||
const char kDefaultMasterPlaylistName[] = "playlist.m3u8";
|
||||
const char kDefaultLanguage[] = "en";
|
||||
const char kDefaultAudioLanguage[] = "en";
|
||||
const char kDefaultTextLanguage[] = "fr";
|
||||
const uint32_t kWidth = 800;
|
||||
const uint32_t kHeight = 600;
|
||||
|
||||
|
@ -123,7 +124,9 @@ std::unique_ptr<MockMediaPlaylist> CreateTextPlaylist(
|
|||
class MasterPlaylistTest : public ::testing::Test {
|
||||
protected:
|
||||
MasterPlaylistTest()
|
||||
: master_playlist_(kDefaultMasterPlaylistName, kDefaultLanguage),
|
||||
: master_playlist_(kDefaultMasterPlaylistName,
|
||||
kDefaultAudioLanguage,
|
||||
kDefaultTextLanguage),
|
||||
test_output_dir_("memory://test_dir"),
|
||||
master_playlist_path_(
|
||||
FilePath::FromUTF8Unsafe(test_output_dir_)
|
||||
|
@ -382,10 +385,11 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) {
|
|||
"test\n"
|
||||
"\n"
|
||||
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/eng.m3u8\","
|
||||
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\",DEFAULT=YES,"
|
||||
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\","
|
||||
"AUTOSELECT=YES\n"
|
||||
"#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\",DEFAULT=YES,"
|
||||
"AUTOSELECT=YES\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=300000,AVERAGE-BANDWIDTH=200000,"
|
||||
"CODECS=\"sdvideocodec,textcodec\",RESOLUTION=800x600,"
|
||||
|
@ -423,8 +427,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextWithCharacteritics) {
|
|||
"test\n"
|
||||
"\n"
|
||||
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/eng.m3u8\","
|
||||
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\",DEFAULT=YES,"
|
||||
"AUTOSELECT=YES,CHARACTERISTICS=\""
|
||||
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\",AUTOSELECT=YES,"
|
||||
"CHARACTERISTICS=\""
|
||||
"public.accessibility.transcribes-spoken-dialog,public.easy-to-read\"\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=300000,AVERAGE-BANDWIDTH=200000,"
|
||||
|
@ -463,10 +467,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) {
|
|||
"\n"
|
||||
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/eng.m3u8\","
|
||||
"GROUP-ID=\"en-text-group\",LANGUAGE=\"en\",NAME=\"english\","
|
||||
"DEFAULT=YES,AUTOSELECT=YES\n"
|
||||
"AUTOSELECT=YES\n"
|
||||
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/fr.m3u8\","
|
||||
"GROUP-ID=\"fr-text-group\",LANGUAGE=\"fr\",NAME=\"french\","
|
||||
"AUTOSELECT=YES\n"
|
||||
"DEFAULT=YES,AUTOSELECT=YES\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=300000,AVERAGE-BANDWIDTH=200000,"
|
||||
"CODECS=\"sdvideocodec,textcodec\",RESOLUTION=800x600,"
|
||||
|
@ -511,7 +515,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) {
|
|||
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"2\"\n"
|
||||
"\n"
|
||||
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/eng.m3u8\","
|
||||
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\",DEFAULT=YES,"
|
||||
"GROUP-ID=\"textgroup\",LANGUAGE=\"en\",NAME=\"english\","
|
||||
"AUTOSELECT=YES\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=230000,"
|
||||
|
@ -537,14 +541,14 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
|
|||
"audiocodec", "en", kAudioChannels, kAudioMaxBitrate,
|
||||
kAudioAvgBitrate),
|
||||
CreateAudioPlaylist("audio-2.m3u8", "audio 2", "audio-group-2",
|
||||
"audiocodec", "en", kAudioChannels, kAudioMaxBitrate,
|
||||
"audiocodec", "fr", kAudioChannels, kAudioMaxBitrate,
|
||||
kAudioAvgBitrate),
|
||||
|
||||
// SUBTITLES
|
||||
CreateTextPlaylist("text-1.m3u8", "text 1", "text-group-1", "textcodec",
|
||||
"en"),
|
||||
CreateTextPlaylist("text-2.m3u8", "text 2", "text-group-2", "textcodec",
|
||||
"en"),
|
||||
"fr"),
|
||||
|
||||
// VIDEO
|
||||
CreateVideoPlaylist("video-1.m3u8", "sdvideocodec", kVideoMaxBitrate,
|
||||
|
@ -581,14 +585,14 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
|
|||
"GROUP-ID=\"audio-group-1\",LANGUAGE=\"en\",NAME=\"audio 1\","
|
||||
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"2\"\n"
|
||||
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://playlists.org/audio-2.m3u8\","
|
||||
"GROUP-ID=\"audio-group-2\",LANGUAGE=\"en\",NAME=\"audio 2\","
|
||||
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"2\"\n"
|
||||
"GROUP-ID=\"audio-group-2\",LANGUAGE=\"fr\",NAME=\"audio 2\","
|
||||
"AUTOSELECT=YES,CHANNELS=\"2\"\n"
|
||||
"\n"
|
||||
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/text-1.m3u8\","
|
||||
"GROUP-ID=\"text-group-1\",LANGUAGE=\"en\",NAME=\"text 1\","
|
||||
"DEFAULT=YES,AUTOSELECT=YES\n"
|
||||
"AUTOSELECT=YES\n"
|
||||
"#EXT-X-MEDIA:TYPE=SUBTITLES,URI=\"http://playlists.org/text-2.m3u8\","
|
||||
"GROUP-ID=\"text-group-2\",LANGUAGE=\"en\",NAME=\"text 2\","
|
||||
"GROUP-ID=\"text-group-2\",LANGUAGE=\"fr\",NAME=\"text 2\","
|
||||
"DEFAULT=YES,AUTOSELECT=YES\n"
|
||||
"\n"
|
||||
"#EXT-X-STREAM-INF:BANDWIDTH=350000,AVERAGE-BANDWIDTH=130000,"
|
||||
|
|
|
@ -282,9 +282,14 @@ SimpleHlsNotifier::SimpleHlsNotifier(const HlsParams& hls_params)
|
|||
const base::FilePath master_playlist_path(
|
||||
base::FilePath::FromUTF8Unsafe(hls_params.master_playlist_output));
|
||||
output_dir_ = master_playlist_path.DirName().AsUTF8Unsafe();
|
||||
const std::string& default_audio_langauge = hls_params.default_language;
|
||||
const std::string& default_text_language =
|
||||
hls_params.default_text_language.empty()
|
||||
? hls_params.default_language
|
||||
: hls_params.default_text_language;
|
||||
master_playlist_.reset(
|
||||
new MasterPlaylist(master_playlist_path.BaseName().AsUTF8Unsafe(),
|
||||
hls_params.default_language));
|
||||
default_audio_langauge, default_text_language));
|
||||
}
|
||||
|
||||
SimpleHlsNotifier::~SimpleHlsNotifier() {}
|
||||
|
|
|
@ -35,7 +35,8 @@ using ::testing::WithParamInterface;
|
|||
|
||||
namespace {
|
||||
const char kMasterPlaylistName[] = "master.m3u8";
|
||||
const char kDefaultLanguage[] = "en";
|
||||
const char kDefaultAudioLanguage[] = "en";
|
||||
const char kDefaultTextLanguage[] = "fr";
|
||||
const char kEmptyKeyUri[] = "";
|
||||
const char kFairPlayKeyUri[] = "skd://www.license.com/getkey?key_id=testing";
|
||||
const char kIdentityKeyUri[] = "https://www.license.com/getkey?key_id=testing";
|
||||
|
@ -45,7 +46,9 @@ const HlsPlaylistType kLivePlaylist = HlsPlaylistType::kLive;
|
|||
class MockMasterPlaylist : public MasterPlaylist {
|
||||
public:
|
||||
MockMasterPlaylist()
|
||||
: MasterPlaylist(kMasterPlaylistName, kDefaultLanguage) {}
|
||||
: MasterPlaylist(kMasterPlaylistName,
|
||||
kDefaultAudioLanguage,
|
||||
kDefaultTextLanguage) {}
|
||||
|
||||
MOCK_METHOD3(WriteMasterPlaylist,
|
||||
bool(const std::string& prefix,
|
||||
|
|
|
@ -45,7 +45,12 @@ struct HlsParams {
|
|||
/// The renditions tagged with this language will have 'DEFAULT' set to 'YES'
|
||||
/// in 'EXT-X-MEDIA' tag. This allows the player to choose the correct default
|
||||
/// language for the content.
|
||||
/// This applies to both audio and text tracks. The default language for text
|
||||
/// tracks can be overriden by 'default_text_language'.
|
||||
std::string default_language;
|
||||
/// Same as above, but this overrides the default language for text tracks,
|
||||
/// i.e. subtitles or close-captions.
|
||||
std::string default_text_language;
|
||||
/// This is the target segment duration requested by the user. The actual
|
||||
/// segment duration may be different to the target segment duration.
|
||||
/// This parameter is included here to for bandwidth estimator to exclude the
|
||||
|
|
|
@ -32,6 +32,16 @@ std::set<std::string> GetUUIDs(
|
|||
return uuids;
|
||||
}
|
||||
|
||||
const std::string& GetDefaultAudioLanguage(const MpdOptions& mpd_options) {
|
||||
return mpd_options.mpd_params.default_language;
|
||||
}
|
||||
|
||||
const std::string& GetDefaultTextLanguage(const MpdOptions& mpd_options) {
|
||||
return mpd_options.mpd_params.default_text_language.empty()
|
||||
? mpd_options.mpd_params.default_language
|
||||
: mpd_options.mpd_params.default_text_language;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Period::Period(uint32_t period_id,
|
||||
|
@ -149,8 +159,14 @@ bool Period::SetNewAdaptationSetAttributes(
|
|||
const MediaInfo& media_info,
|
||||
const std::list<AdaptationSet*>& adaptation_sets,
|
||||
AdaptationSet* new_adaptation_set) {
|
||||
if (!language.empty() && language == mpd_options_.mpd_params.default_language)
|
||||
new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
|
||||
if (!language.empty()) {
|
||||
const bool is_main_role =
|
||||
language == (media_info.has_audio_info()
|
||||
? GetDefaultAudioLanguage(mpd_options_)
|
||||
: GetDefaultTextLanguage(mpd_options_));
|
||||
if (is_main_role)
|
||||
new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
|
||||
}
|
||||
|
||||
if (media_info.has_video_info()) {
|
||||
// Because 'language' is ignored for videos, |adaptation_sets| must have
|
||||
|
|
|
@ -93,15 +93,13 @@ class TestablePeriod : public Period {
|
|||
|
||||
} // namespace
|
||||
|
||||
class PeriodTest : public ::testing::TestWithParam<bool> {
|
||||
class PeriodTest : public ::testing::Test {
|
||||
public:
|
||||
PeriodTest()
|
||||
: testable_period_(mpd_options_),
|
||||
default_adaptation_set_(new StrictMock<MockAdaptationSet>()),
|
||||
default_adaptation_set_ptr_(default_adaptation_set_.get()) {}
|
||||
|
||||
void SetUp() override { content_protection_in_adaptation_set_ = GetParam(); }
|
||||
|
||||
protected:
|
||||
MpdOptions mpd_options_;
|
||||
TestablePeriod testable_period_;
|
||||
|
@ -112,7 +110,7 @@ class PeriodTest : public ::testing::TestWithParam<bool> {
|
|||
StrictMock<MockAdaptationSet>* default_adaptation_set_ptr_;
|
||||
};
|
||||
|
||||
TEST_P(PeriodTest, GetXml) {
|
||||
TEST_F(PeriodTest, GetXml) {
|
||||
const char kVideoMediaInfo[] =
|
||||
"video_info {\n"
|
||||
" codec: 'avc1'\n"
|
||||
|
@ -143,7 +141,7 @@ TEST_P(PeriodTest, GetXml) {
|
|||
XmlNodeEqual(kExpectedXml));
|
||||
}
|
||||
|
||||
TEST_P(PeriodTest, DynamicMpdGetXml) {
|
||||
TEST_F(PeriodTest, DynamicMpdGetXml) {
|
||||
const char kVideoMediaInfo[] =
|
||||
"video_info {\n"
|
||||
" codec: 'avc1'\n"
|
||||
|
@ -175,7 +173,7 @@ TEST_P(PeriodTest, DynamicMpdGetXml) {
|
|||
XmlNodeEqual(kExpectedXml));
|
||||
}
|
||||
|
||||
TEST_P(PeriodTest, SetDurationAndGetXml) {
|
||||
TEST_F(PeriodTest, SetDurationAndGetXml) {
|
||||
const char kVideoMediaInfo[] =
|
||||
"video_info {\n"
|
||||
" codec: 'avc1'\n"
|
||||
|
@ -217,7 +215,7 @@ TEST_P(PeriodTest, SetDurationAndGetXml) {
|
|||
}
|
||||
|
||||
// Verify ForceSetSegmentAlignment is called.
|
||||
TEST_P(PeriodTest, Text) {
|
||||
TEST_F(PeriodTest, Text) {
|
||||
const char kTextMediaInfo[] =
|
||||
"text_info {\n"
|
||||
" codec: 'ttml'\n"
|
||||
|
@ -235,7 +233,7 @@ TEST_P(PeriodTest, Text) {
|
|||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
TEST_P(PeriodTest, TrickPlayWithMatchingAdaptationSet) {
|
||||
TEST_F(PeriodTest, TrickPlayWithMatchingAdaptationSet) {
|
||||
const char kVideoMediaInfo[] =
|
||||
"video_info {\n"
|
||||
" codec: 'avc1'\n"
|
||||
|
@ -282,7 +280,7 @@ TEST_P(PeriodTest, TrickPlayWithMatchingAdaptationSet) {
|
|||
}
|
||||
|
||||
// Verify no AdaptationSet is returned on trickplay media info.
|
||||
TEST_P(PeriodTest, TrickPlayWithNoMatchingAdaptationSet) {
|
||||
TEST_F(PeriodTest, TrickPlayWithNoMatchingAdaptationSet) {
|
||||
const char kVideoMediaInfo[] =
|
||||
"video_info {\n"
|
||||
" codec: 'avc1'\n"
|
||||
|
@ -324,11 +322,338 @@ TEST_P(PeriodTest, TrickPlayWithNoMatchingAdaptationSet) {
|
|||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
// Don't put different audio languages or codecs in the same AdaptationSet.
|
||||
TEST_F(PeriodTest, SplitAdaptationSetsByLanguageAndCodec) {
|
||||
const char kAacEnglishAudioContent[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'eng'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
const char kAacGermanAudioContent[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'ger'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
const char kVorbisGermanAudioContent1[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'vorbis'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'ger'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_WEBM\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
const char kVorbisGermanAudioContent2[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'vorbis'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'ger'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_WEBM\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> aac_eng_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* aac_eng_adaptation_set_ptr = aac_eng_adaptation_set.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> aac_ger_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* aac_ger_adaptation_set_ptr = aac_ger_adaptation_set.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> vorbis_german_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* vorbis_german_adaptation_set_ptr = vorbis_german_adaptation_set.get();
|
||||
|
||||
// We expect three AdaptationSets.
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(aac_eng_adaptation_set))))
|
||||
.WillOnce(Return(ByMove(std::move(aac_ger_adaptation_set))))
|
||||
.WillOnce(Return(ByMove(std::move(vorbis_german_adaptation_set))));
|
||||
|
||||
ASSERT_EQ(aac_eng_adaptation_set_ptr,
|
||||
testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kAacEnglishAudioContent),
|
||||
content_protection_in_adaptation_set_));
|
||||
ASSERT_EQ(aac_ger_adaptation_set_ptr,
|
||||
testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kAacGermanAudioContent),
|
||||
content_protection_in_adaptation_set_));
|
||||
ASSERT_EQ(vorbis_german_adaptation_set_ptr,
|
||||
testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kVorbisGermanAudioContent1),
|
||||
content_protection_in_adaptation_set_));
|
||||
// The same AdaptationSet is returned.
|
||||
ASSERT_EQ(vorbis_german_adaptation_set_ptr,
|
||||
testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kVorbisGermanAudioContent2),
|
||||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
TEST_F(PeriodTest, GetAdaptationSets) {
|
||||
const char kContent1[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'eng'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
const char kContent2[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'ger'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_1(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_1_ptr = adaptation_set_1.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_2(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_2_ptr = adaptation_set_2.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_1))))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_2))));
|
||||
|
||||
ASSERT_EQ(adaptation_set_1_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent1),
|
||||
content_protection_in_adaptation_set_));
|
||||
EXPECT_THAT(testable_period_.GetAdaptationSets(),
|
||||
ElementsAre(adaptation_set_1_ptr));
|
||||
|
||||
ASSERT_EQ(adaptation_set_2_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent2),
|
||||
content_protection_in_adaptation_set_));
|
||||
EXPECT_THAT(testable_period_.GetAdaptationSets(),
|
||||
ElementsAre(adaptation_set_1_ptr, adaptation_set_2_ptr));
|
||||
}
|
||||
|
||||
TEST_F(PeriodTest, OrderedByAdaptationSetId) {
|
||||
const char kContent1[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'eng'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
const char kContent2[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'ger'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_1(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_1_ptr = adaptation_set_1.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_2(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_2_ptr = adaptation_set_2.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_1))))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_2))));
|
||||
|
||||
ASSERT_EQ(adaptation_set_1_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent1),
|
||||
content_protection_in_adaptation_set_));
|
||||
ASSERT_EQ(adaptation_set_2_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent2),
|
||||
content_protection_in_adaptation_set_));
|
||||
|
||||
adaptation_set_1_ptr->set_id(2);
|
||||
adaptation_set_2_ptr->set_id(1);
|
||||
const char kExpectedXml[] =
|
||||
R"(<Period id="9">)"
|
||||
// ContentType and Representation elements are populated after
|
||||
// Representation::Init() is called.
|
||||
R"( <AdaptationSet id="1" contentType=""/>)"
|
||||
R"( <AdaptationSet id="2" contentType=""/>)"
|
||||
R"(</Period>)";
|
||||
EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration).get(),
|
||||
XmlNodeEqual(kExpectedXml));
|
||||
}
|
||||
|
||||
TEST_F(PeriodTest, AudioAdaptationSetDefaultLanguage) {
|
||||
mpd_options_.mpd_params.default_language = "en";
|
||||
const char kEnglishAudioContent[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'en'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_ptr = adaptation_set.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
||||
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
||||
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kEnglishAudioContent),
|
||||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
TEST_F(PeriodTest, AudioAdaptationSetNonDefaultLanguage) {
|
||||
mpd_options_.mpd_params.default_language = "fr";
|
||||
const char kEnglishAudioContent[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'en'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_ptr = adaptation_set.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
||||
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain)).Times(0);
|
||||
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kEnglishAudioContent),
|
||||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
TEST_F(PeriodTest, TextAdaptationSetDefaultLanguage) {
|
||||
mpd_options_.mpd_params.default_language = "en";
|
||||
const char kEnglishTextContent[] =
|
||||
"text_info {\n"
|
||||
" codec: 'webvtt'\n"
|
||||
" language: 'en'\n"
|
||||
" type: SUBTITLE\n"
|
||||
"}";
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_ptr = adaptation_set.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
||||
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
||||
EXPECT_CALL(*adaptation_set_ptr, ForceSetSegmentAlignment(true));
|
||||
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kEnglishTextContent),
|
||||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
TEST_F(PeriodTest, TextAdaptationSetNonDefaultLanguage) {
|
||||
mpd_options_.mpd_params.default_language = "fr";
|
||||
const char kEnglishTextContent[] =
|
||||
"text_info {\n"
|
||||
" codec: 'webvtt'\n"
|
||||
" language: 'en'\n"
|
||||
" type: SUBTITLE\n"
|
||||
"}";
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_ptr = adaptation_set.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
||||
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain)).Times(0);
|
||||
EXPECT_CALL(*adaptation_set_ptr, ForceSetSegmentAlignment(true));
|
||||
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kEnglishTextContent),
|
||||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
TEST_F(PeriodTest, TextAdaptationSetNonDefaultLanguageButDefaultTextLanguage) {
|
||||
mpd_options_.mpd_params.default_language = "fr";
|
||||
mpd_options_.mpd_params.default_text_language = "en";
|
||||
const char kEnglishTextContent[] =
|
||||
"text_info {\n"
|
||||
" codec: 'webvtt'\n"
|
||||
" language: 'en'\n"
|
||||
" type: SUBTITLE\n"
|
||||
"}";
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_ptr = adaptation_set.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
||||
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain));
|
||||
EXPECT_CALL(*adaptation_set_ptr, ForceSetSegmentAlignment(true));
|
||||
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kEnglishTextContent),
|
||||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
TEST_F(PeriodTest, TextAdaptationSetDefaultLanguageButNonDefaultTextLanguage) {
|
||||
mpd_options_.mpd_params.default_language = "en";
|
||||
mpd_options_.mpd_params.default_text_language = "fr";
|
||||
const char kEnglishTextContent[] =
|
||||
"text_info {\n"
|
||||
" codec: 'webvtt'\n"
|
||||
" language: 'en'\n"
|
||||
" type: SUBTITLE\n"
|
||||
"}";
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_ptr = adaptation_set.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set))));
|
||||
EXPECT_CALL(*adaptation_set_ptr, AddRole(AdaptationSet::kRoleMain)).Times(0);
|
||||
EXPECT_CALL(*adaptation_set_ptr, ForceSetSegmentAlignment(true));
|
||||
ASSERT_EQ(adaptation_set_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kEnglishTextContent),
|
||||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
class PeriodTestWithContentProtection
|
||||
: public PeriodTest,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
void SetUp() override { content_protection_in_adaptation_set_ = GetParam(); }
|
||||
};
|
||||
|
||||
// With content_protection_adaptation_set_ == true, verify with different
|
||||
// MediaInfo::ProtectedContent, two AdaptationSets should be created.
|
||||
// AdaptationSets with different DRM won't be switchable.
|
||||
// Otherwise, only one AdaptationSet is created.
|
||||
TEST_P(PeriodTest, DifferentProtectedContent) {
|
||||
TEST_P(PeriodTestWithContentProtection, DifferentProtectedContent) {
|
||||
// Note they both have different (bogus) pssh, like real use case.
|
||||
// default Key ID = _default_key_id_
|
||||
const char kSdProtectedContent[] =
|
||||
|
@ -435,7 +760,7 @@ TEST_P(PeriodTest, DifferentProtectedContent) {
|
|||
// Verify with the same MediaInfo::ProtectedContent, only one AdaptationSets
|
||||
// should be created regardless of the value of
|
||||
// content_protection_in_adaptation_set_.
|
||||
TEST_P(PeriodTest, SameProtectedContent) {
|
||||
TEST_P(PeriodTestWithContentProtection, SameProtectedContent) {
|
||||
// These have the same default key ID and PSSH.
|
||||
const char kSdProtectedContent[] =
|
||||
"video_info {\n"
|
||||
|
@ -527,7 +852,7 @@ TEST_P(PeriodTest, SameProtectedContent) {
|
|||
// 3. Add a 4k protected content. This should also make a new AdaptationSet.
|
||||
// It should be switchable with SD/HD AdaptationSet.
|
||||
// Otherwise only one AdaptationSet is created.
|
||||
TEST_P(PeriodTest, SetAdaptationSetSwitching) {
|
||||
TEST_P(PeriodTestWithContentProtection, SetAdaptationSetSwitching) {
|
||||
// These have the same default key ID and PSSH.
|
||||
const char kSdProtectedContent[] =
|
||||
"video_info {\n"
|
||||
|
@ -668,7 +993,8 @@ TEST_P(PeriodTest, SetAdaptationSetSwitching) {
|
|||
|
||||
// Even if the UUIDs match, video and audio AdaptationSets should not be
|
||||
// switchable.
|
||||
TEST_P(PeriodTest, DoNotSetAdaptationSetSwitchingIfContentTypesDifferent) {
|
||||
TEST_P(PeriodTestWithContentProtection,
|
||||
DoNotSetAdaptationSetSwitchingIfContentTypesDifferent) {
|
||||
// These have the same default key ID and PSSH.
|
||||
const char kVideoContent[] =
|
||||
"video_info {\n"
|
||||
|
@ -745,192 +1071,8 @@ TEST_P(PeriodTest, DoNotSetAdaptationSetSwitchingIfContentTypesDifferent) {
|
|||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
// Don't put different audio languages or codecs in the same AdaptationSet.
|
||||
TEST_P(PeriodTest, SplitAdaptationSetsByLanguageAndCodec) {
|
||||
const char kAacEnglishAudioContent[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'eng'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
const char kAacGermanAudioContent[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'ger'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
const char kVorbisGermanAudioContent1[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'vorbis'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'ger'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_WEBM\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
const char kVorbisGermanAudioContent2[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'vorbis'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'ger'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_WEBM\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> aac_eng_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* aac_eng_adaptation_set_ptr = aac_eng_adaptation_set.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> aac_ger_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* aac_ger_adaptation_set_ptr = aac_ger_adaptation_set.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> vorbis_german_adaptation_set(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* vorbis_german_adaptation_set_ptr = vorbis_german_adaptation_set.get();
|
||||
|
||||
// We expect three AdaptationSets.
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(aac_eng_adaptation_set))))
|
||||
.WillOnce(Return(ByMove(std::move(aac_ger_adaptation_set))))
|
||||
.WillOnce(Return(ByMove(std::move(vorbis_german_adaptation_set))));
|
||||
|
||||
ASSERT_EQ(aac_eng_adaptation_set_ptr,
|
||||
testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kAacEnglishAudioContent),
|
||||
content_protection_in_adaptation_set_));
|
||||
ASSERT_EQ(aac_ger_adaptation_set_ptr,
|
||||
testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kAacGermanAudioContent),
|
||||
content_protection_in_adaptation_set_));
|
||||
ASSERT_EQ(vorbis_german_adaptation_set_ptr,
|
||||
testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kVorbisGermanAudioContent1),
|
||||
content_protection_in_adaptation_set_));
|
||||
// The same AdaptationSet is returned.
|
||||
ASSERT_EQ(vorbis_german_adaptation_set_ptr,
|
||||
testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kVorbisGermanAudioContent2),
|
||||
content_protection_in_adaptation_set_));
|
||||
}
|
||||
|
||||
TEST_P(PeriodTest, GetAdaptationSets) {
|
||||
const char kContent1[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'eng'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
const char kContent2[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'ger'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_1(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_1_ptr = adaptation_set_1.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_2(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_2_ptr = adaptation_set_2.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_1))))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_2))));
|
||||
|
||||
ASSERT_EQ(adaptation_set_1_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent1),
|
||||
content_protection_in_adaptation_set_));
|
||||
EXPECT_THAT(testable_period_.GetAdaptationSets(),
|
||||
ElementsAre(adaptation_set_1_ptr));
|
||||
|
||||
ASSERT_EQ(adaptation_set_2_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent2),
|
||||
content_protection_in_adaptation_set_));
|
||||
EXPECT_THAT(testable_period_.GetAdaptationSets(),
|
||||
ElementsAre(adaptation_set_1_ptr, adaptation_set_2_ptr));
|
||||
}
|
||||
|
||||
TEST_P(PeriodTest, OrderedByAdaptationSetId) {
|
||||
const char kContent1[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'eng'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
const char kContent2[] =
|
||||
"audio_info {\n"
|
||||
" codec: 'mp4a.40.2'\n"
|
||||
" sampling_frequency: 44100\n"
|
||||
" time_scale: 1200\n"
|
||||
" num_channels: 2\n"
|
||||
" language: 'ger'\n"
|
||||
"}\n"
|
||||
"reference_time_scale: 50\n"
|
||||
"container_type: CONTAINER_MP4\n"
|
||||
"media_duration_seconds: 10.5\n";
|
||||
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_1(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_1_ptr = adaptation_set_1.get();
|
||||
std::unique_ptr<StrictMock<MockAdaptationSet>> adaptation_set_2(
|
||||
new StrictMock<MockAdaptationSet>());
|
||||
auto* adaptation_set_2_ptr = adaptation_set_2.get();
|
||||
|
||||
EXPECT_CALL(testable_period_, NewAdaptationSet(_, _, _))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_1))))
|
||||
.WillOnce(Return(ByMove(std::move(adaptation_set_2))));
|
||||
|
||||
ASSERT_EQ(adaptation_set_1_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent1),
|
||||
content_protection_in_adaptation_set_));
|
||||
ASSERT_EQ(adaptation_set_2_ptr, testable_period_.GetOrCreateAdaptationSet(
|
||||
ConvertToMediaInfo(kContent2),
|
||||
content_protection_in_adaptation_set_));
|
||||
|
||||
adaptation_set_1_ptr->set_id(2);
|
||||
adaptation_set_2_ptr->set_id(1);
|
||||
const char kExpectedXml[] =
|
||||
R"(<Period id="9">)"
|
||||
// ContentType and Representation elements are populated after
|
||||
// Representation::Init() is called.
|
||||
R"( <AdaptationSet id="1" contentType=""/>)"
|
||||
R"( <AdaptationSet id="2" contentType=""/>)"
|
||||
R"(</Period>)";
|
||||
EXPECT_THAT(testable_period_.GetXml(!kOutputPeriodDuration).get(),
|
||||
XmlNodeEqual(kExpectedXml));
|
||||
}
|
||||
INSTANTIATE_TEST_CASE_P(ContentProtectionInAdaptationSet,
|
||||
PeriodTest,
|
||||
PeriodTestWithContentProtection,
|
||||
::testing::Bool());
|
||||
|
||||
} // namespace shaka
|
||||
|
|
|
@ -53,7 +53,12 @@ struct MpdParams {
|
|||
/// The tracks tagged with this language will have <Role ... value=\"main\" />
|
||||
/// in the manifest. This allows the player to choose the correct default
|
||||
/// language for the content.
|
||||
/// This applies to both audio and text tracks. The default language for text
|
||||
/// tracks can be overriden by 'default_text_language'.
|
||||
std::string default_language;
|
||||
/// Same as above, but this overrides the default language for text tracks,
|
||||
/// i.e. subtitles or close-captions.
|
||||
std::string default_text_language;
|
||||
/// Generate static MPD for live profile. Note that this flag has no effect
|
||||
/// for on-demand profile, in which case static MPD is always used.
|
||||
bool generate_static_live_mpd = false;
|
||||
|
|
|
@ -895,8 +895,12 @@ Status Packager::Initialize(
|
|||
// in the shortest form.
|
||||
mpd_params.default_language =
|
||||
LanguageToShortestForm(mpd_params.default_language);
|
||||
mpd_params.default_text_language =
|
||||
LanguageToShortestForm(mpd_params.default_text_language);
|
||||
hls_params.default_language =
|
||||
LanguageToShortestForm(hls_params.default_language);
|
||||
hls_params.default_text_language =
|
||||
LanguageToShortestForm(hls_params.default_text_language);
|
||||
|
||||
if (!mpd_params.mpd_output.empty()) {
|
||||
const bool on_demand_dash_profile =
|
||||
|
|
Loading…
Reference in New Issue