parent
682ac3c990
commit
ab8485c5b7
6
AUTHORS
6
AUTHORS
|
@ -20,6 +20,7 @@ Anders Hasselqvist <anders.hasselqvist@gmail.com>
|
||||||
Audible <*@audible.com>
|
Audible <*@audible.com>
|
||||||
Chun-da Chen <capitalm.c@gmail.com>
|
Chun-da Chen <capitalm.c@gmail.com>
|
||||||
Daniel Cantarín <canta@canta.com.ar>
|
Daniel Cantarín <canta@canta.com.ar>
|
||||||
|
Dennis E. Mungai (Brainiarc7) <dmngaie@gmail.com>
|
||||||
Dolby Laboratories <*@dolby.com>
|
Dolby Laboratories <*@dolby.com>
|
||||||
Evgeny Zajcev <zevlg@yandex.ru>
|
Evgeny Zajcev <zevlg@yandex.ru>
|
||||||
Eyevinn Technology AB <*@eyevinn.se>
|
Eyevinn Technology AB <*@eyevinn.se>
|
||||||
|
@ -27,13 +28,14 @@ Google LLC. <*@google.com>
|
||||||
Ivi.ru LLC <*@ivi.ru>
|
Ivi.ru LLC <*@ivi.ru>
|
||||||
Leandro Moreira <leandro.ribeiro.moreira@gmail.com>
|
Leandro Moreira <leandro.ribeiro.moreira@gmail.com>
|
||||||
Leo Law <leoltlaw.gh@gmail.com>
|
Leo Law <leoltlaw.gh@gmail.com>
|
||||||
|
Meta Platforms, Inc. <*@meta.com>
|
||||||
More Screens Ltd. <*@morescreens.net>
|
More Screens Ltd. <*@morescreens.net>
|
||||||
Ole Andre Birkedal <o.birkedal@sportradar.com>
|
Ole Andre Birkedal <o.birkedal@sportradar.com>
|
||||||
Philo Inc. <*@philo.com>
|
Philo Inc. <*@philo.com>
|
||||||
Piotr Srebrny <srebrny.piotr@gmail.com>
|
Piotr Srebrny <srebrny.piotr@gmail.com>
|
||||||
|
Prakash Duggaraju <duggaraju@gmail.com>
|
||||||
Richard Eklycke <richard@eklycke.se>
|
Richard Eklycke <richard@eklycke.se>
|
||||||
Sanil Raut <sr1990003@gmail.com>
|
Sanil Raut <sr1990003@gmail.com>
|
||||||
Sergio Ammirata <sergio@ammirata.net>
|
Sergio Ammirata <sergio@ammirata.net>
|
||||||
The Chromium Authors <*@chromium.org>
|
The Chromium Authors <*@chromium.org>
|
||||||
Prakash Duggaraju <duggaraju@gmail.com>
|
|
||||||
Dennis E. Mungai (Brainiarc7) <dmngaie@gmail.com>
|
|
||||||
|
|
|
@ -27,8 +27,10 @@ Anders Hasselqvist <anders.hasselqvist@gmail.com>
|
||||||
Andreas Motl <andreas.motl@elmyra.de>
|
Andreas Motl <andreas.motl@elmyra.de>
|
||||||
Bei Li <beil@google.com>
|
Bei Li <beil@google.com>
|
||||||
Chun-da Chen <capitalm.c@gmail.com>
|
Chun-da Chen <capitalm.c@gmail.com>
|
||||||
|
Cosmin Stejerean <cstejerean@meta.com>
|
||||||
Daniel Cantarín <canta@canta.com.ar>
|
Daniel Cantarín <canta@canta.com.ar>
|
||||||
David Cavar <pal3thorn@gmail.com>
|
David Cavar <pal3thorn@gmail.com>
|
||||||
|
Dennis E. Mungai (Brainiarc7) <dmngaie@gmail.com>
|
||||||
Evgeny Zajcev <zevlg@yandex.ru>
|
Evgeny Zajcev <zevlg@yandex.ru>
|
||||||
Gabe Kopley <gabe@philo.com>
|
Gabe Kopley <gabe@philo.com>
|
||||||
Geoff Jukes <geoff@jukes.org>
|
Geoff Jukes <geoff@jukes.org>
|
||||||
|
@ -42,6 +44,7 @@ Leo Law <leoltlaw.gh@gmail.com>
|
||||||
Marcus Spangenberg <marcus.spangenberg@eyevinn.se>
|
Marcus Spangenberg <marcus.spangenberg@eyevinn.se>
|
||||||
Ole Andre Birkedal <o.birkedal@sportradar.com>
|
Ole Andre Birkedal <o.birkedal@sportradar.com>
|
||||||
Piotr Srebrny <srebrny.piotr@gmail.com>
|
Piotr Srebrny <srebrny.piotr@gmail.com>
|
||||||
|
Prakash Duggaraju <duggaraju@gmail.com>
|
||||||
Qingquan Wang <wangqq1103@gmail.com>
|
Qingquan Wang <wangqq1103@gmail.com>
|
||||||
Richard Eklycke <richard@eklycke.se>
|
Richard Eklycke <richard@eklycke.se>
|
||||||
Rintaro Kuroiwa <rkuroiwa@google.com>
|
Rintaro Kuroiwa <rkuroiwa@google.com>
|
||||||
|
@ -52,5 +55,4 @@ Thomas Inskip <tinskip@google.com>
|
||||||
Tim Lansen <tim.lansen@gmail.com>
|
Tim Lansen <tim.lansen@gmail.com>
|
||||||
Vincent Nguyen <nvincen@amazon.com>
|
Vincent Nguyen <nvincen@amazon.com>
|
||||||
Weiguo Shao <weiguo.shao@dolby.com>
|
Weiguo Shao <weiguo.shao@dolby.com>
|
||||||
Prakash Duggaraju <duggaraju@gmail.com>
|
|
||||||
Dennis E. Mungai (Brainiarc7) <dmngaie@gmail.com>
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ include("protobuf.cmake")
|
||||||
add_subdirectory(file)
|
add_subdirectory(file)
|
||||||
add_subdirectory(kv_pairs)
|
add_subdirectory(kv_pairs)
|
||||||
add_subdirectory(media)
|
add_subdirectory(media)
|
||||||
|
add_subdirectory(hls)
|
||||||
add_subdirectory(mpd)
|
add_subdirectory(mpd)
|
||||||
add_subdirectory(status)
|
add_subdirectory(status)
|
||||||
add_subdirectory(third_party)
|
add_subdirectory(third_party)
|
||||||
|
|
|
@ -48,4 +48,15 @@ bool TempFilePath(const std::string& temp_dir, std::string* temp_file_path) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string MakePathRelative(const std::filesystem::path& media_path,
|
||||||
|
const std::filesystem::path& parent_path) {
|
||||||
|
auto relative_path = std::filesystem::relative(media_path, parent_path);
|
||||||
|
if (relative_path.empty() || *relative_path.begin() == "..") {
|
||||||
|
// Not related.
|
||||||
|
relative_path = media_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return relative_path.lexically_normal().generic_string();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://developers.google.com/open-source/licenses/bsd
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -15,4 +16,7 @@ namespace shaka {
|
||||||
/// @returns true on success, false otherwise.
|
/// @returns true on success, false otherwise.
|
||||||
bool TempFilePath(const std::string& temp_dir, std::string* temp_file_path);
|
bool TempFilePath(const std::string& temp_dir, std::string* temp_file_path);
|
||||||
|
|
||||||
|
std::string MakePathRelative(const std::filesystem::path& media_path,
|
||||||
|
const std::filesystem::path& parent_path);
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
# Copyright 2016 Google LLC. All rights reserved.
|
||||||
|
#
|
||||||
|
# Use of this source code is governed by a BSD-style
|
||||||
|
# license that can be found in the LICENSE file or at
|
||||||
|
# https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
|
||||||
|
add_library(hls_builder
|
||||||
|
base/hls_notifier.h
|
||||||
|
base/master_playlist.cc
|
||||||
|
base/master_playlist.h
|
||||||
|
base/media_playlist.cc
|
||||||
|
base/media_playlist.h
|
||||||
|
base/simple_hls_notifier.cc
|
||||||
|
base/simple_hls_notifier.h
|
||||||
|
base/tag.cc
|
||||||
|
base/tag.h
|
||||||
|
public/hls_params.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(hls_builder
|
||||||
|
file
|
||||||
|
media_base
|
||||||
|
widevine_protos
|
||||||
|
manifest_base
|
||||||
|
mpd_media_info_proto
|
||||||
|
absl::flags
|
||||||
|
absl::strings
|
||||||
|
absl::str_format
|
||||||
|
glog
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(hls_unittest
|
||||||
|
base/master_playlist_unittest.cc
|
||||||
|
base/media_playlist_unittest.cc
|
||||||
|
base/mock_media_playlist.cc
|
||||||
|
base/mock_media_playlist.h
|
||||||
|
base/simple_hls_notifier_unittest.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(hls_unittest
|
||||||
|
file
|
||||||
|
file_test_util
|
||||||
|
test_data_util
|
||||||
|
absl::flags
|
||||||
|
hls_builder
|
||||||
|
gmock
|
||||||
|
gtest
|
||||||
|
gtest_main)
|
||||||
|
|
||||||
|
add_test(NAME hls_unittest COMMAND hls_unittest)
|
|
@ -10,10 +10,11 @@
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "packager/base/files/file_path.h"
|
#include <absl/strings/numbers.h>
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include <absl/strings/str_format.h>
|
||||||
#include "packager/base/strings/string_util.h"
|
#include <absl/strings/str_join.h>
|
||||||
#include "packager/base/strings/stringprintf.h"
|
#include <glog/logging.h>
|
||||||
|
#include <filesystem>
|
||||||
#include "packager/file/file.h"
|
#include "packager/file/file.h"
|
||||||
#include "packager/hls/base/media_playlist.h"
|
#include "packager/hls/base/media_playlist.h"
|
||||||
#include "packager/hls/base/tag.h"
|
#include "packager/hls/base/tag.h"
|
||||||
|
@ -30,8 +31,8 @@ void AppendVersionString(std::string* content) {
|
||||||
const std::string version = GetPackagerVersion();
|
const std::string version = GetPackagerVersion();
|
||||||
if (version.empty())
|
if (version.empty())
|
||||||
return;
|
return;
|
||||||
base::StringAppendF(content, "## Generated with %s version %s\n",
|
absl::StrAppendFormat(content, "## Generated with %s version %s\n",
|
||||||
GetPackagerProjectUrl().c_str(), version.c_str());
|
GetPackagerProjectUrl().c_str(), version.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This structure roughly maps to the Variant stream in HLS specification.
|
// This structure roughly maps to the Variant stream in HLS specification.
|
||||||
|
@ -207,8 +208,8 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
|
||||||
tag_name = "#EXT-X-I-FRAME-STREAM-INF";
|
tag_name = "#EXT-X-I-FRAME-STREAM-INF";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOTREACHED() << "Cannot build STREAM-INFO tag for type "
|
NOTIMPLEMENTED() << "Cannot build STREAM-INFO tag for type "
|
||||||
<< static_cast<int>(playlist.stream_type());
|
<< static_cast<int>(playlist.stream_type());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Tag tag(tag_name, out);
|
Tag tag(tag_name, out);
|
||||||
|
@ -223,7 +224,7 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
|
||||||
variant.audio_codecs.end());
|
variant.audio_codecs.end());
|
||||||
all_codecs.insert(all_codecs.end(), variant.text_codecs.begin(),
|
all_codecs.insert(all_codecs.end(), variant.text_codecs.begin(),
|
||||||
variant.text_codecs.end());
|
variant.text_codecs.end());
|
||||||
tag.AddQuotedString("CODECS", base::JoinString(all_codecs, ","));
|
tag.AddQuotedString("CODECS", absl::StrJoin(all_codecs, ","));
|
||||||
|
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
|
@ -266,8 +267,8 @@ void BuildStreamInfTag(const MediaPlaylist& playlist,
|
||||||
tag.AddQuotedString("URI", base_url + playlist.file_name());
|
tag.AddQuotedString("URI", base_url + playlist.file_name());
|
||||||
out->append("\n");
|
out->append("\n");
|
||||||
} else {
|
} else {
|
||||||
base::StringAppendF(out, "\n%s%s\n", base_url.c_str(),
|
absl::StrAppendFormat(out, "\n%s%s\n", base_url.c_str(),
|
||||||
playlist.file_name().c_str());
|
playlist.file_name().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,8 +296,8 @@ void BuildMediaTag(const MediaPlaylist& playlist,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
NOTREACHED() << "Cannot build media tag for type "
|
NOTIMPLEMENTED() << "Cannot build media tag for type "
|
||||||
<< static_cast<int>(playlist.stream_type());
|
<< static_cast<int>(playlist.stream_type());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,7 +314,7 @@ void BuildMediaTag(const MediaPlaylist& playlist,
|
||||||
if (is_default) {
|
if (is_default) {
|
||||||
tag.AddString("DEFAULT", "YES");
|
tag.AddString("DEFAULT", "YES");
|
||||||
} else {
|
} else {
|
||||||
tag.AddString("DEFAULT", "NO");
|
tag.AddString("DEFAULT", "NO");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_autoselect) {
|
if (is_autoselect) {
|
||||||
|
@ -322,8 +323,7 @@ void BuildMediaTag(const MediaPlaylist& playlist,
|
||||||
|
|
||||||
const std::vector<std::string>& characteristics = playlist.characteristics();
|
const std::vector<std::string>& characteristics = playlist.characteristics();
|
||||||
if (!characteristics.empty()) {
|
if (!characteristics.empty()) {
|
||||||
tag.AddQuotedString("CHARACTERISTICS",
|
tag.AddQuotedString("CHARACTERISTICS", absl::StrJoin(characteristics, ","));
|
||||||
base::JoinString(characteristics, ","));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MediaPlaylist::MediaPlaylistStreamType kAudio =
|
const MediaPlaylist::MediaPlaylistStreamType kAudio =
|
||||||
|
@ -486,7 +486,7 @@ void AppendPlaylists(const std::string& default_audio_language,
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
MasterPlaylist::MasterPlaylist(const std::string& file_name,
|
MasterPlaylist::MasterPlaylist(const std::filesystem::path& file_name,
|
||||||
const std::string& default_audio_language,
|
const std::string& default_audio_language,
|
||||||
const std::string& default_text_language,
|
const std::string& default_text_language,
|
||||||
bool is_independent_segments)
|
bool is_independent_segments)
|
||||||
|
@ -514,12 +514,9 @@ bool MasterPlaylist::WriteMasterPlaylist(
|
||||||
if (content == written_playlist_)
|
if (content == written_playlist_)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
std::string file_path =
|
auto file_path = std::filesystem::u8path(output_dir) / file_name_;
|
||||||
base::FilePath::FromUTF8Unsafe(output_dir)
|
if (!File::WriteFileAtomically(file_path.string().c_str(), content)) {
|
||||||
.Append(base::FilePath::FromUTF8Unsafe(file_name_))
|
LOG(ERROR) << "Failed to write master playlist to: " << file_path.string();
|
||||||
.AsUTF8Unsafe();
|
|
||||||
if (!File::WriteFileAtomically(file_path.c_str(), content)) {
|
|
||||||
LOG(ERROR) << "Failed to write master playlist to: " << file_path;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
written_playlist_ = content;
|
written_playlist_ = content;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#ifndef PACKAGER_HLS_BASE_MASTER_PLAYLIST_H_
|
#ifndef PACKAGER_HLS_BASE_MASTER_PLAYLIST_H_
|
||||||
#define PACKAGER_HLS_BASE_MASTER_PLAYLIST_H_
|
#define PACKAGER_HLS_BASE_MASTER_PLAYLIST_H_
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -24,7 +25,7 @@ class MasterPlaylist {
|
||||||
/// be tagged with 'DEFAULT'.
|
/// be tagged with 'DEFAULT'.
|
||||||
/// @param default_text_language determines the text rendition that should be
|
/// @param default_text_language determines the text rendition that should be
|
||||||
/// tagged with 'DEFAULT'.
|
/// tagged with 'DEFAULT'.
|
||||||
MasterPlaylist(const std::string& file_name,
|
MasterPlaylist(const std::filesystem::path& file_name,
|
||||||
const std::string& default_audio_language,
|
const std::string& default_audio_language,
|
||||||
const std::string& default_text_language,
|
const std::string& default_text_language,
|
||||||
const bool is_independent_segments);
|
const bool is_independent_segments);
|
||||||
|
@ -48,7 +49,7 @@ class MasterPlaylist {
|
||||||
MasterPlaylist& operator=(const MasterPlaylist&) = delete;
|
MasterPlaylist& operator=(const MasterPlaylist&) = delete;
|
||||||
|
|
||||||
std::string written_playlist_;
|
std::string written_playlist_;
|
||||||
const std::string file_name_;
|
const std::filesystem::path file_name_;
|
||||||
const std::string default_audio_language_;
|
const std::string default_audio_language_;
|
||||||
const std::string default_text_language_;
|
const std::string default_text_language_;
|
||||||
bool is_independent_segments_;
|
bool is_independent_segments_;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "packager/base/files/file_path.h"
|
#include <filesystem>
|
||||||
#include "packager/file/file.h"
|
#include "packager/file/file.h"
|
||||||
#include "packager/hls/base/master_playlist.h"
|
#include "packager/hls/base/master_playlist.h"
|
||||||
#include "packager/hls/base/media_playlist.h"
|
#include "packager/hls/base/media_playlist.h"
|
||||||
|
@ -17,9 +17,9 @@
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace hls {
|
namespace hls {
|
||||||
|
|
||||||
using base::FilePath;
|
|
||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
using ::testing::AtLeast;
|
using ::testing::AtLeast;
|
||||||
|
using ::testing::DoAll;
|
||||||
using ::testing::NotNull;
|
using ::testing::NotNull;
|
||||||
using ::testing::Return;
|
using ::testing::Return;
|
||||||
using ::testing::ReturnRef;
|
using ::testing::ReturnRef;
|
||||||
|
@ -138,20 +138,18 @@ class MasterPlaylistTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
MasterPlaylistTest()
|
MasterPlaylistTest()
|
||||||
: master_playlist_(new MasterPlaylist(kDefaultMasterPlaylistName,
|
: master_playlist_(new MasterPlaylist(kDefaultMasterPlaylistName,
|
||||||
kDefaultAudioLanguage,
|
kDefaultAudioLanguage,
|
||||||
kDefaultTextLanguage,
|
kDefaultTextLanguage,
|
||||||
!kIsIndependentSegments)),
|
!kIsIndependentSegments)),
|
||||||
test_output_dir_("memory://test_dir"),
|
test_output_dir_("memory://test_dir"),
|
||||||
master_playlist_path_(
|
master_playlist_path_(std::filesystem::u8path(test_output_dir_) /
|
||||||
FilePath::FromUTF8Unsafe(test_output_dir_)
|
kDefaultMasterPlaylistName) {}
|
||||||
.Append(FilePath::FromUTF8Unsafe(kDefaultMasterPlaylistName))
|
|
||||||
.AsUTF8Unsafe()) {}
|
|
||||||
|
|
||||||
void SetUp() override { SetPackagerVersionForTesting("test"); }
|
void SetUp() override { SetPackagerVersionForTesting("test"); }
|
||||||
|
|
||||||
std::unique_ptr<MasterPlaylist> master_playlist_;
|
std::unique_ptr<MasterPlaylist> master_playlist_;
|
||||||
std::string test_output_dir_;
|
std::string test_output_dir_;
|
||||||
std::string master_playlist_path_;
|
std::filesystem::path master_playlist_path_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideo) {
|
TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideo) {
|
||||||
|
@ -166,7 +164,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideo) {
|
||||||
{mock_playlist.get()}));
|
{mock_playlist.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -200,7 +199,8 @@ TEST_F(MasterPlaylistTest,
|
||||||
{mock_playlist.get()}));
|
{mock_playlist.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -229,7 +229,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneVideoWithFrameRate) {
|
||||||
{mock_playlist.get()}));
|
{mock_playlist.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -257,7 +258,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistOneIframePlaylist) {
|
||||||
{mock_playlist.get()}));
|
{mock_playlist.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -312,7 +314,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudio) {
|
||||||
spanish_playlist.get()}));
|
spanish_playlist.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -372,7 +375,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMultipleAudioGroups) {
|
||||||
{video_playlist.get(), eng_lo_playlist.get(), eng_hi_playlist.get()}));
|
{video_playlist.get(), eng_lo_playlist.get(), eng_hi_playlist.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -419,7 +423,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistSameAudioGroupSameLanguage) {
|
||||||
{video_playlist.get(), eng_lo_playlist.get(), eng_hi_playlist.get()}));
|
{video_playlist.get(), eng_lo_playlist.get(), eng_hi_playlist.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -464,7 +469,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideosAndTexts) {
|
||||||
{video1.get(), video2.get(), text_eng.get(), text_fr.get()}));
|
{video1.get(), video2.get(), text_eng.get(), text_fr.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -506,7 +512,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextWithCharacteritics) {
|
||||||
{video.get(), text.get()}));
|
{video.get(), text.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -548,7 +555,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndDvsAudio) {
|
||||||
kBaseUrl, test_output_dir_, {video.get(), dvs_audio.get(), audio.get()}));
|
kBaseUrl, test_output_dir_, {video.get(), dvs_audio.get(), audio.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -590,7 +598,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndTextGroups) {
|
||||||
{video.get(), text_eng.get(), text_fr.get()}));
|
{video.get(), text_eng.get(), text_fr.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -636,7 +645,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistVideoAndAudioAndText) {
|
||||||
kBaseUrl, test_output_dir_, {video.get(), audio.get(), text.get()}));
|
kBaseUrl, test_output_dir_, {video.get(), audio.get(), text.get()}));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -709,7 +719,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistMixedPlaylistsDifferentGroups) {
|
||||||
media_playlist_list));
|
media_playlist_list));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -812,7 +823,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistAudioOnly) {
|
||||||
media_playlist_list));
|
media_playlist_list));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -864,7 +876,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistAudioOnlyJOC) {
|
||||||
media_playlist_list));
|
media_playlist_list));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -916,7 +929,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistAudioOnlyAC4IMS) {
|
||||||
media_playlist_list));
|
media_playlist_list));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
@ -969,7 +983,8 @@ TEST_F(MasterPlaylistTest, WriteMasterPlaylistAudioOnlyAC4CBI) {
|
||||||
media_playlist_list));
|
media_playlist_list));
|
||||||
|
|
||||||
std::string actual;
|
std::string actual;
|
||||||
ASSERT_TRUE(File::ReadFileToString(master_playlist_path_.c_str(), &actual));
|
ASSERT_TRUE(
|
||||||
|
File::ReadFileToString(master_playlist_path_.string().c_str(), &actual));
|
||||||
|
|
||||||
const std::string expected =
|
const std::string expected =
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
|
|
|
@ -12,9 +12,9 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
#include <absl/strings/numbers.h>
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include <absl/strings/str_format.h>
|
||||||
#include "packager/base/strings/stringprintf.h"
|
#include <glog/logging.h>
|
||||||
#include "packager/file/file.h"
|
#include "packager/file/file.h"
|
||||||
#include "packager/hls/base/tag.h"
|
#include "packager/hls/base/tag.h"
|
||||||
#include "packager/media/base/language_utils.h"
|
#include "packager/media/base/language_utils.h"
|
||||||
|
@ -112,12 +112,12 @@ std::string CreatePlaylistHeader(
|
||||||
std::string version_line;
|
std::string version_line;
|
||||||
if (!version.empty()) {
|
if (!version.empty()) {
|
||||||
version_line =
|
version_line =
|
||||||
base::StringPrintf("## Generated with %s version %s\n",
|
absl::StrFormat("## Generated with %s version %s\n",
|
||||||
GetPackagerProjectUrl().c_str(), version.c_str());
|
GetPackagerProjectUrl().c_str(), version.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6 is required for EXT-X-MAP without EXT-X-I-FRAMES-ONLY.
|
// 6 is required for EXT-X-MAP without EXT-X-I-FRAMES-ONLY.
|
||||||
std::string header = base::StringPrintf(
|
std::string header = absl::StrFormat(
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
"%s"
|
"%s"
|
||||||
|
@ -133,20 +133,21 @@ std::string CreatePlaylistHeader(
|
||||||
break;
|
break;
|
||||||
case HlsPlaylistType::kLive:
|
case HlsPlaylistType::kLive:
|
||||||
if (media_sequence_number > 0) {
|
if (media_sequence_number > 0) {
|
||||||
base::StringAppendF(&header, "#EXT-X-MEDIA-SEQUENCE:%d\n",
|
absl::StrAppendFormat(&header, "#EXT-X-MEDIA-SEQUENCE:%d\n",
|
||||||
media_sequence_number);
|
media_sequence_number);
|
||||||
}
|
}
|
||||||
if (discontinuity_sequence_number > 0) {
|
if (discontinuity_sequence_number > 0) {
|
||||||
base::StringAppendF(&header, "#EXT-X-DISCONTINUITY-SEQUENCE:%d\n",
|
absl::StrAppendFormat(&header, "#EXT-X-DISCONTINUITY-SEQUENCE:%d\n",
|
||||||
discontinuity_sequence_number);
|
discontinuity_sequence_number);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOTREACHED() << "Unexpected MediaPlaylistType " << static_cast<int>(type);
|
NOTIMPLEMENTED() << "Unexpected MediaPlaylistType "
|
||||||
|
<< static_cast<int>(type);
|
||||||
}
|
}
|
||||||
if (stream_type ==
|
if (stream_type ==
|
||||||
MediaPlaylist::MediaPlaylistStreamType::kVideoIFramesOnly) {
|
MediaPlaylist::MediaPlaylistStreamType::kVideoIFramesOnly) {
|
||||||
base::StringAppendF(&header, "#EXT-X-I-FRAMES-ONLY\n");
|
absl::StrAppendFormat(&header, "#EXT-X-I-FRAMES-ONLY\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put EXT-X-MAP at the end since the rest of the playlist is about the
|
// Put EXT-X-MAP at the end since the rest of the playlist is about the
|
||||||
|
@ -209,17 +210,17 @@ SegmentInfoEntry::SegmentInfoEntry(const std::string& file_name,
|
||||||
previous_segment_end_offset_(previous_segment_end_offset) {}
|
previous_segment_end_offset_(previous_segment_end_offset) {}
|
||||||
|
|
||||||
std::string SegmentInfoEntry::ToString() {
|
std::string SegmentInfoEntry::ToString() {
|
||||||
std::string result = base::StringPrintf("#EXTINF:%.3f,", duration_seconds_);
|
std::string result = absl::StrFormat("#EXTINF:%.3f,", duration_seconds_);
|
||||||
|
|
||||||
if (use_byte_range_) {
|
if (use_byte_range_) {
|
||||||
base::StringAppendF(&result, "\n#EXT-X-BYTERANGE:%" PRIu64,
|
absl::StrAppendFormat(&result, "\n#EXT-X-BYTERANGE:%" PRIu64,
|
||||||
segment_file_size_);
|
segment_file_size_);
|
||||||
if (previous_segment_end_offset_ + 1 != start_byte_offset_) {
|
if (previous_segment_end_offset_ + 1 != start_byte_offset_) {
|
||||||
base::StringAppendF(&result, "@%" PRIu64, start_byte_offset_);
|
absl::StrAppendFormat(&result, "@%" PRIu64, start_byte_offset_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
base::StringAppendF(&result, "\n%s", file_name_.c_str());
|
absl::StrAppendFormat(&result, "\n%s", file_name_.c_str());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -445,7 +446,7 @@ void MediaPlaylist::AddKeyFrame(int64_t timestamp,
|
||||||
stream_type_ = MediaPlaylistStreamType::kVideoIFramesOnly;
|
stream_type_ = MediaPlaylistStreamType::kVideoIFramesOnly;
|
||||||
use_byte_range_ = true;
|
use_byte_range_ = true;
|
||||||
}
|
}
|
||||||
key_frames_.push_back({timestamp, start_byte_offset, size});
|
key_frames_.push_back({timestamp, start_byte_offset, size, std::string("")});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaPlaylist::AddEncryptionInfo(MediaPlaylist::EncryptionMethod method,
|
void MediaPlaylist::AddEncryptionInfo(MediaPlaylist::EncryptionMethod method,
|
||||||
|
@ -469,7 +470,7 @@ void MediaPlaylist::AddPlacementOpportunity() {
|
||||||
entries_.emplace_back(new PlacementOpportunityEntry());
|
entries_.emplace_back(new PlacementOpportunityEntry());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaPlaylist::WriteToFile(const std::string& file_path) {
|
bool MediaPlaylist::WriteToFile(const std::filesystem::path& file_path) {
|
||||||
if (!target_duration_set_) {
|
if (!target_duration_set_) {
|
||||||
SetTargetDuration(ceil(GetLongestSegmentDuration()));
|
SetTargetDuration(ceil(GetLongestSegmentDuration()));
|
||||||
}
|
}
|
||||||
|
@ -479,14 +480,14 @@ bool MediaPlaylist::WriteToFile(const std::string& file_path) {
|
||||||
media_sequence_number_, discontinuity_sequence_number_);
|
media_sequence_number_, discontinuity_sequence_number_);
|
||||||
|
|
||||||
for (const auto& entry : entries_)
|
for (const auto& entry : entries_)
|
||||||
base::StringAppendF(&content, "%s\n", entry->ToString().c_str());
|
absl::StrAppendFormat(&content, "%s\n", entry->ToString().c_str());
|
||||||
|
|
||||||
if (hls_params_.playlist_type == HlsPlaylistType::kVod) {
|
if (hls_params_.playlist_type == HlsPlaylistType::kVod) {
|
||||||
content += "#EXT-X-ENDLIST\n";
|
content += "#EXT-X-ENDLIST\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File::WriteFileAtomically(file_path.c_str(), content)) {
|
if (!File::WriteFileAtomically(file_path.string().c_str(), content)) {
|
||||||
LOG(ERROR) << "Failed to write playlist to: " << file_path;
|
LOG(ERROR) << "Failed to write playlist to: " << file_path.string();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -691,7 +692,8 @@ void MediaPlaylist::SlideWindow() {
|
||||||
} else if (entry_type == HlsEntry::EntryType::kExtDiscontinuity) {
|
} else if (entry_type == HlsEntry::EntryType::kExtDiscontinuity) {
|
||||||
++discontinuity_sequence_number_;
|
++discontinuity_sequence_number_;
|
||||||
} else {
|
} else {
|
||||||
DCHECK_EQ(entry_type, HlsEntry::EntryType::kExtInf);
|
DCHECK_EQ(static_cast<int>(entry_type),
|
||||||
|
static_cast<int>(HlsEntry::EntryType::kExtInf));
|
||||||
|
|
||||||
const SegmentInfoEntry& segment_info =
|
const SegmentInfoEntry& segment_info =
|
||||||
*reinterpret_cast<SegmentInfoEntry*>(last->get());
|
*reinterpret_cast<SegmentInfoEntry*>(last->get());
|
||||||
|
|
|
@ -7,13 +7,14 @@
|
||||||
#ifndef PACKAGER_HLS_BASE_MEDIA_PLAYLIST_H_
|
#ifndef PACKAGER_HLS_BASE_MEDIA_PLAYLIST_H_
|
||||||
#define PACKAGER_HLS_BASE_MEDIA_PLAYLIST_H_
|
#define PACKAGER_HLS_BASE_MEDIA_PLAYLIST_H_
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/macros.h"
|
|
||||||
#include "packager/hls/public/hls_params.h"
|
#include "packager/hls/public/hls_params.h"
|
||||||
|
#include "packager/macros.h"
|
||||||
#include "packager/mpd/base/bandwidth_estimator.h"
|
#include "packager/mpd/base/bandwidth_estimator.h"
|
||||||
#include "packager/mpd/base/media_info.pb.h"
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
|
|
||||||
|
@ -160,7 +161,7 @@ class MediaPlaylist {
|
||||||
/// @param file_path is the output file path accepted by the File
|
/// @param file_path is the output file path accepted by the File
|
||||||
/// implementation.
|
/// implementation.
|
||||||
/// @return true on success, false otherwise.
|
/// @return true on success, false otherwise.
|
||||||
virtual bool WriteToFile(const std::string& file_path);
|
virtual bool WriteToFile(const std::filesystem::path& file_path);
|
||||||
|
|
||||||
/// If bitrate is specified in MediaInfo then it will use that value.
|
/// If bitrate is specified in MediaInfo then it will use that value.
|
||||||
/// Otherwise, returns the max bitrate.
|
/// Otherwise, returns the max bitrate.
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "packager/base/strings/stringprintf.h"
|
#include <absl/strings/str_format.h>
|
||||||
#include "packager/file/file.h"
|
#include "packager/file/file.h"
|
||||||
#include "packager/file/file_closer.h"
|
#include "packager/file/file_closer.h"
|
||||||
#include "packager/file/file_test_util.h"
|
#include "packager/file/file_test_util.h"
|
||||||
|
@ -1032,8 +1032,8 @@ class MediaPlaylistDeleteSegmentsTest
|
||||||
|
|
||||||
std::string GetSegmentName(int index) {
|
std::string GetSegmentName(int index) {
|
||||||
if (segment_template_.find("$Time$") != std::string::npos)
|
if (segment_template_.find("$Time$") != std::string::npos)
|
||||||
return base::StringPrintf(kStringPrintTemplate, GetTime(index));
|
return absl::StrFormat(kStringPrintTemplate, GetTime(index));
|
||||||
return base::StringPrintf(kStringPrintTemplate, index + 1);
|
return absl::StrFormat(kStringPrintTemplate, index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SegmentDeleted(const std::string& segment_name) {
|
bool SegmentDeleted(const std::string& segment_name) {
|
||||||
|
|
|
@ -42,7 +42,7 @@ class MockMediaPlaylist : public MediaPlaylist {
|
||||||
const std::string& key_format,
|
const std::string& key_format,
|
||||||
const std::string& key_format_versions));
|
const std::string& key_format_versions));
|
||||||
MOCK_METHOD0(AddPlacementOpportunity, void());
|
MOCK_METHOD0(AddPlacementOpportunity, void());
|
||||||
MOCK_METHOD1(WriteToFile, bool(const std::string& file_path));
|
MOCK_METHOD1(WriteToFile, bool(const std::filesystem::path& file_path));
|
||||||
MOCK_CONST_METHOD0(MaxBitrate, uint64_t());
|
MOCK_CONST_METHOD0(MaxBitrate, uint64_t());
|
||||||
MOCK_CONST_METHOD0(AvgBitrate, uint64_t());
|
MOCK_CONST_METHOD0(AvgBitrate, uint64_t());
|
||||||
MOCK_CONST_METHOD0(GetLongestSegmentDuration, double());
|
MOCK_CONST_METHOD0(GetLongestSegmentDuration, double());
|
||||||
|
|
|
@ -6,31 +6,29 @@
|
||||||
|
|
||||||
#include "packager/hls/base/simple_hls_notifier.h"
|
#include "packager/hls/base/simple_hls_notifier.h"
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <absl/flags/flag.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "packager/base/base64.h"
|
#include <absl/strings/escaping.h>
|
||||||
#include "packager/base/files/file_path.h"
|
#include <absl/strings/numbers.h>
|
||||||
#include "packager/base/logging.h"
|
#include <glog/logging.h>
|
||||||
#include "packager/base/optional.h"
|
#include <filesystem>
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include <optional>
|
||||||
#include "packager/base/strings/stringprintf.h"
|
#include "packager/file/file_util.h"
|
||||||
#include "packager/hls/base/media_playlist.h"
|
|
||||||
#include "packager/media/base/protection_system_ids.h"
|
#include "packager/media/base/protection_system_ids.h"
|
||||||
#include "packager/media/base/protection_system_specific_info.h"
|
#include "packager/media/base/protection_system_specific_info.h"
|
||||||
#include "packager/media/base/proto_json_util.h"
|
#include "packager/media/base/proto_json_util.h"
|
||||||
#include "packager/media/base/widevine_pssh_data.pb.h"
|
#include "packager/media/base/widevine_pssh_data.pb.h"
|
||||||
|
|
||||||
DEFINE_bool(enable_legacy_widevine_hls_signaling,
|
ABSL_FLAG(bool,
|
||||||
false,
|
enable_legacy_widevine_hls_signaling,
|
||||||
"Specifies whether Legacy Widevine HLS, i.e. v1 is signalled in "
|
false,
|
||||||
"the media playlist. Applies to Widevine protection system in HLS "
|
"Specifies whether Legacy Widevine HLS, i.e. v1 is signalled in "
|
||||||
"with SAMPLE-AES only.");
|
"the media playlist. Applies to Widevine protection system in HLS "
|
||||||
|
"with SAMPLE-AES only.");
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
using base::FilePath;
|
|
||||||
|
|
||||||
namespace hls {
|
namespace hls {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -41,18 +39,18 @@ const char kWidevineDashIfIopUUID[] =
|
||||||
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
|
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
|
||||||
|
|
||||||
bool IsWidevineSystemId(const std::vector<uint8_t>& system_id) {
|
bool IsWidevineSystemId(const std::vector<uint8_t>& system_id) {
|
||||||
return system_id.size() == arraysize(media::kWidevineSystemId) &&
|
return system_id.size() == std::size(media::kWidevineSystemId) &&
|
||||||
std::equal(system_id.begin(), system_id.end(),
|
std::equal(system_id.begin(), system_id.end(),
|
||||||
media::kWidevineSystemId);
|
media::kWidevineSystemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsCommonSystemId(const std::vector<uint8_t>& system_id) {
|
bool IsCommonSystemId(const std::vector<uint8_t>& system_id) {
|
||||||
return system_id.size() == arraysize(media::kCommonSystemId) &&
|
return system_id.size() == std::size(media::kCommonSystemId) &&
|
||||||
std::equal(system_id.begin(), system_id.end(), media::kCommonSystemId);
|
std::equal(system_id.begin(), system_id.end(), media::kCommonSystemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsFairPlaySystemId(const std::vector<uint8_t>& system_id) {
|
bool IsFairPlaySystemId(const std::vector<uint8_t>& system_id) {
|
||||||
return system_id.size() == arraysize(media::kFairPlaySystemId) &&
|
return system_id.size() == std::size(media::kFairPlaySystemId) &&
|
||||||
std::equal(system_id.begin(), system_id.end(),
|
std::equal(system_id.begin(), system_id.end(),
|
||||||
media::kFairPlaySystemId);
|
media::kFairPlaySystemId);
|
||||||
}
|
}
|
||||||
|
@ -60,7 +58,7 @@ bool IsFairPlaySystemId(const std::vector<uint8_t>& system_id) {
|
||||||
std::string Base64EncodeData(const std::string& prefix,
|
std::string Base64EncodeData(const std::string& prefix,
|
||||||
const std::string& data) {
|
const std::string& data) {
|
||||||
std::string data_base64;
|
std::string data_base64;
|
||||||
base::Base64Encode(data, &data_base64);
|
absl::Base64Escape(data, &data_base64);
|
||||||
return prefix + data_base64;
|
return prefix + data_base64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,33 +66,13 @@ std::string VectorToString(const std::vector<uint8_t>& v) {
|
||||||
return std::string(v.begin(), v.end());
|
return std::string(v.begin(), v.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rkuroiwa): Dedup these with the functions in MpdBuilder.
|
|
||||||
// If |media_path| is contained in |parent_path|, then
|
|
||||||
// Strips the common path and keep only the relative part of |media_path|.
|
|
||||||
// e.g. if |parent_path| is /some/parent/ and
|
|
||||||
// |media_path| is /some/parent/abc/child/item.ext,
|
|
||||||
// abc/child/item.ext is returned.
|
|
||||||
// else
|
|
||||||
// Returns |media_path|.
|
|
||||||
// The path separator of the output is also changed to "/" if it is not.
|
|
||||||
std::string MakePathRelative(const std::string& media_path,
|
|
||||||
const FilePath& parent_path) {
|
|
||||||
FilePath relative_path;
|
|
||||||
const FilePath child_path = FilePath::FromUTF8Unsafe(media_path);
|
|
||||||
const bool is_child =
|
|
||||||
parent_path.AppendRelativePath(child_path, &relative_path);
|
|
||||||
if (!is_child)
|
|
||||||
relative_path = child_path;
|
|
||||||
return relative_path.NormalizePathSeparatorsTo('/').AsUTF8Unsafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Segment URL is relative to either output directory or the directory
|
// Segment URL is relative to either output directory or the directory
|
||||||
// containing the media playlist depends on whether base_url is set.
|
// containing the media playlist depends on whether base_url is set.
|
||||||
std::string GenerateSegmentUrl(const std::string& segment_name,
|
std::string GenerateSegmentUrl(const std::string& segment_name,
|
||||||
const std::string& base_url,
|
const std::string& base_url,
|
||||||
const std::string& output_dir,
|
const std::string& output_dir,
|
||||||
const std::string& playlist_file_name) {
|
const std::string& playlist_file_name) {
|
||||||
FilePath output_path = FilePath::FromUTF8Unsafe(output_dir);
|
auto output_path = std::filesystem::u8path(output_dir);
|
||||||
if (!base_url.empty()) {
|
if (!base_url.empty()) {
|
||||||
// Media segment URL is base_url + segment path relative to output
|
// Media segment URL is base_url + segment path relative to output
|
||||||
// directory.
|
// directory.
|
||||||
|
@ -102,10 +80,8 @@ std::string GenerateSegmentUrl(const std::string& segment_name,
|
||||||
}
|
}
|
||||||
// Media segment URL is segment path relative to the directory containing the
|
// Media segment URL is segment path relative to the directory containing the
|
||||||
// playlist.
|
// playlist.
|
||||||
const FilePath playlist_dir =
|
const std::filesystem::path playlist_dir =
|
||||||
output_path.Append(FilePath::FromUTF8Unsafe(playlist_file_name))
|
(output_path / playlist_file_name).parent_path() / "";
|
||||||
.DirName()
|
|
||||||
.AsEndingWithSeparator();
|
|
||||||
return MakePathRelative(segment_name, playlist_dir);
|
return MakePathRelative(segment_name, playlist_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,10 +144,12 @@ bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_box,
|
||||||
|
|
||||||
// Place the current |key_id| to the front and converts all key_id to hex
|
// Place the current |key_id| to the front and converts all key_id to hex
|
||||||
// format.
|
// format.
|
||||||
widevine_header.add_key_ids(base::HexEncode(key_id.data(), key_id.size()));
|
widevine_header.add_key_ids(absl::BytesToHexString(absl::string_view(
|
||||||
|
reinterpret_cast<const char*>(key_id.data()), key_id.size())));
|
||||||
for (const std::string& key_id_in_pssh : pssh_proto.key_id()) {
|
for (const std::string& key_id_in_pssh : pssh_proto.key_id()) {
|
||||||
const std::string key_id_hex =
|
const std::string key_id_hex = absl::BytesToHexString(
|
||||||
base::HexEncode(key_id_in_pssh.data(), key_id_in_pssh.size());
|
absl::string_view(reinterpret_cast<const char*>(key_id_in_pssh.data()),
|
||||||
|
key_id_in_pssh.size()));
|
||||||
if (widevine_header.key_ids(0) != key_id_hex)
|
if (widevine_header.key_ids(0) != key_id_hex)
|
||||||
widevine_header.add_key_ids(key_id_hex);
|
widevine_header.add_key_ids(key_id_hex);
|
||||||
}
|
}
|
||||||
|
@ -180,7 +158,7 @@ bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_box,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
base::Optional<MediaPlaylist::EncryptionMethod> StringToEncryptionMethod(
|
std::optional<MediaPlaylist::EncryptionMethod> StringToEncryptionMethod(
|
||||||
const std::string& method) {
|
const std::string& method) {
|
||||||
if (method == "cenc") {
|
if (method == "cenc") {
|
||||||
return MediaPlaylist::EncryptionMethod::kSampleAesCenc;
|
return MediaPlaylist::EncryptionMethod::kSampleAesCenc;
|
||||||
|
@ -192,7 +170,7 @@ base::Optional<MediaPlaylist::EncryptionMethod> StringToEncryptionMethod(
|
||||||
// cbca is a place holder for sample aes.
|
// cbca is a place holder for sample aes.
|
||||||
return MediaPlaylist::EncryptionMethod::kSampleAes;
|
return MediaPlaylist::EncryptionMethod::kSampleAes;
|
||||||
}
|
}
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifyEncryptionToMediaPlaylist(
|
void NotifyEncryptionToMediaPlaylist(
|
||||||
|
@ -205,11 +183,15 @@ void NotifyEncryptionToMediaPlaylist(
|
||||||
MediaPlaylist* media_playlist) {
|
MediaPlaylist* media_playlist) {
|
||||||
std::string iv_string;
|
std::string iv_string;
|
||||||
if (!iv.empty()) {
|
if (!iv.empty()) {
|
||||||
iv_string = "0x" + base::HexEncode(iv.data(), iv.size());
|
iv_string =
|
||||||
|
"0x" + absl::BytesToHexString(absl::string_view(
|
||||||
|
reinterpret_cast<const char*>(iv.data()), iv.size()));
|
||||||
}
|
}
|
||||||
std::string key_id_string;
|
std::string key_id_string;
|
||||||
if (!key_id.empty()) {
|
if (!key_id.empty()) {
|
||||||
key_id_string = "0x" + base::HexEncode(key_id.data(), key_id.size());
|
key_id_string = "0x" + absl::BytesToHexString(absl::string_view(
|
||||||
|
reinterpret_cast<const char*>(key_id.data()),
|
||||||
|
key_id.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
media_playlist->AddEncryptionInfo(
|
media_playlist->AddEncryptionInfo(
|
||||||
|
@ -225,7 +207,7 @@ bool HandleWidevineKeyFormats(
|
||||||
const std::vector<uint8_t>& iv,
|
const std::vector<uint8_t>& iv,
|
||||||
const std::vector<uint8_t>& protection_system_specific_data,
|
const std::vector<uint8_t>& protection_system_specific_data,
|
||||||
MediaPlaylist* media_playlist) {
|
MediaPlaylist* media_playlist) {
|
||||||
if (FLAGS_enable_legacy_widevine_hls_signaling &&
|
if (absl::GetFlag(FLAGS_enable_legacy_widevine_hls_signaling) &&
|
||||||
encryption_method == MediaPlaylist::EncryptionMethod::kSampleAes) {
|
encryption_method == MediaPlaylist::EncryptionMethod::kSampleAes) {
|
||||||
// This format allows SAMPLE-AES only.
|
// This format allows SAMPLE-AES only.
|
||||||
std::string key_uri_data;
|
std::string key_uri_data;
|
||||||
|
@ -253,12 +235,9 @@ bool HandleWidevineKeyFormats(
|
||||||
|
|
||||||
bool WriteMediaPlaylist(const std::string& output_dir,
|
bool WriteMediaPlaylist(const std::string& output_dir,
|
||||||
MediaPlaylist* playlist) {
|
MediaPlaylist* playlist) {
|
||||||
std::string file_path =
|
auto file_path = std::filesystem::u8path(output_dir) / playlist->file_name();
|
||||||
FilePath::FromUTF8Unsafe(output_dir)
|
|
||||||
.Append(FilePath::FromUTF8Unsafe(playlist->file_name()))
|
|
||||||
.AsUTF8Unsafe();
|
|
||||||
if (!playlist->WriteToFile(file_path)) {
|
if (!playlist->WriteToFile(file_path)) {
|
||||||
LOG(ERROR) << "Failed to write playlist " << file_path;
|
LOG(ERROR) << "Failed to write playlist " << file_path.string();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -280,18 +259,17 @@ std::unique_ptr<MediaPlaylist> MediaPlaylistFactory::Create(
|
||||||
SimpleHlsNotifier::SimpleHlsNotifier(const HlsParams& hls_params)
|
SimpleHlsNotifier::SimpleHlsNotifier(const HlsParams& hls_params)
|
||||||
: HlsNotifier(hls_params),
|
: HlsNotifier(hls_params),
|
||||||
media_playlist_factory_(new MediaPlaylistFactory()) {
|
media_playlist_factory_(new MediaPlaylistFactory()) {
|
||||||
const base::FilePath master_playlist_path(
|
const auto master_playlist_path =
|
||||||
base::FilePath::FromUTF8Unsafe(hls_params.master_playlist_output));
|
std::filesystem::u8path(hls_params.master_playlist_output);
|
||||||
master_playlist_dir_ = master_playlist_path.DirName().AsUTF8Unsafe();
|
master_playlist_dir_ = master_playlist_path.parent_path().string();
|
||||||
const std::string& default_audio_langauge = hls_params.default_language;
|
const std::string& default_audio_langauge = hls_params.default_language;
|
||||||
const std::string& default_text_language =
|
const std::string& default_text_language =
|
||||||
hls_params.default_text_language.empty()
|
hls_params.default_text_language.empty()
|
||||||
? hls_params.default_language
|
? hls_params.default_language
|
||||||
: hls_params.default_text_language;
|
: hls_params.default_text_language;
|
||||||
master_playlist_.reset(
|
master_playlist_.reset(new MasterPlaylist(
|
||||||
new MasterPlaylist(master_playlist_path.BaseName().AsUTF8Unsafe(),
|
master_playlist_path.filename(), default_audio_langauge,
|
||||||
default_audio_langauge, default_text_language,
|
default_text_language, hls_params.is_independent_segments));
|
||||||
hls_params.is_independent_segments));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleHlsNotifier::~SimpleHlsNotifier() {}
|
SimpleHlsNotifier::~SimpleHlsNotifier() {}
|
||||||
|
@ -308,7 +286,7 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info,
|
||||||
DCHECK(stream_id);
|
DCHECK(stream_id);
|
||||||
|
|
||||||
const std::string relative_playlist_path = MakePathRelative(
|
const std::string relative_playlist_path = MakePathRelative(
|
||||||
playlist_name, FilePath::FromUTF8Unsafe(master_playlist_dir_));
|
playlist_name, std::filesystem::u8path(master_playlist_dir_));
|
||||||
|
|
||||||
std::unique_ptr<MediaPlaylist> media_playlist =
|
std::unique_ptr<MediaPlaylist> media_playlist =
|
||||||
media_playlist_factory_->Create(hls_params(), relative_playlist_path,
|
media_playlist_factory_->Create(hls_params(), relative_playlist_path,
|
||||||
|
@ -326,7 +304,7 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info,
|
||||||
if (media_info.protected_content().has_protection_scheme()) {
|
if (media_info.protected_content().has_protection_scheme()) {
|
||||||
const std::string& protection_scheme =
|
const std::string& protection_scheme =
|
||||||
media_info.protected_content().protection_scheme();
|
media_info.protected_content().protection_scheme();
|
||||||
base::Optional<MediaPlaylist::EncryptionMethod> enc_method =
|
std::optional<MediaPlaylist::EncryptionMethod> enc_method =
|
||||||
StringToEncryptionMethod(protection_scheme);
|
StringToEncryptionMethod(protection_scheme);
|
||||||
if (!enc_method) {
|
if (!enc_method) {
|
||||||
LOG(ERROR) << "Failed to recognize protection scheme "
|
LOG(ERROR) << "Failed to recognize protection scheme "
|
||||||
|
@ -336,7 +314,7 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info,
|
||||||
encryption_method = enc_method.value();
|
encryption_method = enc_method.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
*stream_id = sequence_number_++;
|
*stream_id = sequence_number_++;
|
||||||
media_playlists_.push_back(media_playlist.get());
|
media_playlists_.push_back(media_playlist.get());
|
||||||
stream_map_[*stream_id].reset(
|
stream_map_[*stream_id].reset(
|
||||||
|
@ -346,7 +324,7 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info,
|
||||||
|
|
||||||
bool SimpleHlsNotifier::NotifySampleDuration(uint32_t stream_id,
|
bool SimpleHlsNotifier::NotifySampleDuration(uint32_t stream_id,
|
||||||
int32_t sample_duration) {
|
int32_t sample_duration) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto stream_iterator = stream_map_.find(stream_id);
|
auto stream_iterator = stream_map_.find(stream_id);
|
||||||
if (stream_iterator == stream_map_.end()) {
|
if (stream_iterator == stream_map_.end()) {
|
||||||
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
||||||
|
@ -363,7 +341,7 @@ bool SimpleHlsNotifier::NotifyNewSegment(uint32_t stream_id,
|
||||||
int64_t duration,
|
int64_t duration,
|
||||||
uint64_t start_byte_offset,
|
uint64_t start_byte_offset,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto stream_iterator = stream_map_.find(stream_id);
|
auto stream_iterator = stream_map_.find(stream_id);
|
||||||
if (stream_iterator == stream_map_.end()) {
|
if (stream_iterator == stream_map_.end()) {
|
||||||
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
||||||
|
@ -412,7 +390,7 @@ bool SimpleHlsNotifier::NotifyKeyFrame(uint32_t stream_id,
|
||||||
int64_t timestamp,
|
int64_t timestamp,
|
||||||
uint64_t start_byte_offset,
|
uint64_t start_byte_offset,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto stream_iterator = stream_map_.find(stream_id);
|
auto stream_iterator = stream_map_.find(stream_id);
|
||||||
if (stream_iterator == stream_map_.end()) {
|
if (stream_iterator == stream_map_.end()) {
|
||||||
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
||||||
|
@ -424,7 +402,7 @@ bool SimpleHlsNotifier::NotifyKeyFrame(uint32_t stream_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimpleHlsNotifier::NotifyCueEvent(uint32_t stream_id, int64_t timestamp) {
|
bool SimpleHlsNotifier::NotifyCueEvent(uint32_t stream_id, int64_t timestamp) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto stream_iterator = stream_map_.find(stream_id);
|
auto stream_iterator = stream_map_.find(stream_id);
|
||||||
if (stream_iterator == stream_map_.end()) {
|
if (stream_iterator == stream_map_.end()) {
|
||||||
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
||||||
|
@ -441,7 +419,7 @@ bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
||||||
const std::vector<uint8_t>& system_id,
|
const std::vector<uint8_t>& system_id,
|
||||||
const std::vector<uint8_t>& iv,
|
const std::vector<uint8_t>& iv,
|
||||||
const std::vector<uint8_t>& protection_system_specific_data) {
|
const std::vector<uint8_t>& protection_system_specific_data) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto stream_iterator = stream_map_.find(stream_id);
|
auto stream_iterator = stream_map_.find(stream_id);
|
||||||
if (stream_iterator == stream_map_.end()) {
|
if (stream_iterator == stream_map_.end()) {
|
||||||
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
||||||
|
@ -493,12 +471,14 @@ bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(WARNING) << "HLS: Ignore unknown or unsupported system ID: "
|
LOG(WARNING) << "HLS: Ignore unknown or unsupported system ID: "
|
||||||
<< base::HexEncode(system_id.data(), system_id.size());
|
<< absl::BytesToHexString(absl::string_view(
|
||||||
|
reinterpret_cast<const char*>(system_id.data()),
|
||||||
|
system_id.size()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimpleHlsNotifier::Flush() {
|
bool SimpleHlsNotifier::Flush() {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
for (MediaPlaylist* playlist : media_playlists_) {
|
for (MediaPlaylist* playlist : media_playlists_) {
|
||||||
playlist->SetTargetDuration(target_duration_);
|
playlist->SetTargetDuration(target_duration_);
|
||||||
if (!WriteMediaPlaylist(master_playlist_dir_, playlist))
|
if (!WriteMediaPlaylist(master_playlist_dir_, playlist))
|
||||||
|
|
|
@ -13,12 +13,13 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/macros.h"
|
#include <absl/synchronization/mutex.h>
|
||||||
#include "packager/base/synchronization/lock.h"
|
|
||||||
#include "packager/hls/base/hls_notifier.h"
|
#include "packager/hls/base/hls_notifier.h"
|
||||||
#include "packager/hls/base/master_playlist.h"
|
#include "packager/hls/base/master_playlist.h"
|
||||||
#include "packager/hls/base/media_playlist.h"
|
#include "packager/hls/base/media_playlist.h"
|
||||||
#include "packager/hls/public/hls_params.h"
|
#include "packager/hls/public/hls_params.h"
|
||||||
|
#include "packager/macros.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace hls {
|
namespace hls {
|
||||||
|
@ -91,7 +92,7 @@ class SimpleHlsNotifier : public HlsNotifier {
|
||||||
|
|
||||||
uint32_t sequence_number_ = 0;
|
uint32_t sequence_number_ = 0;
|
||||||
|
|
||||||
base::Lock lock_;
|
absl::Mutex lock_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(SimpleHlsNotifier);
|
DISALLOW_COPY_AND_ASSIGN(SimpleHlsNotifier);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,23 +7,26 @@
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <absl/flags/declare.h>
|
||||||
|
#include <absl/flags/flag.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "packager/base/base64.h"
|
#include <absl/strings/escaping.h>
|
||||||
#include "packager/base/files/file_path.h"
|
#include <filesystem>
|
||||||
|
#include "packager/flag_saver.h"
|
||||||
#include "packager/hls/base/mock_media_playlist.h"
|
#include "packager/hls/base/mock_media_playlist.h"
|
||||||
#include "packager/hls/base/simple_hls_notifier.h"
|
#include "packager/hls/base/simple_hls_notifier.h"
|
||||||
#include "packager/media/base/protection_system_ids.h"
|
#include "packager/media/base/protection_system_ids.h"
|
||||||
#include "packager/media/base/protection_system_specific_info.h"
|
#include "packager/media/base/protection_system_specific_info.h"
|
||||||
#include "packager/media/base/widevine_pssh_data.pb.h"
|
#include "packager/media/base/widevine_pssh_data.pb.h"
|
||||||
|
|
||||||
DECLARE_bool(enable_legacy_widevine_hls_signaling);
|
ABSL_DECLARE_FLAG(bool, enable_legacy_widevine_hls_signaling);
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace hls {
|
namespace hls {
|
||||||
|
|
||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
|
using ::testing::DoAll;
|
||||||
using ::testing::ElementsAre;
|
using ::testing::ElementsAre;
|
||||||
using ::testing::Eq;
|
using ::testing::Eq;
|
||||||
using ::testing::InSequence;
|
using ::testing::InSequence;
|
||||||
|
@ -245,10 +248,8 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
|
||||||
EXPECT_CALL(*mock_media_playlist, SetTargetDuration(kTargetDuration))
|
EXPECT_CALL(*mock_media_playlist, SetTargetDuration(kTargetDuration))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
WriteToFile(StrEq(
|
WriteToFile(Eq(
|
||||||
base::FilePath::FromUTF8Unsafe(kAnyOutputDir)
|
(std::filesystem::u8path(kAnyOutputDir) / "playlist.m3u8"))))
|
||||||
.Append(base::FilePath::FromUTF8Unsafe("playlist.m3u8"))
|
|
||||||
.AsUTF8Unsafe())))
|
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_TRUE(notifier.Flush());
|
EXPECT_TRUE(notifier.Flush());
|
||||||
}
|
}
|
||||||
|
@ -289,7 +290,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateIdentityKey) {
|
||||||
const std::vector<uint8_t> dummy_pssh_data(10, 'p');
|
const std::vector<uint8_t> dummy_pssh_data(10, 'p');
|
||||||
|
|
||||||
std::string expected_key_uri_base64;
|
std::string expected_key_uri_base64;
|
||||||
base::Base64Encode(std::string(key_id.begin(), key_id.end()),
|
absl::Base64Escape(std::string(key_id.begin(), key_id.end()),
|
||||||
&expected_key_uri_base64);
|
&expected_key_uri_base64);
|
||||||
|
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
|
@ -557,10 +558,8 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegment) {
|
||||||
EXPECT_CALL(*mock_media_playlist, SetTargetDuration(kTargetDuration))
|
EXPECT_CALL(*mock_media_playlist, SetTargetDuration(kTargetDuration))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
WriteToFile(StrEq(
|
WriteToFile(Eq(
|
||||||
base::FilePath::FromUTF8Unsafe(kAnyOutputDir)
|
(std::filesystem::u8path(kAnyOutputDir) / "playlist.m3u8"))))
|
||||||
.Append(base::FilePath::FromUTF8Unsafe("playlist.m3u8"))
|
|
||||||
.AsUTF8Unsafe())))
|
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
hls_params_.playlist_type = GetParam();
|
hls_params_.playlist_type = GetParam();
|
||||||
|
@ -627,18 +626,14 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
|
||||||
EXPECT_CALL(*mock_media_playlist1, SetTargetDuration(kTargetDuration))
|
EXPECT_CALL(*mock_media_playlist1, SetTargetDuration(kTargetDuration))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
EXPECT_CALL(*mock_media_playlist1,
|
EXPECT_CALL(*mock_media_playlist1,
|
||||||
WriteToFile(StrEq(
|
WriteToFile(Eq(
|
||||||
base::FilePath::FromUTF8Unsafe(kAnyOutputDir)
|
(std::filesystem::u8path(kAnyOutputDir) / "playlist1.m3u8"))))
|
||||||
.Append(base::FilePath::FromUTF8Unsafe("playlist1.m3u8"))
|
|
||||||
.AsUTF8Unsafe())))
|
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_CALL(*mock_media_playlist2, SetTargetDuration(kTargetDuration))
|
EXPECT_CALL(*mock_media_playlist2, SetTargetDuration(kTargetDuration))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
EXPECT_CALL(*mock_media_playlist2,
|
EXPECT_CALL(*mock_media_playlist2,
|
||||||
WriteToFile(StrEq(
|
WriteToFile(Eq(
|
||||||
base::FilePath::FromUTF8Unsafe(kAnyOutputDir)
|
(std::filesystem::u8path(kAnyOutputDir) / "playlist2.m3u8"))))
|
||||||
.Append(base::FilePath::FromUTF8Unsafe("playlist2.m3u8"))
|
|
||||||
.AsUTF8Unsafe())))
|
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
*mock_master_playlist_ptr,
|
*mock_master_playlist_ptr,
|
||||||
|
@ -653,10 +648,8 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) {
|
||||||
.WillOnce(Return(kLongestSegmentDuration));
|
.WillOnce(Return(kLongestSegmentDuration));
|
||||||
// Not updating other playlists as target duration does not change.
|
// Not updating other playlists as target duration does not change.
|
||||||
EXPECT_CALL(*mock_media_playlist2,
|
EXPECT_CALL(*mock_media_playlist2,
|
||||||
WriteToFile(StrEq(
|
WriteToFile(Eq(
|
||||||
base::FilePath::FromUTF8Unsafe(kAnyOutputDir)
|
(std::filesystem::u8path(kAnyOutputDir) / "playlist2.m3u8"))))
|
||||||
.Append(base::FilePath::FromUTF8Unsafe("playlist2.m3u8"))
|
|
||||||
.AsUTF8Unsafe())))
|
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_CALL(*mock_master_playlist_ptr, WriteMasterPlaylist(_, _, _))
|
EXPECT_CALL(*mock_master_playlist_ptr, WriteMasterPlaylist(_, _, _))
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
|
@ -673,12 +666,16 @@ class WidevineSimpleHlsNotifierTest : public SimpleHlsNotifierTest,
|
||||||
public WithParamInterface<bool> {
|
public WithParamInterface<bool> {
|
||||||
protected:
|
protected:
|
||||||
WidevineSimpleHlsNotifierTest()
|
WidevineSimpleHlsNotifierTest()
|
||||||
: enable_legacy_widevine_hls_signaling_(GetParam()) {
|
: enable_legacy_widevine_hls_signaling_(GetParam()),
|
||||||
FLAGS_enable_legacy_widevine_hls_signaling =
|
saver(&FLAGS_enable_legacy_widevine_hls_signaling) {
|
||||||
enable_legacy_widevine_hls_signaling_;
|
absl::SetFlag(&FLAGS_enable_legacy_widevine_hls_signaling,
|
||||||
|
enable_legacy_widevine_hls_signaling_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool enable_legacy_widevine_hls_signaling_ = false;
|
bool enable_legacy_widevine_hls_signaling_ = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FlagSaver<bool> saver;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdate) {
|
TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdate) {
|
||||||
|
@ -698,9 +695,9 @@ TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdate) {
|
||||||
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
||||||
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
||||||
};
|
};
|
||||||
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId));
|
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + std::size(kAnyKeyId));
|
||||||
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
||||||
kAnyKeyId + arraysize(kAnyKeyId));
|
kAnyKeyId + std::size(kAnyKeyId));
|
||||||
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
||||||
|
|
||||||
EXPECT_TRUE(!widevine_pssh_data_str.empty());
|
EXPECT_TRUE(!widevine_pssh_data_str.empty());
|
||||||
|
@ -717,11 +714,11 @@ TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdate) {
|
||||||
R"({"key_ids":["11223344112233441122334411223344"],)"
|
R"({"key_ids":["11223344112233441122334411223344"],)"
|
||||||
R"("provider":"someprovider","content_id":"Y29udGVudGlk"})";
|
R"("provider":"someprovider","content_id":"Y29udGVudGlk"})";
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
absl::Base64Escape(kExpectedJson, &expected_json_base64);
|
||||||
|
|
||||||
std::string expected_pssh_base64;
|
std::string expected_pssh_base64;
|
||||||
const std::vector<uint8_t> pssh_box = pssh_builder.CreateBox();
|
const std::vector<uint8_t> pssh_box = pssh_builder.CreateBox();
|
||||||
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
|
absl::Base64Escape(std::string(pssh_box.begin(), pssh_box.end()),
|
||||||
&expected_pssh_base64);
|
&expected_pssh_base64);
|
||||||
|
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
|
@ -763,7 +760,7 @@ TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdateNoKeyidsInPssh) {
|
||||||
R"({"key_ids":["11223344112233441122334411223344"],)"
|
R"({"key_ids":["11223344112233441122334411223344"],)"
|
||||||
R"("provider":"someprovider","content_id":"Y29udGVudGlk"})";
|
R"("provider":"someprovider","content_id":"Y29udGVudGlk"})";
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
absl::Base64Escape(kExpectedJson, &expected_json_base64);
|
||||||
|
|
||||||
media::PsshBoxBuilder pssh_builder;
|
media::PsshBoxBuilder pssh_builder;
|
||||||
pssh_builder.set_pssh_data(pssh_data);
|
pssh_builder.set_pssh_data(pssh_data);
|
||||||
|
@ -772,7 +769,7 @@ TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdateNoKeyidsInPssh) {
|
||||||
|
|
||||||
std::string expected_pssh_base64;
|
std::string expected_pssh_base64;
|
||||||
const std::vector<uint8_t> pssh_box = pssh_builder.CreateBox();
|
const std::vector<uint8_t> pssh_box = pssh_builder.CreateBox();
|
||||||
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
|
absl::Base64Escape(std::string(pssh_box.begin(), pssh_box.end()),
|
||||||
&expected_pssh_base64);
|
&expected_pssh_base64);
|
||||||
|
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
|
@ -793,7 +790,7 @@ TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdateNoKeyidsInPssh) {
|
||||||
};
|
};
|
||||||
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||||
stream_id,
|
stream_id,
|
||||||
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + std::size(kAnyKeyId)),
|
||||||
widevine_system_id_, iv, pssh_box));
|
widevine_system_id_, iv, pssh_box));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,14 +818,14 @@ TEST_P(WidevineSimpleHlsNotifierTest, MultipleKeyIdsNoContentIdInPssh) {
|
||||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
|
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
|
||||||
};
|
};
|
||||||
std::vector<uint8_t> first_keyid(kFirstKeyId,
|
std::vector<uint8_t> first_keyid(kFirstKeyId,
|
||||||
kFirstKeyId + arraysize(kFirstKeyId));
|
kFirstKeyId + std::size(kFirstKeyId));
|
||||||
std::vector<uint8_t> second_keyid(kSecondKeyId,
|
std::vector<uint8_t> second_keyid(kSecondKeyId,
|
||||||
kSecondKeyId + arraysize(kSecondKeyId));
|
kSecondKeyId + std::size(kSecondKeyId));
|
||||||
|
|
||||||
widevine_pssh_data.add_key_id()->assign(kFirstKeyId,
|
widevine_pssh_data.add_key_id()->assign(kFirstKeyId,
|
||||||
kFirstKeyId + arraysize(kFirstKeyId));
|
kFirstKeyId + std::size(kFirstKeyId));
|
||||||
widevine_pssh_data.add_key_id()->assign(
|
widevine_pssh_data.add_key_id()->assign(
|
||||||
kSecondKeyId, kSecondKeyId + arraysize(kSecondKeyId));
|
kSecondKeyId, kSecondKeyId + std::size(kSecondKeyId));
|
||||||
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
||||||
EXPECT_TRUE(!widevine_pssh_data_str.empty());
|
EXPECT_TRUE(!widevine_pssh_data_str.empty());
|
||||||
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
|
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
|
||||||
|
@ -847,11 +844,11 @@ TEST_P(WidevineSimpleHlsNotifierTest, MultipleKeyIdsNoContentIdInPssh) {
|
||||||
R"("11111111111111111111111111111111"],)"
|
R"("11111111111111111111111111111111"],)"
|
||||||
R"("provider":"someprovider"})";
|
R"("provider":"someprovider"})";
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
absl::Base64Escape(kExpectedJson, &expected_json_base64);
|
||||||
|
|
||||||
std::string expected_pssh_base64;
|
std::string expected_pssh_base64;
|
||||||
const std::vector<uint8_t> pssh_box = pssh_builder.CreateBox();
|
const std::vector<uint8_t> pssh_box = pssh_builder.CreateBox();
|
||||||
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
|
absl::Base64Escape(std::string(pssh_box.begin(), pssh_box.end()),
|
||||||
&expected_pssh_base64);
|
&expected_pssh_base64);
|
||||||
|
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
|
@ -893,9 +890,9 @@ TEST_P(WidevineSimpleHlsNotifierTest, CencEncryptionScheme) {
|
||||||
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
||||||
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
||||||
};
|
};
|
||||||
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId));
|
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + std::size(kAnyKeyId));
|
||||||
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
||||||
kAnyKeyId + arraysize(kAnyKeyId));
|
kAnyKeyId + std::size(kAnyKeyId));
|
||||||
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
||||||
|
|
||||||
EXPECT_TRUE(!widevine_pssh_data_str.empty());
|
EXPECT_TRUE(!widevine_pssh_data_str.empty());
|
||||||
|
@ -904,7 +901,7 @@ TEST_P(WidevineSimpleHlsNotifierTest, CencEncryptionScheme) {
|
||||||
|
|
||||||
std::string expected_pssh_base64;
|
std::string expected_pssh_base64;
|
||||||
const std::vector<uint8_t> pssh_box = {'p', 's', 's', 'h'};
|
const std::vector<uint8_t> pssh_box = {'p', 's', 's', 'h'};
|
||||||
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
|
absl::Base64Escape(std::string(pssh_box.begin(), pssh_box.end()),
|
||||||
&expected_pssh_base64);
|
&expected_pssh_base64);
|
||||||
|
|
||||||
EXPECT_CALL(*mock_media_playlist,
|
EXPECT_CALL(*mock_media_playlist,
|
||||||
|
@ -932,9 +929,9 @@ TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdateEmptyIv) {
|
||||||
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
||||||
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44,
|
||||||
};
|
};
|
||||||
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId));
|
std::vector<uint8_t> any_key_id(kAnyKeyId, kAnyKeyId + std::size(kAnyKeyId));
|
||||||
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
||||||
kAnyKeyId + arraysize(kAnyKeyId));
|
kAnyKeyId + std::size(kAnyKeyId));
|
||||||
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
||||||
EXPECT_TRUE(!widevine_pssh_data_str.empty());
|
EXPECT_TRUE(!widevine_pssh_data_str.empty());
|
||||||
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
|
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
|
||||||
|
@ -944,7 +941,7 @@ TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdateEmptyIv) {
|
||||||
R"({"key_ids":["11223344112233441122334411223344"],)"
|
R"({"key_ids":["11223344112233441122334411223344"],)"
|
||||||
R"("provider":"someprovider","content_id":"Y29udGVudGlk"})";
|
R"("provider":"someprovider","content_id":"Y29udGVudGlk"})";
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
absl::Base64Escape(kExpectedJson, &expected_json_base64);
|
||||||
|
|
||||||
media::PsshBoxBuilder pssh_builder;
|
media::PsshBoxBuilder pssh_builder;
|
||||||
pssh_builder.set_pssh_data(pssh_data);
|
pssh_builder.set_pssh_data(pssh_data);
|
||||||
|
@ -971,13 +968,13 @@ TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdateEmptyIv) {
|
||||||
std::vector<uint8_t> pssh_as_vec = pssh_builder.CreateBox();
|
std::vector<uint8_t> pssh_as_vec = pssh_builder.CreateBox();
|
||||||
std::string pssh_in_string(pssh_as_vec.begin(), pssh_as_vec.end());
|
std::string pssh_in_string(pssh_as_vec.begin(), pssh_as_vec.end());
|
||||||
std::string base_64_encoded_pssh;
|
std::string base_64_encoded_pssh;
|
||||||
base::Base64Encode(pssh_in_string, &base_64_encoded_pssh);
|
absl::Base64Escape(pssh_in_string, &base_64_encoded_pssh);
|
||||||
LOG(INFO) << base_64_encoded_pssh;
|
LOG(INFO) << base_64_encoded_pssh;
|
||||||
|
|
||||||
std::vector<uint8_t> empty_iv;
|
std::vector<uint8_t> empty_iv;
|
||||||
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||||
stream_id,
|
stream_id,
|
||||||
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + std::size(kAnyKeyId)),
|
||||||
widevine_system_id_, empty_iv, pssh_builder.CreateBox()));
|
widevine_system_id_, empty_iv, pssh_builder.CreateBox()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,35 +6,34 @@
|
||||||
|
|
||||||
#include "packager/hls/base/tag.h"
|
#include "packager/hls/base/tag.h"
|
||||||
|
|
||||||
|
#include <absl/strings/str_format.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "packager/base/strings/stringprintf.h"
|
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace hls {
|
namespace hls {
|
||||||
|
|
||||||
Tag::Tag(const std::string& name, std::string* buffer) : buffer_(buffer) {
|
Tag::Tag(const std::string& name, std::string* buffer) : buffer_(buffer) {
|
||||||
base::StringAppendF(buffer_, "%s:", name.c_str());
|
absl::StrAppendFormat(buffer_, "%s:", name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tag::AddString(const std::string& key, const std::string& value) {
|
void Tag::AddString(const std::string& key, const std::string& value) {
|
||||||
NextField();
|
NextField();
|
||||||
base::StringAppendF(buffer_, "%s=%s", key.c_str(), value.c_str());
|
absl::StrAppendFormat(buffer_, "%s=%s", key.c_str(), value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tag::AddQuotedString(const std::string& key, const std::string& value) {
|
void Tag::AddQuotedString(const std::string& key, const std::string& value) {
|
||||||
NextField();
|
NextField();
|
||||||
base::StringAppendF(buffer_, "%s=\"%s\"", key.c_str(), value.c_str());
|
absl::StrAppendFormat(buffer_, "%s=\"%s\"", key.c_str(), value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tag::AddNumber(const std::string& key, uint64_t value) {
|
void Tag::AddNumber(const std::string& key, uint64_t value) {
|
||||||
NextField();
|
NextField();
|
||||||
base::StringAppendF(buffer_, "%s=%" PRIu64, key.c_str(), value);
|
absl::StrAppendFormat(buffer_, "%s=%" PRIu64, key.c_str(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tag::AddFloat(const std::string& key, float value) {
|
void Tag::AddFloat(const std::string& key, float value) {
|
||||||
NextField();
|
NextField();
|
||||||
base::StringAppendF(buffer_, "%s=%.3f", key.c_str(), value);
|
absl::StrAppendFormat(buffer_, "%s=%.3f", key.c_str(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tag::AddNumberPair(const std::string& key,
|
void Tag::AddNumberPair(const std::string& key,
|
||||||
|
@ -42,8 +41,8 @@ void Tag::AddNumberPair(const std::string& key,
|
||||||
char separator,
|
char separator,
|
||||||
uint64_t number2) {
|
uint64_t number2) {
|
||||||
NextField();
|
NextField();
|
||||||
base::StringAppendF(buffer_, "%s=%" PRIu64 "%c%" PRIu64, key.c_str(), number1,
|
absl::StrAppendFormat(buffer_, "%s=%" PRIu64 "%c%" PRIu64, key.c_str(),
|
||||||
separator, number2);
|
number1, separator, number2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tag::AddQuotedNumberPair(const std::string& key,
|
void Tag::AddQuotedNumberPair(const std::string& key,
|
||||||
|
@ -51,8 +50,8 @@ void Tag::AddQuotedNumberPair(const std::string& key,
|
||||||
char separator,
|
char separator,
|
||||||
uint64_t number2) {
|
uint64_t number2) {
|
||||||
NextField();
|
NextField();
|
||||||
base::StringAppendF(buffer_, "%s=\"%" PRIu64 "%c%" PRIu64 "\"", key.c_str(),
|
absl::StrAppendFormat(buffer_, "%s=\"%" PRIu64 "%c%" PRIu64 "\"", key.c_str(),
|
||||||
number1, separator, number2);
|
number1, separator, number2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tag::NextField() {
|
void Tag::NextField() {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#ifndef PACKAGER_HLS_BASE_TAG_H_
|
#ifndef PACKAGER_HLS_BASE_TAG_H_
|
||||||
#define PACKAGER_HLS_BASE_TAG_H_
|
#define PACKAGER_HLS_BASE_TAG_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
# Copyright 2016 Google LLC. All rights reserved.
|
|
||||||
#
|
|
||||||
# Use of this source code is governed by a BSD-style
|
|
||||||
# license that can be found in the LICENSE file or at
|
|
||||||
# https://developers.google.com/open-source/licenses/bsd
|
|
||||||
|
|
||||||
{
|
|
||||||
'variables': {
|
|
||||||
'shaka_code': 1,
|
|
||||||
},
|
|
||||||
'targets': [
|
|
||||||
{
|
|
||||||
'target_name': 'hls_builder',
|
|
||||||
'type': '<(component)',
|
|
||||||
'sources': [
|
|
||||||
'base/hls_notifier.h',
|
|
||||||
'base/master_playlist.cc',
|
|
||||||
'base/master_playlist.h',
|
|
||||||
'base/media_playlist.cc',
|
|
||||||
'base/media_playlist.h',
|
|
||||||
'base/simple_hls_notifier.cc',
|
|
||||||
'base/simple_hls_notifier.h',
|
|
||||||
'base/tag.cc',
|
|
||||||
'base/tag.h',
|
|
||||||
'public/hls_params.h',
|
|
||||||
],
|
|
||||||
'dependencies': [
|
|
||||||
'../base/base.gyp:base',
|
|
||||||
'../file/file.gyp:file',
|
|
||||||
'../media/base/media_base.gyp:media_base',
|
|
||||||
'../media/base/media_base.gyp:widevine_pssh_data_proto',
|
|
||||||
'../mpd/mpd.gyp:manifest_base',
|
|
||||||
'../mpd/mpd.gyp:media_info_proto',
|
|
||||||
'../third_party/gflags/gflags.gyp:gflags',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'target_name': 'hls_unittest',
|
|
||||||
'type': '<(gtest_target_type)',
|
|
||||||
'sources': [
|
|
||||||
'base/master_playlist_unittest.cc',
|
|
||||||
'base/media_playlist_unittest.cc',
|
|
||||||
'base/mock_media_playlist.cc',
|
|
||||||
'base/mock_media_playlist.h',
|
|
||||||
'base/simple_hls_notifier_unittest.cc',
|
|
||||||
],
|
|
||||||
'dependencies': [
|
|
||||||
'../base/base.gyp:base',
|
|
||||||
'../media/test/media_test.gyp:run_tests_with_atexit_manager',
|
|
||||||
'../mpd/mpd.gyp:media_info_proto',
|
|
||||||
'../testing/gmock.gyp:gmock',
|
|
||||||
'../testing/gtest.gyp:gtest',
|
|
||||||
'../third_party/gflags/gflags.gyp:gflags',
|
|
||||||
'hls_builder',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
|
@ -31,8 +31,7 @@ target_link_libraries(mock_muxer_listener
|
||||||
add_executable(media_event_unittest
|
add_executable(media_event_unittest
|
||||||
hls_notify_muxer_listener_unittest.cc
|
hls_notify_muxer_listener_unittest.cc
|
||||||
muxer_listener_internal_unittest.cc
|
muxer_listener_internal_unittest.cc
|
||||||
# TODO(cmake): Re-enable when mpd/base is ported (needs MockMpdNotifier)
|
mpd_notify_muxer_listener_unittest.cc
|
||||||
# mpd_notify_muxer_listener_unittest.cc
|
|
||||||
multi_codec_muxer_listener_unittest.cc
|
multi_codec_muxer_listener_unittest.cc
|
||||||
muxer_listener_test_helper.cc
|
muxer_listener_test_helper.cc
|
||||||
vod_media_info_dump_muxer_listener_unittest.cc
|
vod_media_info_dump_muxer_listener_unittest.cc
|
||||||
|
@ -40,6 +39,7 @@ add_executable(media_event_unittest
|
||||||
target_link_libraries(media_event_unittest
|
target_link_libraries(media_event_unittest
|
||||||
file
|
file
|
||||||
file_test_util
|
file_test_util
|
||||||
|
mpd_mocks
|
||||||
mpd_media_info_proto
|
mpd_media_info_proto
|
||||||
gmock
|
gmock
|
||||||
gtest
|
gtest
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include "packager/file/file_util.h"
|
||||||
#include "packager/media/base/rcheck.h"
|
#include "packager/media/base/rcheck.h"
|
||||||
#include "packager/mpd/base/adaptation_set.h"
|
#include "packager/mpd/base/adaptation_set.h"
|
||||||
#include "packager/mpd/base/mpd_utils.h"
|
#include "packager/mpd/base/mpd_utils.h"
|
||||||
|
@ -88,17 +89,6 @@ bool SetIfPositive(const char* attr_name, double value, XmlNode* mpd) {
|
||||||
mpd->SetStringAttribute(attr_name, SecondsToXmlDuration(value));
|
mpd->SetStringAttribute(attr_name, SecondsToXmlDuration(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MakePathRelative(const std::filesystem::path& media_path,
|
|
||||||
const std::filesystem::path& parent_path) {
|
|
||||||
auto relative_path = std::filesystem::relative(media_path, parent_path);
|
|
||||||
if (relative_path.empty() || *relative_path.begin() == "..") {
|
|
||||||
// Not related.
|
|
||||||
relative_path = media_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return relative_path.lexically_normal().generic_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spooky static initialization/cleanup of libxml.
|
// Spooky static initialization/cleanup of libxml.
|
||||||
class LibXmlInitializer {
|
class LibXmlInitializer {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -19,12 +19,12 @@
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
std::filesystem::path GetTestDataFilePath(const std::string& name) {
|
std::filesystem::path GetTestDataFilePath(const std::string& name) {
|
||||||
auto data_dir = std::filesystem::path(TEST_DATA_DIR);
|
auto data_dir = std::filesystem::u8path(TEST_DATA_DIR);
|
||||||
return data_dir / name;
|
return data_dir / name;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path GetSchemaPath() {
|
std::filesystem::path GetSchemaPath() {
|
||||||
auto schema_dir = std::filesystem::path(TEST_SCHEMA_DIR);
|
auto schema_dir = std::filesystem::u8path(TEST_SCHEMA_DIR);
|
||||||
return schema_dir / "DASH-MPD.xsd";
|
return schema_dir / "DASH-MPD.xsd";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
FROM archlinux:latest
|
FROM archlinux:latest
|
||||||
|
|
||||||
# Install utilities, libraries, and dev tools.
|
# Install utilities, libraries, and dev tools.
|
||||||
RUN pacman -Sy --needed --noconfirm \
|
RUN pacman -Suy --needed --noconfirm \
|
||||||
core/which \
|
core/which \
|
||||||
c-ares \
|
c-ares \
|
||||||
cmake gcc git ninja python3
|
cmake gcc git ninja python3
|
||||||
|
|
|
@ -2,7 +2,7 @@ FROM tgagor/centos:stream9
|
||||||
|
|
||||||
# For CentOS, Ninja is only available from the "CRB" ("Code Ready Builder")
|
# For CentOS, Ninja is only available from the "CRB" ("Code Ready Builder")
|
||||||
# repo. Enable that first.
|
# repo. Enable that first.
|
||||||
RUN dnf update
|
RUN dnf update -y
|
||||||
RUN dnf install -y yum-utils
|
RUN dnf install -y yum-utils
|
||||||
RUN dnf config-manager --set-enabled crb
|
RUN dnf config-manager --set-enabled crb
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue