[HLS] Support AUTOSELECT and DEFAULT in EXT-X-MEDIA

According to HLS spec: https://goo.gl/MiqjNd 4.3.4.1.1. Rendition Groups
- A Group MUST NOT have more than one member with a DEFAULT attribute of
  YES.
- Each EXT-X-MEDIA tag with an AUTOSELECT=YES attribute SHOULD have a
  combination of LANGUAGE[RFC5646], ASSOC-LANGUAGE, FORCED, and
  CHARACTERISTICS attributes that is distinct from those of other
  AUTOSELECT=YES members of its Group.

We tag the first rendition with a particular language in an audio group
with 'AUTOSELECT'; it is tagged with 'DEFAULT' too if the language
matches --default_language.

Fixes #315

Change-Id: Iacc0bc8c89ebffce8717fa65e82d6daf5a1f6adc
This commit is contained in:
KongQun Yang 2018-01-17 15:43:41 -08:00
parent 53892c7467
commit 32d26094ba
12 changed files with 187 additions and 123 deletions

View File

@ -406,6 +406,7 @@ base::Optional<PackagingParams> GetPackagingParams() {
hls_params.base_url = FLAGS_hls_base_url;
hls_params.key_uri = FLAGS_hls_key_uri;
hls_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
hls_params.default_language = FLAGS_default_language;
TestParams& test_params = packaging_params.test_params;
test_params.dump_stream_info = FLAGS_dump_stream_info;

View File

@ -1,5 +1,5 @@
#EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8",GROUP-ID="audio",NAME="stream_0",CHANNELS="2"
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8",GROUP-ID="audio",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
#EXT-X-STREAM-INF:BANDWIDTH=1217518,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="audio"
video.m3u8

View File

@ -1,5 +1,5 @@
#EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8",GROUP-ID="audio",NAME="stream_0",CHANNELS="2"
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8",GROUP-ID="audio",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
#EXT-X-STREAM-INF:BANDWIDTH=1111147,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="audio"
video.m3u8

View File

@ -1,5 +1,5 @@
#EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio/audio.m3u8",GROUP-ID="audio",NAME="stream_0",CHANNELS="2"
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio/audio.m3u8",GROUP-ID="audio",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
#EXT-X-STREAM-INF:BANDWIDTH=1105129,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="audio"
video/video.m3u8

View File

@ -23,6 +23,9 @@ namespace {
void AppendMediaTag(const std::string& base_url,
const std::string& group_id,
const MediaPlaylist* audio_playlist,
const std::string& language,
bool is_default,
bool is_autoselect,
std::string* out) {
DCHECK(audio_playlist);
DCHECK(out);
@ -31,10 +34,13 @@ void AppendMediaTag(const std::string& base_url,
base::StringAppendF(out, ",URI=\"%s\"",
(base_url + audio_playlist->file_name()).c_str());
base::StringAppendF(out, ",GROUP-ID=\"%s\"", group_id.c_str());
std::string language = audio_playlist->GetLanguage();
if (!language.empty())
base::StringAppendF(out, ",LANGUAGE=\"%s\"", language.c_str());
base::StringAppendF(out, ",NAME=\"%s\"", audio_playlist->name().c_str());
if (is_default)
base::StringAppendF(out, ",DEFAULT=YES");
if (is_autoselect)
base::StringAppendF(out, ",AUTOSELECT=YES");
base::StringAppendF(out, ",CHANNELS=\"%d\"",
audio_playlist->GetNumChannels());
out->append("\n");
@ -62,8 +68,9 @@ void AppendStreamInfoTag(uint64_t bitrate,
}
} // namespace
MasterPlaylist::MasterPlaylist(const std::string& file_name)
: file_name_(file_name) {}
MasterPlaylist::MasterPlaylist(const std::string& file_name,
const std::string& default_language)
: file_name_(file_name), default_language_(default_language) {}
MasterPlaylist::~MasterPlaylist() {}
void MasterPlaylist::AddMediaPlaylist(MediaPlaylist* media_playlist) {
@ -99,9 +106,32 @@ bool MasterPlaylist::WriteMasterPlaylist(const std::string& base_url,
const std::list<const MediaPlaylist*>& audio_playlists =
group_id_audio_playlists.second;
// Tracks the language of the playlist in this group.
// According to HLS spec: https://goo.gl/MiqjNd 4.3.4.1.1. Rendition Groups
// - A Group MUST NOT have more than one member with a DEFAULT attribute of
// YES.
// - Each EXT-X-MEDIA tag with an AUTOSELECT=YES attribute SHOULD have a
// combination of LANGUAGE[RFC5646], ASSOC-LANGUAGE, FORCED, and
// CHARACTERISTICS attributes that is distinct from those of other
// AUTOSELECT=YES members of its Group.
// We tag the first rendition encountered with a particular language with
// 'AUTOSELECT'; it is tagged with 'DEFAULT' too if the language matches
// |default_language_|.
std::set<std::string> languages;
uint64_t max_audio_bitrate = 0;
for (const MediaPlaylist* audio_playlist : audio_playlists) {
AppendMediaTag(base_url, group_id, audio_playlist, &audio_output);
bool is_default = false;
bool is_autoselect = false;
const std::string language = audio_playlist->GetLanguage();
if (languages.find(language) == languages.end()) {
is_default = !language.empty() && language == default_language_;
is_autoselect = true;
languages.insert(language);
}
AppendMediaTag(base_url, group_id, audio_playlist, language, is_default,
is_autoselect, &audio_output);
const uint64_t audio_bitrate = audio_playlist->Bitrate();
if (audio_bitrate > max_audio_bitrate)
max_audio_bitrate = audio_bitrate;

View File

@ -23,7 +23,10 @@ class MediaPlaylist;
class MasterPlaylist {
public:
/// @param file_name is the file name of the master playlist.
explicit MasterPlaylist(const std::string& file_name);
/// @param default_language determines the rendition that should be tagged
/// with 'DEFAULT'.
MasterPlaylist(const std::string& file_name,
const std::string& default_language);
virtual ~MasterPlaylist();
/// @param media_playlist is a MediaPlaylist that should get added to this
@ -46,6 +49,7 @@ class MasterPlaylist {
private:
std::string written_playlist_;
const std::string file_name_;
const std::string default_language_;
std::list<MediaPlaylist*> all_playlists_;
std::list<const MediaPlaylist*> video_playlists_;
// The key is the audio group name.

View File

@ -28,6 +28,7 @@ using base::FilePath;
namespace {
const char kDefaultMasterPlaylistName[] = "playlist.m3u8";
const char kDefaultLanguage[] = "en";
const uint32_t kWidth = 800;
const uint32_t kHeight = 600;
const HlsPlaylistType kVodPlaylist = HlsPlaylistType::kVod;
@ -36,7 +37,7 @@ const HlsPlaylistType kVodPlaylist = HlsPlaylistType::kVod;
class MasterPlaylistTest : public ::testing::Test {
protected:
MasterPlaylistTest()
: master_playlist_(kDefaultMasterPlaylistName),
: master_playlist_(kDefaultMasterPlaylistName, kDefaultLanguage),
test_output_dir_("memory://test_dir"),
master_playlist_path_(
FilePath::FromUTF8Unsafe(test_output_dir_)
@ -166,10 +167,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
"test\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://playlists.org/eng.m3u8\","
"GROUP-ID=\"audiogroup\",LANGUAGE=\"en\",NAME=\"english\","
"CHANNELS=\"2\"\n"
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"2\"\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://playlists.org/spa.m3u8\","
"GROUP-ID=\"audiogroup\",LANGUAGE=\"es\",NAME=\"espanol\","
"CHANNELS=\"5\"\n"
"AUTOSELECT=YES,CHANNELS=\"5\"\n"
"#EXT-X-STREAM-INF:BANDWIDTH=360000,CODECS=\"sdvideocodec,audiocodec\","
"RESOLUTION=800x600,AUDIO=\"audiogroup\"\n"
"http://playlists.org/sd.m3u8\n"
@ -181,7 +182,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
}
TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
// First video, sd.m3u8.
// First video, video.m3u8.
std::string video_codec = "videocodec";
MockMediaPlaylist video_playlist(kVodPlaylist, "video.m3u8", "somename",
"somegroupid");
@ -213,6 +214,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
.Times(0);
master_playlist_.AddMediaPlaylist(&eng_lo_playlist);
// Second audio, eng_hi.m3u8.
std::string audio_codec_hi = "audiocodec_hi";
MockMediaPlaylist eng_hi_playlist(kVodPlaylist, "eng_hi.m3u8", "english_hi",
"audio_hi");
@ -240,10 +242,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
"test\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_hi.m3u8\","
"GROUP-ID=\"audio_hi\",LANGUAGE=\"en\",NAME=\"english_hi\","
"CHANNELS=\"8\"\n"
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"8\"\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_lo.m3u8\","
"GROUP-ID=\"audio_lo\",LANGUAGE=\"en\",NAME=\"english_lo\","
"CHANNELS=\"1\"\n"
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"1\"\n"
"#EXT-X-STREAM-INF:BANDWIDTH=400000,CODECS=\"videocodec,audiocodec_hi\","
"RESOLUTION=800x600,AUDIO=\"audio_hi\"\n"
"http://anydomain.com/video.m3u8\n"
@ -254,5 +256,75 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
ASSERT_EQ(expected, actual);
}
TEST_F(MasterPlaylistTest, WriteMasterPlaylistSameAudioGroupSameLanguage) {
// First video, video.m3u8.
std::string video_codec = "videocodec";
MockMediaPlaylist video_playlist(kVodPlaylist, "video.m3u8", "somename",
"somegroupid");
video_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListVideo);
video_playlist.SetCodecForTesting(video_codec);
EXPECT_CALL(video_playlist, Bitrate())
.Times(AtLeast(1))
.WillRepeatedly(Return(300000));
EXPECT_CALL(video_playlist, GetDisplayResolution(NotNull(), NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<0>(kWidth), SetArgPointee<1>(kHeight),
Return(true)));
master_playlist_.AddMediaPlaylist(&video_playlist);
// First audio, eng_lo.m3u8.
const std::string audio_codec = "audiocodec";
MockMediaPlaylist eng_lo_playlist(kVodPlaylist, "eng_lo.m3u8", "english",
"audio");
EXPECT_CALL(eng_lo_playlist, GetLanguage()).WillRepeatedly(Return("en"));
EXPECT_CALL(eng_lo_playlist, GetNumChannels()).WillRepeatedly(Return(1));
eng_lo_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
eng_lo_playlist.SetCodecForTesting(audio_codec);
EXPECT_CALL(eng_lo_playlist, Bitrate())
.Times(AtLeast(1))
.WillRepeatedly(Return(50000));
EXPECT_CALL(eng_lo_playlist, GetDisplayResolution(NotNull(), NotNull()))
.Times(0);
master_playlist_.AddMediaPlaylist(&eng_lo_playlist);
// Second audio, eng_hi.m3u8.
MockMediaPlaylist eng_hi_playlist(kVodPlaylist, "eng_hi.m3u8", "english",
"audio");
EXPECT_CALL(eng_hi_playlist, GetLanguage()).WillRepeatedly(Return("en"));
EXPECT_CALL(eng_hi_playlist, GetNumChannels()).WillRepeatedly(Return(8));
eng_hi_playlist.SetStreamTypeForTesting(
MediaPlaylist::MediaPlaylistStreamType::kPlayListAudio);
eng_hi_playlist.SetCodecForTesting(audio_codec);
EXPECT_CALL(eng_hi_playlist, Bitrate())
.Times(AtLeast(1))
.WillRepeatedly(Return(100000));
EXPECT_CALL(eng_hi_playlist, GetDisplayResolution(NotNull(), NotNull()))
.Times(0);
master_playlist_.AddMediaPlaylist(&eng_hi_playlist);
const char kBaseUrl[] = "http://anydomain.com/";
EXPECT_TRUE(master_playlist_.WriteMasterPlaylist(kBaseUrl, test_output_dir_));
std::string actual;
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
const std::string expected =
"#EXTM3U\n"
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_lo.m3u8\","
"GROUP-ID=\"audio\",LANGUAGE=\"en\",NAME=\"english\","
"DEFAULT=YES,AUTOSELECT=YES,CHANNELS=\"1\"\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_hi.m3u8\","
"GROUP-ID=\"audio\",LANGUAGE=\"en\",NAME=\"english\","
"CHANNELS=\"8\"\n"
"#EXT-X-STREAM-INF:BANDWIDTH=400000,CODECS=\"videocodec,audiocodec\","
"RESOLUTION=800x600,AUDIO=\"audio\"\n"
"http://anydomain.com/video.m3u8\n";
ASSERT_EQ(expected, actual);
}
} // namespace hls
} // namespace shaka

View File

@ -247,19 +247,19 @@ std::unique_ptr<MediaPlaylist> MediaPlaylistFactory::Create(
type, time_shift_buffer_depth, file_name, name, group_id));
}
SimpleHlsNotifier::SimpleHlsNotifier(HlsPlaylistType playlist_type,
double time_shift_buffer_depth,
const std::string& prefix,
const std::string& key_uri,
const std::string& output_dir,
const std::string& master_playlist_name)
: HlsNotifier(playlist_type),
time_shift_buffer_depth_(time_shift_buffer_depth),
prefix_(prefix),
key_uri_(key_uri),
output_dir_(output_dir),
media_playlist_factory_(new MediaPlaylistFactory()),
master_playlist_(new MasterPlaylist(master_playlist_name)) {}
SimpleHlsNotifier::SimpleHlsNotifier(const HlsParams& hls_params)
: HlsNotifier(hls_params.playlist_type),
time_shift_buffer_depth_(hls_params.time_shift_buffer_depth),
prefix_(hls_params.base_url),
key_uri_(hls_params.key_uri),
media_playlist_factory_(new MediaPlaylistFactory()) {
const base::FilePath master_playlist_path(
base::FilePath::FromUTF8Unsafe(hls_params.master_playlist_output));
output_dir_ = master_playlist_path.DirName().AsUTF8Unsafe();
master_playlist_.reset(
new MasterPlaylist(master_playlist_path.BaseName().AsUTF8Unsafe(),
hls_params.default_language));
}
SimpleHlsNotifier::~SimpleHlsNotifier() {}

View File

@ -38,25 +38,8 @@ class MediaPlaylistFactory {
/// This is thread safe.
class SimpleHlsNotifier : public HlsNotifier {
public:
/// @a prefix is used as hte prefix for all the URIs for Media Playlist. This
/// includes the segment URIs in the Media Playlists.
/// @param playlist_type is the type of the playlists.
/// @param time_shift_buffer_depth determines the duration of the time
/// shifting buffer, only for live HLS.
/// @param prefix is the used as the prefix for MediaPlaylist URIs. May be
/// empty for relative URI from the playlist.
/// @param key_uri defines the key uri for "identity" and
/// "com.apple.streamingkeydelivery" key formats. Ignored if the
/// playlist is not encrypted or not using the above key formats.
/// @param output_dir is the output directory of the playlists. May be empty
/// to write to current directory.
/// @param master_playlist_name is the name of the master playlist.
SimpleHlsNotifier(HlsPlaylistType playlist_type,
double time_shift_buffer_depth,
const std::string& prefix,
const std::string& key_uri,
const std::string& output_dir,
const std::string& master_playlist_name);
/// @param hls_params contains parameters for setting up the notifier.
explicit SimpleHlsNotifier(const HlsParams& hls_params);
~SimpleHlsNotifier() override;
/// @name HlsNotifier implemetation overrides.
@ -94,7 +77,7 @@ class SimpleHlsNotifier : public HlsNotifier {
const double time_shift_buffer_depth_ = 0;
const std::string prefix_;
const std::string key_uri_;
const std::string output_dir_;
std::string output_dir_;
uint32_t target_duration_ = 0;
std::unique_ptr<MediaPlaylistFactory> media_playlist_factory_;

View File

@ -31,6 +31,7 @@ using ::testing::_;
namespace {
const char kMasterPlaylistName[] = "master.m3u8";
const char kDefaultLanguage[] = "en";
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";
@ -39,7 +40,8 @@ const HlsPlaylistType kLivePlaylist = HlsPlaylistType::kLive;
class MockMasterPlaylist : public MasterPlaylist {
public:
MockMasterPlaylist() : MasterPlaylist(kMasterPlaylistName) {}
MockMasterPlaylist()
: MasterPlaylist(kMasterPlaylistName, kDefaultLanguage) {}
MOCK_METHOD1(AddMediaPlaylist, void(MediaPlaylist* media_playlist));
MOCK_METHOD2(WriteMasterPlaylist,
@ -68,7 +70,7 @@ class MockMediaPlaylistFactory : public MediaPlaylistFactory {
const double kTestTimeShiftBufferDepth = 1800.0;
const char kTestPrefix[] = "http://testprefix.com/";
const char kEmptyPrefix[] = "";
const char kAnyOutputDir[] = "anything/";
const char kAnyOutputDir[] = "anything";
const uint64_t kAnyStartTime = 10;
const uint64_t kAnyDuration = 1000;
@ -92,7 +94,14 @@ class SimpleHlsNotifierTest : public ::testing::Test {
media::kCommonSystemId + arraysize(media::kCommonSystemId)),
fairplay_system_id_(
media::kFairplaySystemId,
media::kFairplaySystemId + arraysize(media::kFairplaySystemId)) {}
media::kFairplaySystemId + arraysize(media::kFairplaySystemId)) {
hls_params_.playlist_type = kVodPlaylist;
hls_params_.time_shift_buffer_depth = kTestTimeShiftBufferDepth;
hls_params_.base_url = kTestPrefix;
hls_params_.key_uri = kEmptyKeyUri;
hls_params_.master_playlist_output =
std::string(kAnyOutputDir) + "/" + kMasterPlaylistName;
}
void InjectMediaPlaylistFactory(std::unique_ptr<MediaPlaylistFactory> factory,
SimpleHlsNotifier* notifier) {
@ -138,12 +147,11 @@ class SimpleHlsNotifierTest : public ::testing::Test {
const std::vector<uint8_t> widevine_system_id_;
const std::vector<uint8_t> common_system_id_;
const std::vector<uint8_t> fairplay_system_id_;
HlsParams hls_params_;
};
TEST_F(SimpleHlsNotifierTest, Init) {
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
EXPECT_TRUE(notifier.Init());
}
@ -174,9 +182,7 @@ TEST_F(SimpleHlsNotifierTest, RebaseSegmentUrl) {
StrEq("groupid")))
.WillOnce(Return(mock_media_playlist));
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
@ -215,9 +221,7 @@ TEST_F(SimpleHlsNotifierTest, RebaseInitSegmentUrl) {
StrEq("groupid")))
.WillOnce(Return(mock_media_playlist));
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
@ -255,9 +259,8 @@ TEST_F(SimpleHlsNotifierTest, RebaseSegmentUrlRelativeToPlaylist) {
StrEq("groupid")))
.WillOnce(Return(mock_media_playlist));
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kEmptyPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
hls_params_.base_url = kEmptyPrefix;
SimpleHlsNotifier notifier(hls_params_);
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
@ -277,9 +280,9 @@ TEST_F(SimpleHlsNotifierTest, RebaseSegmentUrlRelativeToPlaylist) {
// prefix is stripped from segment path.
TEST_F(SimpleHlsNotifierTest, RebaseAbsoluteSegmentPrefixAndOutputDirMatch) {
const char kAbsoluteOutputDir[] = "/tmp/something/";
SimpleHlsNotifier test_notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAbsoluteOutputDir,
kMasterPlaylistName);
hls_params_.master_playlist_output =
std::string(kAbsoluteOutputDir) + kMasterPlaylistName;
SimpleHlsNotifier test_notifier(hls_params_);
std::unique_ptr<MockMasterPlaylist> mock_master_playlist(
new MockMasterPlaylist());
@ -319,9 +322,9 @@ TEST_F(SimpleHlsNotifierTest, RebaseAbsoluteSegmentPrefixAndOutputDirMatch) {
TEST_F(SimpleHlsNotifierTest,
RebaseAbsoluteSegmentCompletelyDifferentDirectory) {
const char kAbsoluteOutputDir[] = "/tmp/something/";
SimpleHlsNotifier test_notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAbsoluteOutputDir,
kMasterPlaylistName);
hls_params_.master_playlist_output =
std::string(kAbsoluteOutputDir) + kMasterPlaylistName;
SimpleHlsNotifier test_notifier(hls_params_);
std::unique_ptr<MockMasterPlaylist> mock_master_playlist(
new MockMasterPlaylist());
@ -356,9 +359,7 @@ TEST_F(SimpleHlsNotifierTest,
}
TEST_F(SimpleHlsNotifierTest, Flush) {
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
std::unique_ptr<MockMasterPlaylist> mock_master_playlist(
new MockMasterPlaylist());
EXPECT_CALL(*mock_master_playlist,
@ -386,9 +387,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewStream) {
StrEq("groupid")))
.WillOnce(Return(mock_media_playlist));
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
@ -430,9 +429,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
EXPECT_CALL(*mock_media_playlist, GetLongestSegmentDuration())
.WillOnce(Return(kLongestSegmentDuration));
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
MockMasterPlaylist* mock_master_playlist_ptr = mock_master_playlist.get();
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
@ -463,9 +460,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
}
TEST_F(SimpleHlsNotifierTest, NotifyNewSegmentWithoutStreamsRegistered) {
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
EXPECT_TRUE(notifier.Init());
EXPECT_FALSE(notifier.NotifyNewSegment(1u, "anything", 0u, 0u, 0u, 0u));
}
@ -474,9 +469,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
@ -537,9 +530,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
@ -596,9 +587,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateIdentityKey) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
@ -626,9 +615,7 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
@ -704,9 +691,8 @@ TEST_F(SimpleHlsNotifierTest, EncryptionScheme) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kIdentityKeyUri, kAnyOutputDir,
kMasterPlaylistName);
hls_params_.key_uri = kIdentityKeyUri;
SimpleHlsNotifier notifier(hls_params_);
const uint32_t stream_id =
SetupStream(kCencProtectionScheme, mock_media_playlist, &notifier);
@ -729,9 +715,9 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFairplay) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kLivePlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(kLivePlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kFairplayKeyUri, kAnyOutputDir,
kMasterPlaylistName);
hls_params_.playlist_type = kLivePlaylist;
hls_params_.key_uri = kFairplayKeyUri;
SimpleHlsNotifier notifier(hls_params_);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
const std::vector<uint8_t> key_id(16, 0x12);
@ -752,9 +738,7 @@ TEST_F(SimpleHlsNotifierTest, WidevineCencEncryptionScheme) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
const uint32_t stream_id =
SetupStream(kCencProtectionScheme, mock_media_playlist, &notifier);
@ -801,9 +785,7 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, &notifier);
@ -869,9 +851,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) {
std::vector<uint8_t> iv;
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> key_id;
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kEmptyKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
EXPECT_TRUE(notifier.Init());
EXPECT_FALSE(
notifier.NotifyEncryptionUpdate(1238u, key_id, system_id, iv, pssh_data));
@ -881,9 +861,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyCueEvent) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "playlist.m3u8", "", "");
SimpleHlsNotifier notifier(kVodPlaylist, kTestTimeShiftBufferDepth,
kTestPrefix, kIdentityKeyUri, kAnyOutputDir,
kMasterPlaylistName);
SimpleHlsNotifier notifier(hls_params_);
const uint32_t stream_id =
SetupStream(kCencProtectionScheme, mock_media_playlist, &notifier);
@ -945,8 +923,8 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegment) {
.AsUTF8Unsafe())))
.WillOnce(Return(true));
SimpleHlsNotifier notifier(GetParam(), kTestTimeShiftBufferDepth, kTestPrefix,
kEmptyKeyUri, kAnyOutputDir, kMasterPlaylistName);
hls_params_.playlist_type = GetParam();
SimpleHlsNotifier notifier(hls_params_);
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);
EXPECT_TRUE(notifier.Init());
@ -990,8 +968,8 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
*mock_master_playlist,
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist2)));
SimpleHlsNotifier notifier(GetParam(), kTestTimeShiftBufferDepth, kTestPrefix,
kEmptyKeyUri, kAnyOutputDir, kMasterPlaylistName);
hls_params_.playlist_type = GetParam();
SimpleHlsNotifier notifier(hls_params_);
MockMasterPlaylist* mock_master_playlist_ptr = mock_master_playlist.get();
InjectMasterPlaylist(std::move(mock_master_playlist), &notifier);
InjectMediaPlaylistFactory(std::move(factory), &notifier);

View File

@ -35,6 +35,10 @@ struct HlsParams {
/// key formats. Ignored if the playlist is not encrypted or not using the
/// above key formats.
std::string key_uri;
/// 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.
std::string default_language;
};
} // namespace shaka

View File

@ -815,15 +815,7 @@ Status Packager::Initialize(
}
if (!hls_params.master_playlist_output.empty()) {
base::FilePath master_playlist_path(
base::FilePath::FromUTF8Unsafe(hls_params.master_playlist_output));
base::FilePath master_playlist_name = master_playlist_path.BaseName();
internal->hls_notifier.reset(new hls::SimpleHlsNotifier(
hls_params.playlist_type, hls_params.time_shift_buffer_depth,
hls_params.base_url, hls_params.key_uri,
master_playlist_path.DirName().AsEndingWithSeparator().AsUTF8Unsafe(),
master_playlist_name.AsUTF8Unsafe()));
internal->hls_notifier.reset(new hls::SimpleHlsNotifier(hls_params));
}
std::vector<StreamDescriptor> streams_for_jobs;