[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.base_url = FLAGS_hls_base_url;
hls_params.key_uri = FLAGS_hls_key_uri; hls_params.key_uri = FLAGS_hls_key_uri;
hls_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth; 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; TestParams& test_params = packaging_params.test_params;
test_params.dump_stream_info = FLAGS_dump_stream_info; test_params.dump_stream_info = FLAGS_dump_stream_info;

View File

@ -1,5 +1,5 @@
#EXTM3U #EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test> ## 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" #EXT-X-STREAM-INF:BANDWIDTH=1217518,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="audio"
video.m3u8 video.m3u8

View File

@ -1,5 +1,5 @@
#EXTM3U #EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test> ## 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" #EXT-X-STREAM-INF:BANDWIDTH=1111147,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="audio"
video.m3u8 video.m3u8

View File

@ -1,5 +1,5 @@
#EXTM3U #EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test> ## 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" #EXT-X-STREAM-INF:BANDWIDTH=1105129,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="audio"
video/video.m3u8 video/video.m3u8

View File

@ -23,6 +23,9 @@ namespace {
void AppendMediaTag(const std::string& base_url, void AppendMediaTag(const std::string& base_url,
const std::string& group_id, const std::string& group_id,
const MediaPlaylist* audio_playlist, const MediaPlaylist* audio_playlist,
const std::string& language,
bool is_default,
bool is_autoselect,
std::string* out) { std::string* out) {
DCHECK(audio_playlist); DCHECK(audio_playlist);
DCHECK(out); DCHECK(out);
@ -31,10 +34,13 @@ void AppendMediaTag(const std::string& base_url,
base::StringAppendF(out, ",URI=\"%s\"", base::StringAppendF(out, ",URI=\"%s\"",
(base_url + audio_playlist->file_name()).c_str()); (base_url + audio_playlist->file_name()).c_str());
base::StringAppendF(out, ",GROUP-ID=\"%s\"", group_id.c_str()); base::StringAppendF(out, ",GROUP-ID=\"%s\"", group_id.c_str());
std::string language = audio_playlist->GetLanguage();
if (!language.empty()) if (!language.empty())
base::StringAppendF(out, ",LANGUAGE=\"%s\"", language.c_str()); base::StringAppendF(out, ",LANGUAGE=\"%s\"", language.c_str());
base::StringAppendF(out, ",NAME=\"%s\"", audio_playlist->name().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\"", base::StringAppendF(out, ",CHANNELS=\"%d\"",
audio_playlist->GetNumChannels()); audio_playlist->GetNumChannels());
out->append("\n"); out->append("\n");
@ -62,8 +68,9 @@ void AppendStreamInfoTag(uint64_t bitrate,
} }
} // namespace } // namespace
MasterPlaylist::MasterPlaylist(const std::string& file_name) MasterPlaylist::MasterPlaylist(const std::string& file_name,
: file_name_(file_name) {} const std::string& default_language)
: file_name_(file_name), default_language_(default_language) {}
MasterPlaylist::~MasterPlaylist() {} MasterPlaylist::~MasterPlaylist() {}
void MasterPlaylist::AddMediaPlaylist(MediaPlaylist* media_playlist) { 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 = const std::list<const MediaPlaylist*>& audio_playlists =
group_id_audio_playlists.second; 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; uint64_t max_audio_bitrate = 0;
for (const MediaPlaylist* audio_playlist : audio_playlists) { 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(); const uint64_t audio_bitrate = audio_playlist->Bitrate();
if (audio_bitrate > max_audio_bitrate) if (audio_bitrate > max_audio_bitrate)
max_audio_bitrate = audio_bitrate; max_audio_bitrate = audio_bitrate;

View File

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

View File

@ -28,6 +28,7 @@ using base::FilePath;
namespace { namespace {
const char kDefaultMasterPlaylistName[] = "playlist.m3u8"; const char kDefaultMasterPlaylistName[] = "playlist.m3u8";
const char kDefaultLanguage[] = "en";
const uint32_t kWidth = 800; const uint32_t kWidth = 800;
const uint32_t kHeight = 600; const uint32_t kHeight = 600;
const HlsPlaylistType kVodPlaylist = HlsPlaylistType::kVod; const HlsPlaylistType kVodPlaylist = HlsPlaylistType::kVod;
@ -36,7 +37,7 @@ const HlsPlaylistType kVodPlaylist = HlsPlaylistType::kVod;
class MasterPlaylistTest : public ::testing::Test { class MasterPlaylistTest : public ::testing::Test {
protected: protected:
MasterPlaylistTest() MasterPlaylistTest()
: master_playlist_(kDefaultMasterPlaylistName), : master_playlist_(kDefaultMasterPlaylistName, kDefaultLanguage),
test_output_dir_("memory://test_dir"), test_output_dir_("memory://test_dir"),
master_playlist_path_( master_playlist_path_(
FilePath::FromUTF8Unsafe(test_output_dir_) FilePath::FromUTF8Unsafe(test_output_dir_)
@ -166,10 +167,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
"test\n" "test\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://playlists.org/eng.m3u8\"," "#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://playlists.org/eng.m3u8\","
"GROUP-ID=\"audiogroup\",LANGUAGE=\"en\",NAME=\"english\"," "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\"," "#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://playlists.org/spa.m3u8\","
"GROUP-ID=\"audiogroup\",LANGUAGE=\"es\",NAME=\"espanol\"," "GROUP-ID=\"audiogroup\",LANGUAGE=\"es\",NAME=\"espanol\","
"CHANNELS=\"5\"\n" "AUTOSELECT=YES,CHANNELS=\"5\"\n"
"#EXT-X-STREAM-INF:BANDWIDTH=360000,CODECS=\"sdvideocodec,audiocodec\"," "#EXT-X-STREAM-INF:BANDWIDTH=360000,CODECS=\"sdvideocodec,audiocodec\","
"RESOLUTION=800x600,AUDIO=\"audiogroup\"\n" "RESOLUTION=800x600,AUDIO=\"audiogroup\"\n"
"http://playlists.org/sd.m3u8\n" "http://playlists.org/sd.m3u8\n"
@ -181,7 +182,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
} }
TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) { TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
// First video, sd.m3u8. // First video, video.m3u8.
std::string video_codec = "videocodec"; std::string video_codec = "videocodec";
MockMediaPlaylist video_playlist(kVodPlaylist, "video.m3u8", "somename", MockMediaPlaylist video_playlist(kVodPlaylist, "video.m3u8", "somename",
"somegroupid"); "somegroupid");
@ -213,6 +214,7 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
.Times(0); .Times(0);
master_playlist_.AddMediaPlaylist(&eng_lo_playlist); master_playlist_.AddMediaPlaylist(&eng_lo_playlist);
// Second audio, eng_hi.m3u8.
std::string audio_codec_hi = "audiocodec_hi"; std::string audio_codec_hi = "audiocodec_hi";
MockMediaPlaylist eng_hi_playlist(kVodPlaylist, "eng_hi.m3u8", "english_hi", MockMediaPlaylist eng_hi_playlist(kVodPlaylist, "eng_hi.m3u8", "english_hi",
"audio_hi"); "audio_hi");
@ -240,10 +242,10 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
"test\n" "test\n"
"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_hi.m3u8\"," "#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_hi.m3u8\","
"GROUP-ID=\"audio_hi\",LANGUAGE=\"en\",NAME=\"english_hi\"," "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\"," "#EXT-X-MEDIA:TYPE=AUDIO,URI=\"http://anydomain.com/eng_lo.m3u8\","
"GROUP-ID=\"audio_lo\",LANGUAGE=\"en\",NAME=\"english_lo\"," "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\"," "#EXT-X-STREAM-INF:BANDWIDTH=400000,CODECS=\"videocodec,audiocodec_hi\","
"RESOLUTION=800x600,AUDIO=\"audio_hi\"\n" "RESOLUTION=800x600,AUDIO=\"audio_hi\"\n"
"http://anydomain.com/video.m3u8\n" "http://anydomain.com/video.m3u8\n"
@ -254,5 +256,75 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
ASSERT_EQ(expected, actual); 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 hls
} // namespace shaka } // namespace shaka

View File

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

View File

@ -38,25 +38,8 @@ class MediaPlaylistFactory {
/// This is thread safe. /// This is thread safe.
class SimpleHlsNotifier : public HlsNotifier { class SimpleHlsNotifier : public HlsNotifier {
public: public:
/// @a prefix is used as hte prefix for all the URIs for Media Playlist. This /// @param hls_params contains parameters for setting up the notifier.
/// includes the segment URIs in the Media Playlists. explicit SimpleHlsNotifier(const HlsParams& hls_params);
/// @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);
~SimpleHlsNotifier() override; ~SimpleHlsNotifier() override;
/// @name HlsNotifier implemetation overrides. /// @name HlsNotifier implemetation overrides.
@ -94,7 +77,7 @@ class SimpleHlsNotifier : public HlsNotifier {
const double time_shift_buffer_depth_ = 0; const double time_shift_buffer_depth_ = 0;
const std::string prefix_; const std::string prefix_;
const std::string key_uri_; const std::string key_uri_;
const std::string output_dir_; std::string output_dir_;
uint32_t target_duration_ = 0; uint32_t target_duration_ = 0;
std::unique_ptr<MediaPlaylistFactory> media_playlist_factory_; std::unique_ptr<MediaPlaylistFactory> media_playlist_factory_;

View File

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

View File

@ -815,15 +815,7 @@ Status Packager::Initialize(
} }
if (!hls_params.master_playlist_output.empty()) { if (!hls_params.master_playlist_output.empty()) {
base::FilePath master_playlist_path( internal->hls_notifier.reset(new hls::SimpleHlsNotifier(hls_params));
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()));
} }
std::vector<StreamDescriptor> streams_for_jobs; std::vector<StreamDescriptor> streams_for_jobs;