feat: convert mpd module to cmake (#1234)

Related to issue #1047
This commit is contained in:
Cosmin Stejerean 2023-08-04 20:45:21 -07:00 committed by GitHub
parent 97cf7c0a5b
commit 96acd1ecfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 743 additions and 658 deletions

View File

@ -43,4 +43,23 @@ std::vector<uint8_t> ReadTestDataFile(const std::string& name) {
} }
} // namespace media } // namespace media
// Get the content of |file_path|. Returns empty string on error.
std::string GetPathContent(std::filesystem::path& file_path) {
std::string content;
FILE* f = fopen(file_path.string().c_str(), "rb");
if (!f) {
LOG(FATAL) << "Failed to read test data from " << file_path;
return std::string{};
}
content.resize(std::filesystem::file_size(file_path));
size_t size = fread(content.data(), 1, content.size(), f);
content.resize(size);
fclose(f);
return content;
}
} // namespace shaka } // namespace shaka

View File

@ -24,6 +24,10 @@ std::filesystem::path GetAppTestDataFilePath(const std::string& name);
std::vector<uint8_t> ReadTestDataFile(const std::string& name); std::vector<uint8_t> ReadTestDataFile(const std::string& name);
} // namespace media } // namespace media
// Get the content of |file_path|. Returns empty string on error.
std::string GetPathContent(std::filesystem::path& file_path);
} // namespace shaka } // namespace shaka
#endif // PACKAGER_MEDIA_TEST_TEST_DATA_UTIL_H_ #endif // PACKAGER_MEDIA_TEST_TEST_DATA_UTIL_H_

View File

@ -5,3 +5,111 @@
# https://developers.google.com/open-source/licenses/bsd # https://developers.google.com/open-source/licenses/bsd
add_subdirectory(base) add_subdirectory(base)
add_library(manifest_base STATIC
base/bandwidth_estimator.cc
base/bandwidth_estimator.h
)
target_link_libraries(manifest_base
glog
)
add_library(mpd_builder STATIC
base/adaptation_set.cc
base/adaptation_set.h
base/content_protection_element.cc
base/content_protection_element.h
base/mpd_builder.cc
base/mpd_builder.h
base/mpd_notifier_util.cc
base/mpd_notifier_util.h
base/mpd_notifier.h
base/mpd_options.h
base/mpd_utils.cc
base/mpd_utils.h
base/period.cc
base/period.h
base/representation.cc
base/representation.h
base/segment_info.h
base/simple_mpd_notifier.cc
base/simple_mpd_notifier.h
base/xml/scoped_xml_ptr.h
base/xml/xml_node.cc
base/xml/xml_node.h
public/mpd_params.h
)
target_link_libraries(mpd_builder
absl::flags
absl::strings
absl::str_format
glog
LibXml2
file
media_base
manifest_base
mpd_media_info_proto
utils_clock
)
add_library(mpd_mocks STATIC
base/mock_mpd_builder.cc
base/mock_mpd_builder.h
base/mock_mpd_notifier.cc
base/mock_mpd_notifier.h
)
target_link_libraries(mpd_mocks gmock absl::synchronization LibXml2 mpd_media_info_proto)
add_executable(mpd_unittest
base/adaptation_set_unittest.cc
base/bandwidth_estimator_unittest.cc
base/mpd_builder_unittest.cc
base/mpd_utils_unittest.cc
base/period_unittest.cc
base/representation_unittest.cc
base/simple_mpd_notifier_unittest.cc
base/xml/xml_node_unittest.cc
test/mpd_builder_test_helper.cc
test/mpd_builder_test_helper.h
test/xml_compare.cc
test/xml_compare.h
util/mpd_writer_unittest.cc
)
target_compile_definitions(mpd_unittest
PRIVATE
# We used to build off of __FILE__, but that is not always an absolute
# path, depending on the version of CMake. This is consistent.
TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/test/data"
TEST_SCHEMA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/test/schema")
target_link_libraries(mpd_unittest
file
file_test_util
test_data_util
absl::flags
mpd_builder
mpd_mocks
mpd_util
gmock
gtest
utils_test_clock
gtest_main)
add_test(NAME mpd_unittest COMMAND mpd_unittest)
add_library(mpd_util STATIC
util/mpd_writer.cc
util/mpd_writer.h)
target_link_libraries(mpd_util
file
absl::flags
mpd_builder
mpd_mocks
)

View File

@ -6,10 +6,11 @@
#include "packager/mpd/base/adaptation_set.h" #include "packager/mpd/base/adaptation_set.h"
#include <absl/strings/numbers.h>
#include <glog/logging.h>
#include <cmath> #include <cmath>
#include "packager/base/logging.h" #include "absl/strings/str_format.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/mpd_options.h" #include "packager/mpd/base/mpd_options.h"
#include "packager/mpd/base/mpd_utils.h" #include "packager/mpd/base/mpd_utils.h"
@ -30,7 +31,7 @@ AdaptationSet::Role MediaInfoTextTypeToRole(
case MediaInfo::TextInfo::SUBTITLE: case MediaInfo::TextInfo::SUBTITLE:
return AdaptationSet::kRoleSubtitle; return AdaptationSet::kRoleSubtitle;
default: default:
NOTREACHED() << "Unknown MediaInfo TextType: " << type NOTIMPLEMENTED() << "Unknown MediaInfo TextType: " << type
<< " assuming subtitle."; << " assuming subtitle.";
return AdaptationSet::kRoleSubtitle; return AdaptationSet::kRoleSubtitle;
} }
@ -98,7 +99,7 @@ std::string GetPictureAspectRatio(uint32_t width,
<< scaled_height << ") reduced to " << par_num << ":" << par_den << scaled_height << ") reduced to " << par_num << ":" << par_den
<< " with error " << min_error << "."; << " with error " << min_error << ".";
return base::IntToString(par_num) + ":" + base::IntToString(par_den); return absl::StrFormat("%d:%d", par_num, par_den);
} }
// Adds an entry to picture_aspect_ratio if the size of picture_aspect_ratio is // Adds an entry to picture_aspect_ratio if the size of picture_aspect_ratio is
@ -239,7 +240,7 @@ void AdaptationSet::AddRole(Role role) {
// can be passed to Representation to avoid setting redundant attributes. For // can be passed to Representation to avoid setting redundant attributes. For
// example, if AdaptationSet@width is set, then Representation@width is // example, if AdaptationSet@width is set, then Representation@width is
// redundant and should not be set. // redundant and should not be set.
base::Optional<xml::XmlNode> AdaptationSet::GetXml() { std::optional<xml::XmlNode> AdaptationSet::GetXml() {
xml::AdaptationSetXmlNode adaptation_set; xml::AdaptationSetXmlNode adaptation_set;
bool suppress_representation_width = false; bool suppress_representation_width = false;
@ -247,33 +248,33 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
bool suppress_representation_frame_rate = false; bool suppress_representation_frame_rate = false;
if (id_ && !adaptation_set.SetId(id_.value())) if (id_ && !adaptation_set.SetId(id_.value()))
return base::nullopt; return std::nullopt;
if (!adaptation_set.SetStringAttribute("contentType", content_type_)) if (!adaptation_set.SetStringAttribute("contentType", content_type_))
return base::nullopt; return std::nullopt;
if (!language_.empty() && language_ != "und" && if (!language_.empty() && language_ != "und" &&
!adaptation_set.SetStringAttribute("lang", language_)) { !adaptation_set.SetStringAttribute("lang", language_)) {
return base::nullopt; return std::nullopt;
} }
// Note that std::{set,map} are ordered, so the last element is the max value. // Note that std::{set,map} are ordered, so the last element is the max value.
if (video_widths_.size() == 1) { if (video_widths_.size() == 1) {
suppress_representation_width = true; suppress_representation_width = true;
if (!adaptation_set.SetIntegerAttribute("width", *video_widths_.begin())) if (!adaptation_set.SetIntegerAttribute("width", *video_widths_.begin()))
return base::nullopt; return std::nullopt;
} else if (video_widths_.size() > 1) { } else if (video_widths_.size() > 1) {
if (!adaptation_set.SetIntegerAttribute("maxWidth", if (!adaptation_set.SetIntegerAttribute("maxWidth",
*video_widths_.rbegin())) { *video_widths_.rbegin())) {
return base::nullopt; return std::nullopt;
} }
} }
if (video_heights_.size() == 1) { if (video_heights_.size() == 1) {
suppress_representation_height = true; suppress_representation_height = true;
if (!adaptation_set.SetIntegerAttribute("height", *video_heights_.begin())) if (!adaptation_set.SetIntegerAttribute("height", *video_heights_.begin()))
return base::nullopt; return std::nullopt;
} else if (video_heights_.size() > 1) { } else if (video_heights_.size() > 1) {
if (!adaptation_set.SetIntegerAttribute("maxHeight", if (!adaptation_set.SetIntegerAttribute("maxHeight",
*video_heights_.rbegin())) { *video_heights_.rbegin())) {
return base::nullopt; return std::nullopt;
} }
} }
@ -281,12 +282,12 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
suppress_representation_frame_rate = true; suppress_representation_frame_rate = true;
if (!adaptation_set.SetStringAttribute( if (!adaptation_set.SetStringAttribute(
"frameRate", video_frame_rates_.begin()->second)) { "frameRate", video_frame_rates_.begin()->second)) {
return base::nullopt; return std::nullopt;
} }
} else if (video_frame_rates_.size() > 1) { } else if (video_frame_rates_.size() > 1) {
if (!adaptation_set.SetStringAttribute( if (!adaptation_set.SetStringAttribute(
"maxFrameRate", video_frame_rates_.rbegin()->second)) { "maxFrameRate", video_frame_rates_.rbegin()->second)) {
return base::nullopt; return std::nullopt;
} }
} }
@ -302,60 +303,60 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
? "subsegmentAlignment" ? "subsegmentAlignment"
: "segmentAlignment", : "segmentAlignment",
"true")) { "true")) {
return base::nullopt; return std::nullopt;
} }
} }
if (picture_aspect_ratio_.size() == 1 && if (picture_aspect_ratio_.size() == 1 &&
!adaptation_set.SetStringAttribute("par", !adaptation_set.SetStringAttribute("par",
*picture_aspect_ratio_.begin())) { *picture_aspect_ratio_.begin())) {
return base::nullopt; return std::nullopt;
} }
if (!adaptation_set.AddContentProtectionElements( if (!adaptation_set.AddContentProtectionElements(
content_protection_elements_)) { content_protection_elements_)) {
return base::nullopt; return std::nullopt;
} }
std::string trick_play_reference_ids; std::string trick_play_reference_ids;
for (const AdaptationSet* adaptation_set : trick_play_references_) { for (const AdaptationSet* tp_adaptation_set : trick_play_references_) {
// Should be a whitespace-separated list, see DASH-IOP 3.2.9. // Should be a whitespace-separated list, see DASH-IOP 3.2.9.
if (!trick_play_reference_ids.empty()) if (!trick_play_reference_ids.empty())
trick_play_reference_ids += ' '; trick_play_reference_ids += ' ';
CHECK(adaptation_set->has_id()); CHECK(tp_adaptation_set->has_id());
trick_play_reference_ids += std::to_string(adaptation_set->id()); trick_play_reference_ids += std::to_string(tp_adaptation_set->id());
} }
if (!trick_play_reference_ids.empty() && if (!trick_play_reference_ids.empty() &&
!adaptation_set.AddEssentialProperty( !adaptation_set.AddEssentialProperty(
"http://dashif.org/guidelines/trickmode", trick_play_reference_ids)) { "http://dashif.org/guidelines/trickmode", trick_play_reference_ids)) {
return base::nullopt; return std::nullopt;
} }
std::string switching_ids; std::string switching_ids;
for (const AdaptationSet* adaptation_set : switchable_adaptation_sets_) { for (const AdaptationSet* s_adaptation_set : switchable_adaptation_sets_) {
// Should be a comma-separated list, see DASH-IOP 3.8. // Should be a comma-separated list, see DASH-IOP 3.8.
if (!switching_ids.empty()) if (!switching_ids.empty())
switching_ids += ','; switching_ids += ',';
CHECK(adaptation_set->has_id()); CHECK(s_adaptation_set->has_id());
switching_ids += std::to_string(adaptation_set->id()); switching_ids += std::to_string(s_adaptation_set->id());
} }
if (!switching_ids.empty() && if (!switching_ids.empty() &&
!adaptation_set.AddSupplementalProperty( !adaptation_set.AddSupplementalProperty(
"urn:mpeg:dash:adaptation-set-switching:2016", switching_ids)) { "urn:mpeg:dash:adaptation-set-switching:2016", switching_ids)) {
return base::nullopt; return std::nullopt;
} }
for (const AdaptationSet::Accessibility& accessibility : accessibilities_) { for (const AdaptationSet::Accessibility& accessibility : accessibilities_) {
if (!adaptation_set.AddAccessibilityElement(accessibility.scheme, if (!adaptation_set.AddAccessibilityElement(accessibility.scheme,
accessibility.value)) { accessibility.value)) {
return base::nullopt; return std::nullopt;
} }
} }
for (AdaptationSet::Role role : roles_) { for (AdaptationSet::Role role : roles_) {
if (!adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011", if (!adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011",
RoleToText(role))) { RoleToText(role))) {
return base::nullopt; return std::nullopt;
} }
} }
@ -369,10 +370,10 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
representation->SuppressOnce(Representation::kSuppressFrameRate); representation->SuppressOnce(Representation::kSuppressFrameRate);
auto child = representation->GetXml(); auto child = representation->GetXml();
if (!child || !adaptation_set.AddChild(std::move(*child))) if (!child || !adaptation_set.AddChild(std::move(*child)))
return base::nullopt; return std::nullopt;
} }
return std::move(adaptation_set); return adaptation_set;
} }
void AdaptationSet::ForceSetSegmentAlignment(bool segment_alignment) { void AdaptationSet::ForceSetSegmentAlignment(bool segment_alignment) {
@ -593,7 +594,7 @@ void AdaptationSet::RecordFrameRate(int32_t frame_duration, int32_t timescale) {
return; return;
} }
video_frame_rates_[static_cast<double>(timescale) / frame_duration] = video_frame_rates_[static_cast<double>(timescale) / frame_duration] =
base::IntToString(timescale) + "/" + base::IntToString(frame_duration); absl::StrFormat("%d/%d", timescale, frame_duration);
} }
} // namespace shaka } // namespace shaka

View File

@ -14,10 +14,10 @@
#include <list> #include <list>
#include <map> #include <map>
#include <memory> #include <memory>
#include <optional>
#include <set> #include <set>
#include <vector> #include <vector>
#include "packager/base/optional.h"
#include "packager/mpd/base/xml/xml_node.h" #include "packager/mpd/base/xml/xml_node.h"
namespace shaka { namespace shaka {
@ -109,7 +109,7 @@ class AdaptationSet {
/// and ContentProtection elements. /// and ContentProtection elements.
/// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a /// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a
/// NULL scoped_xml_ptr. /// NULL scoped_xml_ptr.
base::Optional<xml::XmlNode> GetXml(); std::optional<xml::XmlNode> GetXml();
/// Forces the (sub)segmentAlignment field to be set to @a segment_alignment. /// Forces the (sub)segmentAlignment field to be set to @a segment_alignment.
/// Use this if you are certain that the (sub)segments are alinged/unaligned /// Use this if you are certain that the (sub)segments are alinged/unaligned
@ -248,7 +248,7 @@ class AdaptationSet {
uint32_t* const representation_counter_; uint32_t* const representation_counter_;
base::Optional<uint32_t> id_; std::optional<uint32_t> id_;
const std::string language_; const std::string language_;
const MpdOptions& mpd_options_; const MpdOptions& mpd_options_;

View File

@ -10,7 +10,7 @@
#include <cmath> #include <cmath>
#include <numeric> #include <numeric>
#include "packager/base/logging.h" #include "glog/logging.h"
namespace shaka { namespace shaka {
@ -106,7 +106,7 @@ uint64_t BandwidthEstimator::GetBitrate(const Block& block,
VLOG(1) << "Exclude short segment (duration " << block.duration VLOG(1) << "Exclude short segment (duration " << block.duration
<< ", target_duration " << target_block_duration << ", target_duration " << target_block_duration
<< ") in peak bandwidth computation."; << ") in peak bandwidth computation.";
return 0.0; return 0;
} }
return static_cast<uint64_t>(ceil(block.size_in_bits / block.duration)); return static_cast<uint64_t>(ceil(block.size_in_bits / block.duration));
} }

View File

@ -9,8 +9,8 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include "packager/base/compiler_specific.h" #include "absl/synchronization/mutex.h"
#include "packager/base/synchronization/lock.h" #include "packager/macros.h"
#include "packager/mpd/base/adaptation_set.h" #include "packager/mpd/base/adaptation_set.h"
#include "packager/mpd/base/content_protection_element.h" #include "packager/mpd/base/content_protection_element.h"
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"

View File

@ -6,16 +6,15 @@
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"
#include <absl/strings/numbers.h>
#include <absl/strings/str_format.h>
#include <absl/synchronization/mutex.h>
#include <glog/logging.h>
#include <algorithm> #include <algorithm>
#include <chrono>
#include <filesystem>
#include <optional>
#include "packager/base/files/file_path.h"
#include "packager/base/logging.h"
#include "packager/base/optional.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/stringprintf.h"
#include "packager/base/synchronization/lock.h"
#include "packager/base/time/default_clock.h"
#include "packager/base/time/time.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"
@ -25,7 +24,6 @@
namespace shaka { namespace shaka {
using base::FilePath;
using xml::XmlNode; using xml::XmlNode;
namespace { namespace {
@ -63,7 +61,7 @@ bool AddMpdNameSpaceInfo(XmlNode* mpd) {
CHECK(iter != uris.end()) << " unexpected namespace " << namespace_name; CHECK(iter != uris.end()) << " unexpected namespace " << namespace_name;
RCHECK(mpd->SetStringAttribute( RCHECK(mpd->SetStringAttribute(
base::StringPrintf("xmlns:%s", namespace_name.c_str()).c_str(), absl::StrFormat("xmlns:%s", namespace_name.c_str()).c_str(),
iter->second)); iter->second));
} }
return true; return true;
@ -75,18 +73,14 @@ bool Positive(double d) {
// Return current time in XML DateTime format. The value is in UTC, so the // Return current time in XML DateTime format. The value is in UTC, so the
// string ends with a 'Z'. // string ends with a 'Z'.
std::string XmlDateTimeNowWithOffset( std::string XmlDateTimeNowWithOffset(int32_t offset_seconds, Clock* clock) {
int32_t offset_seconds, auto time_t = std::chrono::system_clock::to_time_t(
base::Clock* clock) { clock->now() + std::chrono::seconds(offset_seconds));
base::Time time = clock->Now(); std::tm* tm = std::gmtime(&time_t);
time += base::TimeDelta::FromSeconds(offset_seconds);
base::Time::Exploded time_exploded;
time.UTCExplode(&time_exploded);
return base::StringPrintf("%4d-%02d-%02dT%02d:%02d:%02dZ", time_exploded.year, std::stringstream ss;
time_exploded.month, time_exploded.day_of_month, ss << std::put_time(tm, "%Y-%m-%dT%H:%M:%SZ");
time_exploded.hour, time_exploded.minute, return ss.str();
time_exploded.second);
} }
bool SetIfPositive(const char* attr_name, double value, XmlNode* mpd) { bool SetIfPositive(const char* attr_name, double value, XmlNode* mpd) {
@ -94,22 +88,22 @@ 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::string& media_path, std::string MakePathRelative(const std::filesystem::path& media_path,
const FilePath& parent_path) { const std::filesystem::path& parent_path) {
FilePath relative_path; auto relative_path = std::filesystem::relative(media_path, parent_path);
const FilePath child_path = FilePath::FromUTF8Unsafe(media_path); if (relative_path.empty() || *relative_path.begin() == "..") {
const bool is_child = // Not related.
parent_path.AppendRelativePath(child_path, &relative_path); relative_path = media_path;
if (!is_child) }
relative_path = child_path;
return relative_path.NormalizePathSeparatorsTo('/').AsUTF8Unsafe(); 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:
LibXmlInitializer() : initialized_(false) { LibXmlInitializer() : initialized_(false) {
base::AutoLock lock(lock_); absl::MutexLock lock(&lock_);
if (!initialized_) { if (!initialized_) {
xmlInitParser(); xmlInitParser();
initialized_ = true; initialized_ = true;
@ -117,7 +111,7 @@ class LibXmlInitializer {
} }
~LibXmlInitializer() { ~LibXmlInitializer() {
base::AutoLock lock(lock_); absl::MutexLock lock(&lock_);
if (initialized_) { if (initialized_) {
xmlCleanupParser(); xmlCleanupParser();
initialized_ = false; initialized_ = false;
@ -125,7 +119,7 @@ class LibXmlInitializer {
} }
private: private:
base::Lock lock_; absl::Mutex lock_;
bool initialized_; bool initialized_;
DISALLOW_COPY_AND_ASSIGN(LibXmlInitializer); DISALLOW_COPY_AND_ASSIGN(LibXmlInitializer);
@ -134,7 +128,7 @@ class LibXmlInitializer {
} // namespace } // namespace
MpdBuilder::MpdBuilder(const MpdOptions& mpd_options) MpdBuilder::MpdBuilder(const MpdOptions& mpd_options)
: mpd_options_(mpd_options), clock_(new base::DefaultClock()) {} : mpd_options_(mpd_options), clock_(new Clock{}) {}
MpdBuilder::~MpdBuilder() {} MpdBuilder::~MpdBuilder() {}
@ -166,15 +160,14 @@ bool MpdBuilder::ToString(std::string* output) {
std::string version = GetPackagerVersion(); std::string version = GetPackagerVersion();
if (!version.empty()) { if (!version.empty()) {
version = version = absl::StrFormat("Generated with %s version %s",
base::StringPrintf("Generated with %s version %s",
GetPackagerProjectUrl().c_str(), version.c_str()); GetPackagerProjectUrl().c_str(), version.c_str());
} }
*output = mpd->ToString(version); *output = mpd->ToString(version);
return true; return true;
} }
base::Optional<xml::XmlNode> MpdBuilder::GenerateMpd() { std::optional<xml::XmlNode> MpdBuilder::GenerateMpd() {
XmlNode mpd("MPD"); XmlNode mpd("MPD");
// Add baseurls to MPD. // Add baseurls to MPD.
@ -183,7 +176,7 @@ base::Optional<xml::XmlNode> MpdBuilder::GenerateMpd() {
xml_base_url.SetContent(base_url); xml_base_url.SetContent(base_url);
if (!mpd.AddChild(std::move(xml_base_url))) if (!mpd.AddChild(std::move(xml_base_url)))
return base::nullopt; return std::nullopt;
} }
bool output_period_duration = false; bool output_period_duration = false;
@ -198,11 +191,11 @@ base::Optional<xml::XmlNode> MpdBuilder::GenerateMpd() {
for (const auto& period : periods_) { for (const auto& period : periods_) {
auto period_node = period->GetXml(output_period_duration); auto period_node = period->GetXml(output_period_duration);
if (!period_node || !mpd.AddChild(std::move(*period_node))) if (!period_node || !mpd.AddChild(std::move(*period_node)))
return base::nullopt; return std::nullopt;
} }
if (!AddMpdNameSpaceInfo(&mpd)) if (!AddMpdNameSpaceInfo(&mpd))
return base::nullopt; return std::nullopt;
static const char kOnDemandProfile[] = static const char kOnDemandProfile[] =
"urn:mpeg:dash:profile:isoff-on-demand:2011"; "urn:mpeg:dash:profile:isoff-on-demand:2011";
@ -211,34 +204,34 @@ base::Optional<xml::XmlNode> MpdBuilder::GenerateMpd() {
switch (mpd_options_.dash_profile) { switch (mpd_options_.dash_profile) {
case DashProfile::kOnDemand: case DashProfile::kOnDemand:
if (!mpd.SetStringAttribute("profiles", kOnDemandProfile)) if (!mpd.SetStringAttribute("profiles", kOnDemandProfile))
return base::nullopt; return std::nullopt;
break; break;
case DashProfile::kLive: case DashProfile::kLive:
if (!mpd.SetStringAttribute("profiles", kLiveProfile)) if (!mpd.SetStringAttribute("profiles", kLiveProfile))
return base::nullopt; return std::nullopt;
break; break;
default: default:
NOTREACHED() << "Unknown DASH profile: " NOTIMPLEMENTED() << "Unknown DASH profile: "
<< static_cast<int>(mpd_options_.dash_profile); << static_cast<int>(mpd_options_.dash_profile);
break; break;
} }
if (!AddCommonMpdInfo(&mpd)) if (!AddCommonMpdInfo(&mpd))
return base::nullopt; return std::nullopt;
switch (mpd_options_.mpd_type) { switch (mpd_options_.mpd_type) {
case MpdType::kStatic: case MpdType::kStatic:
if (!AddStaticMpdInfo(&mpd)) if (!AddStaticMpdInfo(&mpd))
return base::nullopt; return std::nullopt;
break; break;
case MpdType::kDynamic: case MpdType::kDynamic:
if (!AddDynamicMpdInfo(&mpd)) if (!AddDynamicMpdInfo(&mpd))
return base::nullopt; return std::nullopt;
// Must be after Period element. // Must be after Period element.
if (!AddUtcTiming(&mpd)) if (!AddUtcTiming(&mpd))
return base::nullopt; return std::nullopt;
break; break;
default: default:
NOTREACHED() << "Unknown MPD type: " NOTIMPLEMENTED() << "Unknown MPD type: "
<< static_cast<int>(mpd_options_.mpd_type); << static_cast<int>(mpd_options_.mpd_type);
break; break;
} }
@ -259,7 +252,8 @@ bool MpdBuilder::AddCommonMpdInfo(XmlNode* mpd_node) {
bool MpdBuilder::AddStaticMpdInfo(XmlNode* mpd_node) { bool MpdBuilder::AddStaticMpdInfo(XmlNode* mpd_node) {
DCHECK(mpd_node); DCHECK(mpd_node);
DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type); DCHECK_EQ(static_cast<int>(MpdType::kStatic),
static_cast<int>(mpd_options_.mpd_type));
static const char kStaticMpdType[] = "static"; static const char kStaticMpdType[] = "static";
return mpd_node->SetStringAttribute("type", kStaticMpdType) && return mpd_node->SetStringAttribute("type", kStaticMpdType) &&
@ -270,7 +264,8 @@ bool MpdBuilder::AddStaticMpdInfo(XmlNode* mpd_node) {
bool MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) { bool MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
DCHECK(mpd_node); DCHECK(mpd_node);
DCHECK_EQ(MpdType::kDynamic, mpd_options_.mpd_type); DCHECK_EQ(static_cast<int>(MpdType::kDynamic),
static_cast<int>(mpd_options_.mpd_type));
static const char kDynamicMpdType[] = "dynamic"; static const char kDynamicMpdType[] = "dynamic";
RCHECK(mpd_node->SetStringAttribute("type", kDynamicMpdType)); RCHECK(mpd_node->SetStringAttribute("type", kDynamicMpdType));
@ -316,7 +311,8 @@ bool MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
bool MpdBuilder::AddUtcTiming(XmlNode* mpd_node) { bool MpdBuilder::AddUtcTiming(XmlNode* mpd_node) {
DCHECK(mpd_node); DCHECK(mpd_node);
DCHECK_EQ(MpdType::kDynamic, mpd_options_.mpd_type); DCHECK_EQ(static_cast<int>(MpdType::kDynamic),
static_cast<int>(mpd_options_.mpd_type));
for (const MpdParams::UtcTiming& utc_timing : for (const MpdParams::UtcTiming& utc_timing :
mpd_options_.mpd_params.utc_timings) { mpd_options_.mpd_params.utc_timings) {
@ -330,7 +326,8 @@ bool MpdBuilder::AddUtcTiming(XmlNode* mpd_node) {
} }
float MpdBuilder::GetStaticMpdDuration() { float MpdBuilder::GetStaticMpdDuration() {
DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type); DCHECK_EQ(static_cast<int>(MpdType::kStatic),
static_cast<int>(mpd_options_.mpd_type));
float total_duration = 0.0f; float total_duration = 0.0f;
for (const auto& period : periods_) { for (const auto& period : periods_) {
@ -365,7 +362,8 @@ bool MpdBuilder::GetEarliestTimestamp(double* timestamp_seconds) {
} }
void MpdBuilder::UpdatePeriodDurationAndPresentationTimestamp() { void MpdBuilder::UpdatePeriodDurationAndPresentationTimestamp() {
DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type); DCHECK_EQ(static_cast<int>(MpdType::kStatic),
static_cast<int>(mpd_options_.mpd_type));
for (const auto& period : periods_) { for (const auto& period : periods_) {
std::list<Representation*> video_representations; std::list<Representation*> video_representations;
@ -383,8 +381,8 @@ void MpdBuilder::UpdatePeriodDurationAndPresentationTimestamp() {
} }
} }
base::Optional<double> earliest_start_time; std::optional<double> earliest_start_time;
base::Optional<double> latest_end_time; std::optional<double> latest_end_time;
// The timestamps are based on Video Representations if exist. // The timestamps are based on Video Representations if exist.
const auto& representations = video_representations.size() > 0 const auto& representations = video_representations.size() > 0
? video_representations ? video_representations
@ -418,14 +416,13 @@ void MpdBuilder::MakePathsRelativeToMpd(const std::string& mpd_path,
MediaInfo* media_info) { MediaInfo* media_info) {
DCHECK(media_info); DCHECK(media_info);
const std::string kFileProtocol("file://"); const std::string kFileProtocol("file://");
std::string mpd_file_path = (mpd_path.find(kFileProtocol) == 0) std::filesystem::path mpd_file_path =
(mpd_path.find(kFileProtocol) == 0)
? mpd_path.substr(kFileProtocol.size()) ? mpd_path.substr(kFileProtocol.size())
: mpd_path; : mpd_path;
if (!mpd_file_path.empty()) { if (!mpd_file_path.empty()) {
const FilePath mpd_dir(FilePath::FromUTF8Unsafe(mpd_file_path) const std::filesystem::path mpd_dir(mpd_file_path.parent_path());
.DirName()
.AsEndingWithSeparator());
if (!mpd_dir.empty()) { if (!mpd_dir.empty()) {
if (media_info->has_media_file_name()) { if (media_info->has_media_file_name()) {
media_info->set_media_file_url( media_info->set_media_file_url(

View File

@ -13,15 +13,16 @@
#include <libxml/tree.h> #include <libxml/tree.h>
#include <chrono>
#include <list> #include <list>
#include <memory> #include <memory>
#include <optional>
#include <string> #include <string>
#include "packager/base/compiler_specific.h" #include "packager/macros.h"
#include "packager/base/optional.h"
#include "packager/base/time/clock.h"
#include "packager/mpd/base/mpd_options.h" #include "packager/mpd/base/mpd_options.h"
#include "packager/mpd/base/xml/xml_node.h" #include "packager/mpd/base/xml/xml_node.h"
#include "packager/utils/clock.h"
// TODO(rkuroiwa): For classes with |id_|, consider removing the field and let // TODO(rkuroiwa): For classes with |id_|, consider removing the field and let
// the MPD (XML) generation functions take care of assigning an ID to each // the MPD (XML) generation functions take care of assigning an ID to each
@ -56,7 +57,7 @@ class MpdBuilder {
/// @param[out] output is an output string where the MPD gets written. /// @param[out] output is an output string where the MPD gets written.
/// @return true on success, false otherwise. /// @return true on success, false otherwise.
// TODO(kqyang): Handle file IO in this class as in HLS media_playlist? // TODO(kqyang): Handle file IO in this class as in HLS media_playlist?
virtual bool ToString(std::string* output) WARN_UNUSED_RESULT; [[nodiscard]] virtual bool ToString(std::string* output);
/// Adjusts the fields of MediaInfo so that paths are relative to the /// Adjusts the fields of MediaInfo so that paths are relative to the
/// specified MPD path. /// specified MPD path.
@ -68,7 +69,7 @@ class MpdBuilder {
// Inject a |clock| that returns the current time. // Inject a |clock| that returns the current time.
/// This is for testing. /// This is for testing.
void InjectClockForTesting(std::unique_ptr<base::Clock> clock) { void InjectClockForTesting(std::unique_ptr<Clock> clock) {
clock_ = std::move(clock); clock_ = std::move(clock);
} }
@ -85,21 +86,21 @@ class MpdBuilder {
// Returns the document pointer to the MPD. This must be freed by the caller // Returns the document pointer to the MPD. This must be freed by the caller
// using appropriate xmlDocPtr freeing function. // using appropriate xmlDocPtr freeing function.
// On failure, this returns NULL. // On failure, this returns NULL.
base::Optional<xml::XmlNode> GenerateMpd(); std::optional<xml::XmlNode> GenerateMpd();
// Set MPD attributes common to all profiles. Uses non-zero |mpd_options_| to // Set MPD attributes common to all profiles. Uses non-zero |mpd_options_| to
// set attributes for the MPD. // set attributes for the MPD.
bool AddCommonMpdInfo(xml::XmlNode* mpd_node) WARN_UNUSED_RESULT; [[nodiscard]] bool AddCommonMpdInfo(xml::XmlNode* mpd_node);
// Adds 'static' MPD attributes and elements to |mpd_node|. This assumes that // Adds 'static' MPD attributes and elements to |mpd_node|. This assumes that
// the first child element is a Period element. // the first child element is a Period element.
bool AddStaticMpdInfo(xml::XmlNode* mpd_node) WARN_UNUSED_RESULT; [[nodiscard]] bool AddStaticMpdInfo(xml::XmlNode* mpd_node);
// Same as AddStaticMpdInfo() but for 'dynamic' MPDs. // Same as AddStaticMpdInfo() but for 'dynamic' MPDs.
bool AddDynamicMpdInfo(xml::XmlNode* mpd_node) WARN_UNUSED_RESULT; [[nodiscard]] bool AddDynamicMpdInfo(xml::XmlNode* mpd_node);
// Add UTCTiming element if utc timing is provided. // Add UTCTiming element if utc timing is provided.
bool AddUtcTiming(xml::XmlNode* mpd_node) WARN_UNUSED_RESULT; [[nodiscard]] bool AddUtcTiming(xml::XmlNode* mpd_node);
float GetStaticMpdDuration(); float GetStaticMpdDuration();
@ -109,7 +110,7 @@ class MpdBuilder {
// Gets the earliest, normalized segment timestamp. Returns true if // Gets the earliest, normalized segment timestamp. Returns true if
// successful, false otherwise. // successful, false otherwise.
bool GetEarliestTimestamp(double* timestamp_seconds) WARN_UNUSED_RESULT; [[nodiscard]] bool GetEarliestTimestamp(double* timestamp_seconds);
// Update Period durations and presentation timestamps. // Update Period durations and presentation timestamps.
void UpdatePeriodDurationAndPresentationTimestamp(); void UpdatePeriodDurationAndPresentationTimestamp();
@ -125,7 +126,7 @@ class MpdBuilder {
// By default, this returns the current time. This can be injected for // By default, this returns the current time. This can be injected for
// testing. // testing.
std::unique_ptr<base::Clock> clock_; std::unique_ptr<Clock> clock_;
}; };
} // namespace shaka } // namespace shaka

View File

@ -7,31 +7,21 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <memory>
#include "packager/mpd/base/adaptation_set.h" #include "packager/mpd/base/adaptation_set.h"
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/period.h" #include "packager/mpd/base/period.h"
#include "packager/mpd/base/representation.h" #include "packager/mpd/base/representation.h"
#include "packager/mpd/test/mpd_builder_test_helper.h" #include "packager/mpd/test/mpd_builder_test_helper.h"
#include "packager/utils/clock.h"
#include "packager/utils/test_clock.h"
#include "packager/version/version.h" #include "packager/version/version.h"
using ::testing::HasSubstr; using ::testing::HasSubstr;
namespace shaka { namespace shaka {
namespace {
class TestClock : public base::Clock {
public:
explicit TestClock(const base::Time& t) : time_(t) {}
~TestClock() override {}
base::Time Now() override { return time_; }
private:
base::Time time_;
};
} // namespace
template <DashProfile profile> template <DashProfile profile>
class MpdBuilderTest : public ::testing::Test { class MpdBuilderTest : public ::testing::Test {
public: public:
@ -117,17 +107,8 @@ class LiveMpdBuilderTest : public MpdBuilderTest<DashProfile::kLive> {
// Injects a clock that always returns 2016 Jan 11 15:10:24 in UTC. // Injects a clock that always returns 2016 Jan 11 15:10:24 in UTC.
void InjectTestClock() { void InjectTestClock() {
base::Time::Exploded test_time = { 2016, // year. mpd_.InjectClockForTesting(
1, // month std::make_unique<TestClock>("2016-01-11T15:10:24"));
1, // day_of_week = Monday.
11, // day_of_month
15, // hour.
10, // minute.
24, // second.
0 }; // millisecond.
ASSERT_TRUE(test_time.HasValidValues());
mpd_.InjectClockForTesting(std::unique_ptr<base::Clock>(
new TestClock(base::Time::FromUTCExploded(test_time))));
} }
}; };

View File

@ -6,8 +6,7 @@
#include "packager/mpd/base/mpd_notifier_util.h" #include "packager/mpd/base/mpd_notifier_util.h"
#include "packager/base/strings/string_number_conversions.h" #include "glog/logging.h"
#include "packager/base/strings/string_util.h"
#include "packager/file/file.h" #include "packager/file/file.h"
#include "packager/mpd/base/mpd_utils.h" #include "packager/mpd/base/mpd_utils.h"
@ -49,7 +48,7 @@ ContentType GetContentType(const MediaInfo& media_info) {
std::string Uint8VectorToBase64(const std::vector<uint8_t>& input) { std::string Uint8VectorToBase64(const std::vector<uint8_t>& input) {
std::string output; std::string output;
std::string input_in_string(input.begin(), input.end()); std::string input_in_string(input.begin(), input.end());
base::Base64Encode(input_in_string, &output); absl::Base64Escape(input_in_string, &output);
return output; return output;
} }

View File

@ -13,7 +13,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "packager/base/base64.h" #include <absl/strings/escaping.h>
#include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"

View File

@ -6,13 +6,13 @@
#include "packager/mpd/base/mpd_utils.h" #include "packager/mpd/base/mpd_utils.h"
#include <gflags/gflags.h> #include <absl/flags/flag.h>
#include <absl/strings/escaping.h>
#include <absl/strings/numbers.h>
#include <glog/logging.h>
#include <libxml/tree.h> #include <libxml/tree.h>
#include "packager/base/base64.h" #include "absl/strings/str_format.h"
#include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/string_util.h"
#include "packager/media/base/language_utils.h" #include "packager/media/base/language_utils.h"
#include "packager/media/base/protection_system_specific_info.h" #include "packager/media/base/protection_system_specific_info.h"
#include "packager/mpd/base/adaptation_set.h" #include "packager/mpd/base/adaptation_set.h"
@ -20,7 +20,8 @@
#include "packager/mpd/base/representation.h" #include "packager/mpd/base/representation.h"
#include "packager/mpd/base/xml/scoped_xml_ptr.h" #include "packager/mpd/base/xml/scoped_xml_ptr.h"
DEFINE_bool( ABSL_FLAG(
bool,
use_legacy_vp9_codec_string, use_legacy_vp9_codec_string,
false, false,
"Use legacy vp9 codec string 'vp9' if set to true; otherwise new style " "Use legacy vp9 codec string 'vp9' if set to true; otherwise new style "
@ -107,7 +108,7 @@ std::string GetCodecs(const MediaInfo& media_info) {
// new codec strings. // new codec strings.
if (codec == "vp08") if (codec == "vp08")
return "vp8"; return "vp8";
if (FLAGS_use_legacy_vp9_codec_string) { if (absl::GetFlag(FLAGS_use_legacy_vp9_codec_string)) {
if (codec == "vp09") if (codec == "vp09")
return "vp9"; return "vp9";
} }
@ -121,7 +122,7 @@ std::string GetCodecs(const MediaInfo& media_info) {
if (media_info.has_text_info()) if (media_info.has_text_info())
return TextCodecString(media_info); return TextCodecString(media_info);
NOTREACHED(); NOTIMPLEMENTED();
return ""; return "";
} }
@ -187,13 +188,30 @@ std::string GetAdaptationSetKey(const MediaInfo& media_info,
return key; return key;
} }
std::string FloatToXmlString(double number) {
// Keep up to microsecond accuracy but trim trailing 0s
std::string formatted = absl::StrFormat("%.6g", number);
size_t decimalPos = formatted.find('.');
if (decimalPos != std::string::npos) {
size_t lastNonZeroPos = formatted.find_last_not_of('0');
if (lastNonZeroPos >= decimalPos) {
formatted.erase(lastNonZeroPos + 1);
}
if (formatted.back() == '.') {
formatted.pop_back();
}
}
return formatted;
}
std::string SecondsToXmlDuration(double seconds) { std::string SecondsToXmlDuration(double seconds) {
// Chrome internally uses time accurate to microseconds, which is implemented // Chrome internally uses time accurate to microseconds, which is implemented
// per MSE spec (https://www.w3.org/TR/media-source/). // per MSE spec (https://www.w3.org/TR/media-source/).
// We need a string formatter that has at least microseconds accuracy for a // We need a string formatter that has at least microseconds accuracy for a
// normal video (with duration up to 3 hours). Chrome's DoubleToString // normal video (with duration up to 3 hours). FloatToXmlString
// implementation meets the requirement. // implementation meets the requirement.
return "PT" + base::DoubleToString(seconds) + "S"; return absl::StrFormat("PT%sS", FloatToXmlString(seconds));
} }
bool GetDurationAttribute(xmlNodePtr node, float* duration) { bool GetDurationAttribute(xmlNodePtr node, float* duration) {
@ -207,7 +225,7 @@ bool GetDurationAttribute(xmlNodePtr node, float* duration) {
return false; return false;
double duration_double_precision = 0.0; double duration_double_precision = 0.0;
if (!base::StringToDouble(reinterpret_cast<const char*>(duration_value.get()), if (!absl::SimpleAtod(reinterpret_cast<const char*>(duration_value.get()),
&duration_double_precision)) { &duration_double_precision)) {
return false; return false;
} }
@ -235,35 +253,28 @@ bool HexToUUID(const std::string& data, std::string* uuid_format) {
if (data.size() != kExpectedUUIDSize) { if (data.size() != kExpectedUUIDSize) {
LOG(ERROR) << "UUID size is expected to be " << kExpectedUUIDSize LOG(ERROR) << "UUID size is expected to be " << kExpectedUUIDSize
<< " but is " << data.size() << " and the data in hex is " << " but is " << data.size() << " and the data in hex is "
<< base::HexEncode(data.data(), data.size()); << absl::BytesToHexString(data);
return false; return false;
} }
const std::string hex_encoded = const std::string hex_encoded =
base::ToLowerASCII(base::HexEncode(data.data(), data.size())); absl::AsciiStrToLower(absl::BytesToHexString(data));
DCHECK_EQ(hex_encoded.size(), kExpectedUUIDSize * 2); DCHECK_EQ(hex_encoded.size(), kExpectedUUIDSize * 2);
base::StringPiece all(hex_encoded); std::string_view all(hex_encoded);
// Note UUID has 5 parts separated with dashes. // Note UUID has 5 parts separated with dashes.
// e.g. 123e4567-e89b-12d3-a456-426655440000 // e.g. 123e4567-e89b-12d3-a456-426655440000
// These StringPieces have each part. // These StringPieces have each part.
base::StringPiece first = all.substr(0, 8); std::string_view first = all.substr(0, 8);
base::StringPiece second = all.substr(8, 4); std::string_view second = all.substr(8, 4);
base::StringPiece third = all.substr(12, 4); std::string_view third = all.substr(12, 4);
base::StringPiece fourth = all.substr(16, 4); std::string_view fourth = all.substr(16, 4);
base::StringPiece fifth = all.substr(20, 12); std::string_view fifth = all.substr(20, 12);
// 32 hexadecimal characters with 4 hyphens. // 32 hexadecimal characters with 4 hyphens.
const size_t kHumanReadableUUIDSize = 36; const size_t kHumanReadableUUIDSize = 36;
uuid_format->reserve(kHumanReadableUUIDSize); uuid_format->reserve(kHumanReadableUUIDSize);
first.CopyToString(uuid_format); absl::StrAppendFormat(uuid_format, "%s-%s-%s-%s-%s", first, second, third,
uuid_format->append("-"); fourth, fifth);
second.AppendToString(uuid_format);
uuid_format->append("-");
third.AppendToString(uuid_format);
uuid_format->append("-");
fourth.AppendToString(uuid_format);
uuid_format->append("-");
fifth.AppendToString(uuid_format);
return true; return true;
} }
@ -327,8 +338,9 @@ const char kMarlinUUID[] = "5e629af5-38da-4063-8977-97ffbd9902d4";
const char kFairPlayUUID[] = "29701fe4-3cc7-4a34-8c5b-ae90c7439a47"; const char kFairPlayUUID[] = "29701fe4-3cc7-4a34-8c5b-ae90c7439a47";
// String representation of media::kPlayReadySystemId. // String representation of media::kPlayReadySystemId.
const char kPlayReadyUUID[] = "9a04f079-9840-4286-ab92-e65be0885f95"; const char kPlayReadyUUID[] = "9a04f079-9840-4286-ab92-e65be0885f95";
// It is RECOMMENDED to include the @value attribute with name and version "MSPR 2.0". // It is RECOMMENDED to include the @value attribute with name and version
// See https://docs.microsoft.com/en-us/playready/specifications/mpeg-dash-playready#221-general. // "MSPR 2.0". See
// https://docs.microsoft.com/en-us/playready/specifications/mpeg-dash-playready#221-general.
const char kContentProtectionValueMSPR20[] = "MSPR 2.0"; const char kContentProtectionValueMSPR20[] = "MSPR 2.0";
Element GenerateMarlinContentIds(const std::string& key_id) { Element GenerateMarlinContentIds(const std::string& key_id) {
@ -341,7 +353,7 @@ Element GenerateMarlinContentIds(const std::string& key_id) {
marlin_content_id.name = kMarlinContentIdName; marlin_content_id.name = kMarlinContentIdName;
marlin_content_id.content = marlin_content_id.content =
kMarlinContentIdPrefix + kMarlinContentIdPrefix +
base::ToLowerASCII(base::HexEncode(key_id.data(), key_id.size())); absl::AsciiStrToLower(absl::BytesToHexString(key_id));
Element marlin_content_ids; Element marlin_content_ids;
marlin_content_ids.name = kMarlinContentIdsName; marlin_content_ids.name = kMarlinContentIdsName;
@ -352,7 +364,7 @@ Element GenerateMarlinContentIds(const std::string& key_id) {
Element GenerateCencPsshElement(const std::string& pssh) { Element GenerateCencPsshElement(const std::string& pssh) {
std::string base64_encoded_pssh; std::string base64_encoded_pssh;
base::Base64Encode(base::StringPiece(pssh.data(), pssh.size()), absl::Base64Escape(std::string_view(pssh.data(), pssh.size()),
&base64_encoded_pssh); &base64_encoded_pssh);
Element cenc_pssh; Element cenc_pssh;
cenc_pssh.name = kPsshElementName; cenc_pssh.name = kPsshElementName;
@ -371,12 +383,10 @@ Element GenerateMsprProElement(const std::string& pssh) {
const std::vector<uint8_t> *p_pssh = &b->pssh_data(); const std::vector<uint8_t> *p_pssh = &b->pssh_data();
std::string base64_encoded_mspr; std::string base64_encoded_mspr;
base::Base64Encode( absl::Base64Escape(
base::StringPiece( std::string_view(reinterpret_cast<const char*>(p_pssh->data()),
reinterpret_cast<const char*>(p_pssh->data()),
p_pssh->size()), p_pssh->size()),
&base64_encoded_mspr &base64_encoded_mspr);
);
Element mspr_pro; Element mspr_pro;
mspr_pro.name = kMsproElementName; mspr_pro.name = kMsproElementName;
mspr_pro.content = base64_encoded_mspr; mspr_pro.content = base64_encoded_mspr;
@ -440,7 +450,7 @@ void AddContentProtectionElementsHelperTemplated(
} else if (entry.uuid() == kMarlinUUID) { } else if (entry.uuid() == kMarlinUUID) {
// Marlin requires its uuid to be in upper case. See #525 for details. // Marlin requires its uuid to be in upper case. See #525 for details.
drm_content_protection.scheme_id_uri = drm_content_protection.scheme_id_uri =
"urn:uuid:" + base::ToUpperASCII(entry.uuid()); "urn:uuid:" + absl::AsciiStrToUpper(entry.uuid());
drm_content_protection.subelements.push_back( drm_content_protection.subelements.push_back(
GenerateMarlinContentIds(protected_content.default_key_id())); GenerateMarlinContentIds(protected_content.default_key_id()));
} else { } else {

View File

@ -50,6 +50,8 @@ std::string GetBaseCodec(const MediaInfo& media_info);
// Returns a key made from the characteristics that separate AdaptationSets. // Returns a key made from the characteristics that separate AdaptationSets.
std::string GetAdaptationSetKey(const MediaInfo& media_info, bool ignore_codec); std::string GetAdaptationSetKey(const MediaInfo& media_info, bool ignore_codec);
std::string FloatToXmlString(double number);
std::string SecondsToXmlDuration(double seconds); std::string SecondsToXmlDuration(double seconds);
// Tries to get "duration" attribute from |node|. On success |duration| is set. // Tries to get "duration" attribute from |node|. On success |duration| is set.

View File

@ -4,14 +4,14 @@
// 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 "packager/mpd/base/mpd_utils.h" #include <absl/strings/escaping.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <memory> #include <memory>
#include "packager/base/strings/string_number_conversions.h" #include "absl/types/span.h"
#include "packager/mpd/base/adaptation_set.h" #include "packager/mpd/base/adaptation_set.h"
#include "packager/mpd/base/mpd_options.h" #include "packager/mpd/base/mpd_options.h"
#include "packager/mpd/base/mpd_utils.h"
#include "packager/mpd/test/mpd_builder_test_helper.h" #include "packager/mpd/test/mpd_builder_test_helper.h"
#include "packager/mpd/test/xml_compare.h" #include "packager/mpd/test/xml_compare.h"
@ -122,8 +122,8 @@ TEST_F(MpdUtilsTest, ContentProtectionPlayReadyCencMspr) {
"98404286AB92E65BE0885F9500000001" "98404286AB92E65BE0885F9500000001"
"11223344556677889900AABBCCDDEEFF" "11223344556677889900AABBCCDDEEFF"
"0000000430313233"); "0000000430313233");
std::vector<uint8_t> pssh;
base::HexStringToBytes(pssh_str, &pssh); std::string pssh = absl::HexStringToBytes(pssh_str);
const char kMediaInfoWithContentProtection[] = const char kMediaInfoWithContentProtection[] =
"video_info {" "video_info {"
@ -179,8 +179,13 @@ TEST_F(MpdUtilsTest, ContentProtectionPlayReadyCenc) {
"98404286AB92E65BE0885F9500000001" "98404286AB92E65BE0885F9500000001"
"11223344556677889900AABBCCDDEEFF" "11223344556677889900AABBCCDDEEFF"
"0000000430313233"); "0000000430313233");
std::vector<uint8_t> pssh;
base::HexStringToBytes(pssh_str, &pssh); std::string pssh_hex_str = absl::HexStringToBytes(pssh_str);
absl::string_view pssh_str_view(pssh_hex_str);
absl::Span<const uint8_t> span(
reinterpret_cast<const uint8_t*>(pssh_str_view.data()),
pssh_str_view.size());
std::vector<uint8_t> pssh = std::vector<uint8_t>(span.begin(), span.end());
const char kMediaInfoWithContentProtection[] = const char kMediaInfoWithContentProtection[] =
"video_info {" "video_info {"
@ -219,10 +224,12 @@ TEST_F(MpdUtilsTest, ContentProtectionPlayReadyCenc) {
" <ContentProtection" " <ContentProtection"
" schemeIdUri='urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95'>" " schemeIdUri='urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95'>"
" <cenc:pssh>" " <cenc:pssh>"
"AAAAOHBzc2gBAAAAmgTweZhAQoarkuZb4IhflQAAAAERIjNEVWZ3iJkAqrvM3e7/AAAABDAxMjM=" "AAAAOHBzc2gBAAAAmgTweZhAQoarkuZb4IhflQAAAAERIjNEVWZ3iJkAqrvM3e7/"
"AAAABDAxMjM="
" </cenc:pssh>" " </cenc:pssh>"
" </ContentProtection>" " </ContentProtection>"
" <Representation id='0' bandwidth='0' codecs='avc1' mimeType='video/mp4'/>" " <Representation id='0' bandwidth='0' codecs='avc1' "
"mimeType='video/mp4'/>"
"</AdaptationSet>"; "</AdaptationSet>";
EXPECT_THAT(adaptation_set_.GetXml(), XmlNodeEqual(kExpectedOutput)); EXPECT_THAT(adaptation_set_.GetXml(), XmlNodeEqual(kExpectedOutput));

View File

@ -5,8 +5,8 @@
// https://developers.google.com/open-source/licenses/bsd // https://developers.google.com/open-source/licenses/bsd
#include "packager/mpd/base/period.h" #include "packager/mpd/base/period.h"
#include <glog/logging.h>
#include "packager/base/stl_util.h"
#include "packager/mpd/base/adaptation_set.h" #include "packager/mpd/base/adaptation_set.h"
#include "packager/mpd/base/mpd_options.h" #include "packager/mpd/base/mpd_options.h"
#include "packager/mpd/base/mpd_utils.h" #include "packager/mpd/base/mpd_utils.h"
@ -122,7 +122,7 @@ AdaptationSet* Period::GetOrCreateAdaptationSet(
return adaptation_set_ptr; return adaptation_set_ptr;
} }
base::Optional<xml::XmlNode> Period::GetXml(bool output_period_duration) { std::optional<xml::XmlNode> Period::GetXml(bool output_period_duration) {
adaptation_sets_.sort( adaptation_sets_.sort(
[](const std::unique_ptr<AdaptationSet>& adaptation_set_a, [](const std::unique_ptr<AdaptationSet>& adaptation_set_a,
const std::unique_ptr<AdaptationSet>& adaptation_set_b) { const std::unique_ptr<AdaptationSet>& adaptation_set_b) {
@ -137,45 +137,45 @@ base::Optional<xml::XmlNode> Period::GetXml(bool output_period_duration) {
// Required for 'dynamic' MPDs. // Required for 'dynamic' MPDs.
if (!period.SetId(id_)) if (!period.SetId(id_))
return base::nullopt; return std::nullopt;
// Required for LL-DASH MPDs. // Required for LL-DASH MPDs.
if (mpd_options_.mpd_params.low_latency_dash_mode) { if (mpd_options_.mpd_params.low_latency_dash_mode) {
// Create ServiceDescription element. // Create ServiceDescription element.
xml::XmlNode service_description_node("ServiceDescription"); xml::XmlNode service_description_node("ServiceDescription");
if (!service_description_node.SetIntegerAttribute("id", id_)) if (!service_description_node.SetIntegerAttribute("id", id_))
return base::nullopt; return std::nullopt;
// Insert Latency into ServiceDescription element. // Insert Latency into ServiceDescription element.
xml::XmlNode latency_node("Latency"); xml::XmlNode latency_node("Latency");
uint64_t target_latency_ms = uint64_t target_latency_ms =
mpd_options_.mpd_params.target_latency_seconds * 1000; mpd_options_.mpd_params.target_latency_seconds * 1000;
if (!latency_node.SetIntegerAttribute("target", target_latency_ms)) if (!latency_node.SetIntegerAttribute("target", target_latency_ms))
return base::nullopt; return std::nullopt;
if (!service_description_node.AddChild(std::move(latency_node))) if (!service_description_node.AddChild(std::move(latency_node)))
return base::nullopt; return std::nullopt;
// Insert ServiceDescription into Period element. // Insert ServiceDescription into Period element.
if (!period.AddChild(std::move(service_description_node))) if (!period.AddChild(std::move(service_description_node)))
return base::nullopt; return std::nullopt;
} }
// Iterate thru AdaptationSets and add them to one big Period element. // Iterate thru AdaptationSets and add them to one big Period element.
for (const auto& adaptation_set : adaptation_sets_) { for (const auto& adaptation_set : adaptation_sets_) {
auto child = adaptation_set->GetXml(); auto child = adaptation_set->GetXml();
if (!child || !period.AddChild(std::move(*child))) if (!child || !period.AddChild(std::move(*child)))
return base::nullopt; return std::nullopt;
} }
if (output_period_duration) { if (output_period_duration) {
if (!period.SetStringAttribute("duration", if (!period.SetStringAttribute("duration",
SecondsToXmlDuration(duration_seconds_))) { SecondsToXmlDuration(duration_seconds_))) {
return base::nullopt; return std::nullopt;
} }
} else if (mpd_options_.mpd_type == MpdType::kDynamic) { } else if (mpd_options_.mpd_type == MpdType::kDynamic) {
if (!period.SetStringAttribute( if (!period.SetStringAttribute(
"start", SecondsToXmlDuration(start_time_in_seconds_))) { "start", SecondsToXmlDuration(start_time_in_seconds_))) {
return base::nullopt; return std::nullopt;
} }
} }
return period; return period;
@ -320,7 +320,8 @@ std::string Period::GetAdaptationSetKeyForTrickPlay(
void Period::ProtectedAdaptationSetMap::Register( void Period::ProtectedAdaptationSetMap::Register(
const AdaptationSet& adaptation_set, const AdaptationSet& adaptation_set,
const MediaInfo& media_info) { const MediaInfo& media_info) {
DCHECK(!ContainsKey(protected_content_map_, &adaptation_set)); CHECK(protected_content_map_.find(&adaptation_set) ==
protected_content_map_.end());
protected_content_map_[&adaptation_set] = media_info.protected_content(); protected_content_map_[&adaptation_set] = media_info.protected_content();
} }

View File

@ -12,7 +12,7 @@
#include <list> #include <list>
#include <map> #include <map>
#include "packager/base/optional.h" #include <optional>
#include "packager/mpd/base/adaptation_set.h" #include "packager/mpd/base/adaptation_set.h"
#include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/xml/xml_node.h" #include "packager/mpd/base/xml/xml_node.h"
@ -46,7 +46,7 @@ class Period {
/// Generates <Period> xml element with its child AdaptationSet elements. /// Generates <Period> xml element with its child AdaptationSet elements.
/// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a /// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a
/// NULL scoped_xml_ptr. /// NULL scoped_xml_ptr.
base::Optional<xml::XmlNode> GetXml(bool output_period_duration); std::optional<xml::XmlNode> GetXml(bool output_period_duration);
/// @return The list of AdaptationSets in this Period. /// @return The list of AdaptationSets in this Period.
const std::list<AdaptationSet*> GetAdaptationSets() const; const std::list<AdaptationSet*> GetAdaptationSets() const;

View File

@ -6,12 +6,11 @@
#include "packager/mpd/base/representation.h" #include "packager/mpd/base/representation.h"
#include <gflags/gflags.h> #include <absl/flags/declare.h>
#include <absl/strings/str_format.h>
#include <glog/logging.h>
#include <algorithm> #include <algorithm>
#include "packager/base/logging.h"
#include "packager/base/strings/stringprintf.h"
#include "packager/file/file.h" #include "packager/file/file.h"
#include "packager/media/base/muxer_util.h" #include "packager/media/base/muxer_util.h"
#include "packager/mpd/base/mpd_options.h" #include "packager/mpd/base/mpd_options.h"
@ -249,10 +248,10 @@ const MediaInfo& Representation::GetMediaInfo() const {
// AddVideoInfo() (possibly adds FramePacking elements), AddAudioInfo() (Adds // AddVideoInfo() (possibly adds FramePacking elements), AddAudioInfo() (Adds
// AudioChannelConfig elements), AddContentProtectionElements*(), and // AudioChannelConfig elements), AddContentProtectionElements*(), and
// AddVODOnlyInfo() (Adds segment info). // AddVODOnlyInfo() (Adds segment info).
base::Optional<xml::XmlNode> Representation::GetXml() { std::optional<xml::XmlNode> Representation::GetXml() {
if (!HasRequiredMediaInfoFields()) { if (!HasRequiredMediaInfoFields()) {
LOG(ERROR) << "MediaInfo missing required fields."; LOG(ERROR) << "MediaInfo missing required fields.";
return base::nullopt; return std::nullopt;
} }
const uint64_t bandwidth = media_info_.has_bandwidth() const uint64_t bandwidth = media_info_.has_bandwidth()
@ -268,7 +267,7 @@ base::Optional<xml::XmlNode> Representation::GetXml() {
!(codecs_.empty() || !(codecs_.empty() ||
representation.SetStringAttribute("codecs", codecs_)) || representation.SetStringAttribute("codecs", codecs_)) ||
!representation.SetStringAttribute("mimeType", mime_type_)) { !representation.SetStringAttribute("mimeType", mime_type_)) {
return base::nullopt; return std::nullopt;
} }
const bool has_video_info = media_info_.has_video_info(); const bool has_video_info = media_info_.has_video_info();
@ -281,18 +280,18 @@ base::Optional<xml::XmlNode> Representation::GetXml() {
!(output_suppression_flags_ & kSuppressHeight), !(output_suppression_flags_ & kSuppressHeight),
!(output_suppression_flags_ & kSuppressFrameRate))) { !(output_suppression_flags_ & kSuppressFrameRate))) {
LOG(ERROR) << "Failed to add video info to Representation XML."; LOG(ERROR) << "Failed to add video info to Representation XML.";
return base::nullopt; return std::nullopt;
} }
if (has_audio_info && if (has_audio_info &&
!representation.AddAudioInfo(media_info_.audio_info())) { !representation.AddAudioInfo(media_info_.audio_info())) {
LOG(ERROR) << "Failed to add audio info to Representation XML."; LOG(ERROR) << "Failed to add audio info to Representation XML.";
return base::nullopt; return std::nullopt;
} }
if (!representation.AddContentProtectionElements( if (!representation.AddContentProtectionElements(
content_protection_elements_)) { content_protection_elements_)) {
return base::nullopt; return std::nullopt;
} }
if (HasVODOnlyFields(media_info_) && if (HasVODOnlyFields(media_info_) &&
@ -300,7 +299,7 @@ base::Optional<xml::XmlNode> Representation::GetXml() {
media_info_, mpd_options_.mpd_params.use_segment_list, media_info_, mpd_options_.mpd_params.use_segment_list,
mpd_options_.mpd_params.target_segment_duration)) { mpd_options_.mpd_params.target_segment_duration)) {
LOG(ERROR) << "Failed to add VOD info."; LOG(ERROR) << "Failed to add VOD info.";
return base::nullopt; return std::nullopt;
} }
if (HasLiveOnlyFields(media_info_) && if (HasLiveOnlyFields(media_info_) &&
@ -308,13 +307,13 @@ base::Optional<xml::XmlNode> Representation::GetXml() {
media_info_, segment_infos_, start_number_, media_info_, segment_infos_, start_number_,
mpd_options_.mpd_params.low_latency_dash_mode)) { mpd_options_.mpd_params.low_latency_dash_mode)) {
LOG(ERROR) << "Failed to add Live info."; LOG(ERROR) << "Failed to add Live info.";
return base::nullopt; return std::nullopt;
} }
// TODO(rkuroiwa): It is likely that all representations have the exact same // TODO(rkuroiwa): It is likely that all representations have the exact same
// SegmentTemplate. Optimize and propagate the tag up to AdaptationSet level. // SegmentTemplate. Optimize and propagate the tag up to AdaptationSet level.
output_suppression_flags_ = 0; output_suppression_flags_ = 0;
return std::move(representation); return representation;
} }
void Representation::SuppressOnce(SuppressFlag flag) { void Representation::SuppressOnce(SuppressFlag flag) {
@ -567,24 +566,24 @@ std::string Representation::GetTextMimeType() const {
} }
std::string Representation::RepresentationAsString() const { std::string Representation::RepresentationAsString() const {
std::string s = base::StringPrintf("Representation (id=%d,", id_); std::string s = absl::StrFormat("Representation (id=%d,", id_);
if (media_info_.has_video_info()) { if (media_info_.has_video_info()) {
const MediaInfo_VideoInfo& video_info = media_info_.video_info(); const MediaInfo_VideoInfo& video_info = media_info_.video_info();
base::StringAppendF(&s, "codec='%s',width=%d,height=%d", absl::StrAppendFormat(&s, "codec='%s',width=%d,height=%d",
video_info.codec().c_str(), video_info.width(), video_info.codec().c_str(), video_info.width(),
video_info.height()); video_info.height());
} else if (media_info_.has_audio_info()) { } else if (media_info_.has_audio_info()) {
const MediaInfo_AudioInfo& audio_info = media_info_.audio_info(); const MediaInfo_AudioInfo& audio_info = media_info_.audio_info();
base::StringAppendF( absl::StrAppendFormat(
&s, "codec='%s',frequency=%d,language='%s'", audio_info.codec().c_str(), &s, "codec='%s',frequency=%d,language='%s'", audio_info.codec().c_str(),
audio_info.sampling_frequency(), audio_info.language().c_str()); audio_info.sampling_frequency(), audio_info.language().c_str());
} else if (media_info_.has_text_info()) { } else if (media_info_.has_text_info()) {
const MediaInfo_TextInfo& text_info = media_info_.text_info(); const MediaInfo_TextInfo& text_info = media_info_.text_info();
base::StringAppendF(&s, "codec='%s',language='%s'", absl::StrAppendFormat(&s, "codec='%s',language='%s'",
text_info.codec().c_str(), text_info.codec().c_str(),
text_info.language().c_str()); text_info.language().c_str());
} }
base::StringAppendF(&s, ")"); absl::StrAppendFormat(&s, ")");
return s; return s;
} }

View File

@ -13,8 +13,8 @@
#include <list> #include <list>
#include <memory> #include <memory>
#include <optional>
#include "packager/base/optional.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"
#include "packager/mpd/base/segment_info.h" #include "packager/mpd/base/segment_info.h"
@ -125,7 +125,7 @@ class Representation {
virtual const MediaInfo& GetMediaInfo() const; virtual const MediaInfo& GetMediaInfo() const;
/// @return Copy of <Representation>. /// @return Copy of <Representation>.
base::Optional<xml::XmlNode> GetXml(); std::optional<xml::XmlNode> GetXml();
/// By calling this methods, the next time GetXml() is /// By calling this methods, the next time GetXml() is
/// called, the corresponding attributes will not be set. /// called, the corresponding attributes will not be set.

View File

@ -6,14 +6,16 @@
#include "packager/mpd/base/representation.h" #include "packager/mpd/base/representation.h"
#include <gflags/gflags.h> #include <absl/flags/declare.h>
#include <absl/flags/flag.h>
#include <absl/strings/str_format.h>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <inttypes.h> #include <inttypes.h>
#include "packager/base/strings/stringprintf.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/flag_saver.h"
#include "packager/mpd/base/mpd_options.h" #include "packager/mpd/base/mpd_options.h"
#include "packager/mpd/test/mpd_builder_test_helper.h" #include "packager/mpd/test/mpd_builder_test_helper.h"
#include "packager/mpd/test/xml_compare.h" #include "packager/mpd/test/xml_compare.h"
@ -23,7 +25,7 @@ using ::testing::Not;
using ::testing::Values; using ::testing::Values;
using ::testing::WithParamInterface; using ::testing::WithParamInterface;
DECLARE_bool(use_legacy_vp9_codec_string); ABSL_DECLARE_FLAG(bool, use_legacy_vp9_codec_string);
namespace shaka { namespace shaka {
namespace { namespace {
@ -234,7 +236,8 @@ TEST_F(RepresentationTest, CheckVideoInfoVp9CodecInWebm) {
} }
TEST_F(RepresentationTest, CheckVideoInfoLegacyVp9CodecInWebm) { TEST_F(RepresentationTest, CheckVideoInfoLegacyVp9CodecInWebm) {
FLAGS_use_legacy_vp9_codec_string = true; FlagSaver<bool> saver(&FLAGS_use_legacy_vp9_codec_string);
absl::SetFlag(&FLAGS_use_legacy_vp9_codec_string, true);
const char kTestMediaInfoCodecVp9[] = const char kTestMediaInfoCodecVp9[] =
"video_info {\n" "video_info {\n"
@ -435,7 +438,7 @@ std::string GetDefaultMediaInfo() {
"init_segment_url: 'init.mp4'\n" "init_segment_url: 'init.mp4'\n"
"segment_template_url: '$Time$.mp4'\n"; "segment_template_url: '$Time$.mp4'\n";
return base::StringPrintf(kMediaInfo, kDefaultTimeScale); return absl::StrFormat(kMediaInfo, kDefaultTimeScale);
} }
} // namespace } // namespace
@ -472,10 +475,10 @@ class SegmentTemplateTest : public RepresentationTest {
if (repeat == 0) { if (repeat == 0) {
expected_s_elements_ += expected_s_elements_ +=
base::StringPrintf(kSElementTemplateWithoutR, start_time, duration); absl::StrFormat(kSElementTemplateWithoutR, start_time, duration);
} else { } else {
expected_s_elements_ += expected_s_elements_ +=
base::StringPrintf(kSElementTemplate, start_time, duration, repeat); absl::StrFormat(kSElementTemplate, start_time, duration, repeat);
} }
for (int i = 0; i < repeat + 1; ++i) { for (int i = 0; i < repeat + 1; ++i) {
@ -511,7 +514,7 @@ class SegmentTemplateTest : public RepresentationTest {
" </SegmentTimeline>\n" " </SegmentTimeline>\n"
" </SegmentTemplate>\n" " </SegmentTemplate>\n"
"</Representation>\n"; "</Representation>\n";
return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Max(), return absl::StrFormat(kOutputTemplate, bandwidth_estimator_.Max(),
expected_s_elements_.c_str()); expected_s_elements_.c_str());
} }
@ -768,7 +771,7 @@ class SegmentTimelineTestBase : public SegmentTemplateTest {
"init_segment_url: 'init.mp4'\n" "init_segment_url: 'init.mp4'\n"
"segment_template_url: '$Number$.mp4'\n"; "segment_template_url: '$Number$.mp4'\n";
const std::string& number_template_media_info = const std::string& number_template_media_info =
base::StringPrintf(kMediaInfo, kDefaultTimeScale); absl::StrFormat(kMediaInfo, kDefaultTimeScale);
mpd_options_.mpd_type = MpdType::kDynamic; mpd_options_.mpd_type = MpdType::kDynamic;
mpd_options_.mpd_params.target_segment_duration = mpd_options_.mpd_params.target_segment_duration =
kTargetSegmentDurationInSeconds; kTargetSegmentDurationInSeconds;
@ -794,9 +797,8 @@ class SegmentTimelineTestBase : public SegmentTemplateTest {
" </SegmentTemplate>\n" " </SegmentTemplate>\n"
"</Representation>\n"; "</Representation>\n";
return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Max(), return absl::StrFormat(kOutputTemplate, bandwidth_estimator_.Max(),
expected_start_number, expected_start_number, expected_s_element.c_str());
expected_s_element.c_str());
} }
}; };
@ -829,11 +831,11 @@ TEST_P(ApproximateSegmentTimelineTest, SegmentDurationAdjusted) {
std::string expected_s_elements; std::string expected_s_elements;
if (allow_approximate_segment_timeline_) { if (allow_approximate_segment_timeline_) {
expected_s_elements = base::StringPrintf( expected_s_elements = absl::StrFormat(kSElementTemplateWithoutR, kStartTime,
kSElementTemplateWithoutR, kStartTime, kScaledTargetSegmentDuration); kScaledTargetSegmentDuration);
} else { } else {
expected_s_elements = base::StringPrintf(kSElementTemplateWithoutR, expected_s_elements = absl::StrFormat(kSElementTemplateWithoutR, kStartTime,
kStartTime, kDurationSmaller); kDurationSmaller);
} }
EXPECT_THAT(representation_->GetXml(), EXPECT_THAT(representation_->GetXml(),
XmlNodeEqual(ExpectedXml(expected_s_elements))); XmlNodeEqual(ExpectedXml(expected_s_elements)));
@ -850,11 +852,11 @@ TEST_P(ApproximateSegmentTimelineTest,
std::string expected_s_elements; std::string expected_s_elements;
if (allow_approximate_segment_timeline_) { if (allow_approximate_segment_timeline_) {
expected_s_elements = base::StringPrintf( expected_s_elements = absl::StrFormat(kSElementTemplateWithoutR, kStartTime,
kSElementTemplateWithoutR, kStartTime, kScaledTargetSegmentDuration); kScaledTargetSegmentDuration);
} else { } else {
expected_s_elements = base::StringPrintf(kSElementTemplateWithoutR, expected_s_elements = absl::StrFormat(kSElementTemplateWithoutR, kStartTime,
kStartTime, kDurationSmaller); kDurationSmaller);
} }
EXPECT_THAT(representation_->GetXml(), EXPECT_THAT(representation_->GetXml(),
XmlNodeEqual(ExpectedXml(expected_s_elements))); XmlNodeEqual(ExpectedXml(expected_s_elements)));
@ -876,15 +878,15 @@ TEST_P(ApproximateSegmentTimelineTest, SegmentsWithSimilarDurations) {
if (allow_approximate_segment_timeline_) { if (allow_approximate_segment_timeline_) {
int kNumSegments = 3; int kNumSegments = 3;
expected_s_elements = expected_s_elements =
base::StringPrintf(kSElementTemplate, kStartTime, absl::StrFormat(kSElementTemplate, kStartTime,
kScaledTargetSegmentDuration, kNumSegments - 1); kScaledTargetSegmentDuration, kNumSegments - 1);
} else { } else {
expected_s_elements = expected_s_elements =
base::StringPrintf(kSElementTemplateWithoutR, kStartTime, absl::StrFormat(kSElementTemplateWithoutR, kStartTime,
kDurationSmaller) + kDurationSmaller) +
base::StringPrintf(kSElementTemplateWithoutR, absl::StrFormat(kSElementTemplateWithoutR,
kStartTime + kDurationSmaller, kDurationLarger) + kStartTime + kDurationSmaller, kDurationLarger) +
base::StringPrintf(kSElementTemplateWithoutR, absl::StrFormat(kSElementTemplateWithoutR,
kStartTime + kDurationSmaller + kDurationLarger, kStartTime + kDurationSmaller + kDurationLarger,
kDurationSmaller); kDurationSmaller);
} }
@ -911,7 +913,7 @@ TEST_P(ApproximateSegmentTimelineTest, SegmentsWithSimilarDurations2) {
"<S t=\"20\" d=\"13\"/>"; "<S t=\"20\" d=\"13\"/>";
} else { } else {
int kNumSegments = 3; int kNumSegments = 3;
expected_s_elements = base::StringPrintf(kSElementTemplate, kStartTime, expected_s_elements = absl::StrFormat(kSElementTemplate, kStartTime,
kDurationLarger, kNumSegments - 1); kDurationLarger, kNumSegments - 1);
} }
EXPECT_THAT(representation_->GetXml(), EXPECT_THAT(representation_->GetXml(),
@ -930,12 +932,12 @@ TEST_P(ApproximateSegmentTimelineTest, FillSmallGap) {
std::string expected_s_elements; std::string expected_s_elements;
if (allow_approximate_segment_timeline_) { if (allow_approximate_segment_timeline_) {
int kNumSegments = 3; int kNumSegments = 3;
expected_s_elements = base::StringPrintf(kSElementTemplate, kStartTime, expected_s_elements = absl::StrFormat(kSElementTemplate, kStartTime,
kDuration, kNumSegments - 1); kDuration, kNumSegments - 1);
} else { } else {
expected_s_elements = expected_s_elements =
base::StringPrintf(kSElementTemplateWithoutR, kStartTime, kDuration) + absl::StrFormat(kSElementTemplateWithoutR, kStartTime, kDuration) +
base::StringPrintf(kSElementTemplate, kStartTime + kDuration + kGap, absl::StrFormat(kSElementTemplate, kStartTime + kDuration + kGap,
kDuration, 1 /* repeat */); kDuration, 1 /* repeat */);
} }
EXPECT_THAT(representation_->GetXml(), EXPECT_THAT(representation_->GetXml(),
@ -954,12 +956,12 @@ TEST_P(ApproximateSegmentTimelineTest, FillSmallOverlap) {
std::string expected_s_elements; std::string expected_s_elements;
if (allow_approximate_segment_timeline_) { if (allow_approximate_segment_timeline_) {
int kNumSegments = 3; int kNumSegments = 3;
expected_s_elements = base::StringPrintf(kSElementTemplate, kStartTime, expected_s_elements = absl::StrFormat(kSElementTemplate, kStartTime,
kDuration, kNumSegments - 1); kDuration, kNumSegments - 1);
} else { } else {
expected_s_elements = expected_s_elements =
base::StringPrintf(kSElementTemplateWithoutR, kStartTime, kDuration) + absl::StrFormat(kSElementTemplateWithoutR, kStartTime, kDuration) +
base::StringPrintf(kSElementTemplate, kStartTime + kDuration - kOverlap, absl::StrFormat(kSElementTemplate, kStartTime + kDuration - kOverlap,
kDuration, 1 /* repeat */); kDuration, 1 /* repeat */);
} }
EXPECT_THAT(representation_->GetXml(), EXPECT_THAT(representation_->GetXml(),
@ -1045,7 +1047,7 @@ TEST_P(TimeShiftBufferDepthTest, Normal) {
const int kExpectedRepeatsLeft = kTimeShiftBufferDepth; const int kExpectedRepeatsLeft = kTimeShiftBufferDepth;
const int kExpectedStartNumber = kRepeat - kExpectedRepeatsLeft + 1; const int kExpectedStartNumber = kRepeat - kExpectedRepeatsLeft + 1;
const std::string expected_s_element = base::StringPrintf( const std::string expected_s_element = absl::StrFormat(
kSElementTemplate, kSElementTemplate,
initial_start_time_ + kDuration * (kRepeat - kExpectedRepeatsLeft), initial_start_time_ + kDuration * (kRepeat - kExpectedRepeatsLeft),
kDuration, kExpectedRepeatsLeft); kDuration, kExpectedRepeatsLeft);
@ -1071,7 +1073,7 @@ TEST_P(TimeShiftBufferDepthTest, TimeShiftBufferDepthShorterThanSegmentLength) {
AddSegments(initial_start_time_, kDuration, kSize, kRepeat); AddSegments(initial_start_time_, kDuration, kSize, kRepeat);
const std::string expected_s_element = base::StringPrintf( const std::string expected_s_element = absl::StrFormat(
kSElementTemplate, initial_start_time_, kDuration, kRepeat); kSElementTemplate, initial_start_time_, kDuration, kRepeat);
EXPECT_THAT( EXPECT_THAT(
representation_->GetXml(), representation_->GetXml(),
@ -1102,7 +1104,7 @@ TEST_P(TimeShiftBufferDepthTest, Generic) {
// Expect only the latest S element with 2 segments. // Expect only the latest S element with 2 segments.
const std::string expected_s_element = const std::string expected_s_element =
base::StringPrintf(kSElementTemplate, first_s_element_end_time, absl::StrFormat(kSElementTemplate, first_s_element_end_time,
kTimeShiftBufferDepthDuration, kMoreSegmentsRepeat); kTimeShiftBufferDepthDuration, kMoreSegmentsRepeat);
const int kExpectedRemovedSegments = kRepeat + 1; const int kExpectedRemovedSegments = kRepeat + 1;
@ -1140,12 +1142,12 @@ TEST_P(TimeShiftBufferDepthTest, MoreThanOneS) {
(kOneSecondSegmentRepeat + 1 + kTwoSecondSegmentRepeat * 2) - (kOneSecondSegmentRepeat + 1 + kTwoSecondSegmentRepeat * 2) -
kTimeShiftBufferDepth; kTimeShiftBufferDepth;
std::string expected_s_element = base::StringPrintf( std::string expected_s_element = absl::StrFormat(
kSElementTemplate, kSElementTemplate,
initial_start_time_ + kOneSecondDuration * kExpectedRemovedSegments, initial_start_time_ + kOneSecondDuration * kExpectedRemovedSegments,
kOneSecondDuration, kOneSecondSegmentRepeat - kExpectedRemovedSegments); kOneSecondDuration, kOneSecondSegmentRepeat - kExpectedRemovedSegments);
expected_s_element += expected_s_element +=
base::StringPrintf(kSElementTemplate, first_s_element_end_time, absl::StrFormat(kSElementTemplate, first_s_element_end_time,
kTwoSecondDuration, kTwoSecondSegmentRepeat); kTwoSecondDuration, kTwoSecondSegmentRepeat);
EXPECT_THAT( EXPECT_THAT(
@ -1182,13 +1184,13 @@ TEST_P(TimeShiftBufferDepthTest, UseLastSegmentInS) {
AddSegments(first_s_element_end_time, kTwoSecondDuration, kSize, AddSegments(first_s_element_end_time, kTwoSecondDuration, kSize,
kTwoSecondSegmentRepeat); kTwoSecondSegmentRepeat);
std::string expected_s_element = base::StringPrintf( std::string expected_s_element = absl::StrFormat(
kSElementTemplateWithoutR, kSElementTemplateWithoutR,
initial_start_time_ + kDuration1, // Expect one segment removed. initial_start_time_ + kDuration1, // Expect one segment removed.
kDuration1); kDuration1);
expected_s_element += expected_s_element +=
base::StringPrintf(kSElementTemplate, first_s_element_end_time, absl::StrFormat(kSElementTemplate, first_s_element_end_time,
kTwoSecondDuration, kTwoSecondSegmentRepeat); kTwoSecondDuration, kTwoSecondSegmentRepeat);
EXPECT_THAT(representation_->GetXml(), EXPECT_THAT(representation_->GetXml(),
XmlNodeEqual(ExpectedXml(expected_s_element, 2))); XmlNodeEqual(ExpectedXml(expected_s_element, 2)));
@ -1216,9 +1218,9 @@ TEST_P(TimeShiftBufferDepthTest, NormalGap) {
const int64_t gap_s_element_start_time = first_s_element_end_time + 1; const int64_t gap_s_element_start_time = first_s_element_end_time + 1;
AddSegments(gap_s_element_start_time, kDuration, kSize, /* no repeat */ 0); AddSegments(gap_s_element_start_time, kDuration, kSize, /* no repeat */ 0);
std::string expected_s_element = base::StringPrintf( std::string expected_s_element = absl::StrFormat(
kSElementTemplate, initial_start_time_, kDuration, kRepeat); kSElementTemplate, initial_start_time_, kDuration, kRepeat);
expected_s_element += base::StringPrintf(kSElementTemplateWithoutR, expected_s_element += absl::StrFormat(kSElementTemplateWithoutR,
gap_s_element_start_time, kDuration); gap_s_element_start_time, kDuration);
EXPECT_THAT( EXPECT_THAT(
@ -1252,9 +1254,9 @@ TEST_P(TimeShiftBufferDepthTest, HugeGap) {
kSecondSElementRepeat); kSecondSElementRepeat);
std::string expected_s_element = std::string expected_s_element =
base::StringPrintf(kSElementTemplateWithoutR, absl::StrFormat(kSElementTemplateWithoutR,
initial_start_time_ + kRepeat * kDuration, kDuration) + initial_start_time_ + kRepeat * kDuration, kDuration) +
base::StringPrintf(kSElementTemplate, gap_s_element_start_time, kDuration, absl::StrFormat(kSElementTemplate, gap_s_element_start_time, kDuration,
kSecondSElementRepeat); kSecondSElementRepeat);
const int kExpectedRemovedSegments = kRepeat; const int kExpectedRemovedSegments = kRepeat;
EXPECT_THAT( EXPECT_THAT(
@ -1282,7 +1284,7 @@ TEST_P(TimeShiftBufferDepthTest, ManySegments) {
const int kExpectedStartNumber = const int kExpectedStartNumber =
kDefaultStartNumber + kExpectedRemovedSegments; kDefaultStartNumber + kExpectedRemovedSegments;
std::string expected_s_element = base::StringPrintf( std::string expected_s_element = absl::StrFormat(
kSElementTemplate, kSElementTemplate,
initial_start_time_ + kExpectedRemovedSegments * kDuration, kDuration, initial_start_time_ + kExpectedRemovedSegments * kDuration, kDuration,
kExpectedSegmentsRepeat); kExpectedSegmentsRepeat);
@ -1318,8 +1320,8 @@ class RepresentationDeleteSegmentsTest : public SegmentTimelineTestBase {
// Create 100 files with the template. // Create 100 files with the template.
for (int i = 1; i <= 100; ++i) { for (int i = 1; i <= 100; ++i) {
File::WriteStringToFile( File::WriteStringToFile(absl::StrFormat(kStringPrintTemplate, i).c_str(),
base::StringPrintf(kStringPrintTemplate, i).c_str(), "dummy content"); "dummy content");
} }
MediaInfo media_info = ConvertToMediaInfo(GetDefaultMediaInfo()); MediaInfo media_info = ConvertToMediaInfo(GetDefaultMediaInfo());
@ -1348,8 +1350,7 @@ TEST_F(RepresentationDeleteSegmentsTest, NoSegmentsDeletedInitially) {
AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat); AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat);
} }
for (int i = 0; i < kMaxNumSegmentsAvailable; ++i) { for (int i = 0; i < kMaxNumSegmentsAvailable; ++i) {
EXPECT_FALSE( EXPECT_FALSE(SegmentDeleted(absl::StrFormat(kStringPrintTemplate, i + 1)));
SegmentDeleted(base::StringPrintf(kStringPrintTemplate, i + 1)));
} }
} }
@ -1357,8 +1358,8 @@ TEST_F(RepresentationDeleteSegmentsTest, OneSegmentDeleted) {
for (int i = 0; i <= kMaxNumSegmentsAvailable; ++i) { for (int i = 0; i <= kMaxNumSegmentsAvailable; ++i) {
AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat); AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat);
} }
EXPECT_FALSE(SegmentDeleted(base::StringPrintf(kStringPrintTemplate, 2))); EXPECT_FALSE(SegmentDeleted(absl::StrFormat(kStringPrintTemplate, 2)));
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(kStringPrintTemplate, 1))); EXPECT_TRUE(SegmentDeleted(absl::StrFormat(kStringPrintTemplate, 1)));
} }
// Verify that segments are deleted as expected with many non-repeating // Verify that segments are deleted as expected with many non-repeating
@ -1371,9 +1372,9 @@ TEST_F(RepresentationDeleteSegmentsTest, ManyNonRepeatingSegments) {
const int last_available_segment_index = const int last_available_segment_index =
many_segments - kMaxNumSegmentsAvailable + 1; many_segments - kMaxNumSegmentsAvailable + 1;
EXPECT_FALSE(SegmentDeleted( EXPECT_FALSE(SegmentDeleted(
base::StringPrintf(kStringPrintTemplate, last_available_segment_index))); absl::StrFormat(kStringPrintTemplate, last_available_segment_index)));
EXPECT_TRUE(SegmentDeleted(base::StringPrintf( EXPECT_TRUE(SegmentDeleted(
kStringPrintTemplate, last_available_segment_index - 1))); absl::StrFormat(kStringPrintTemplate, last_available_segment_index - 1)));
} }
// Verify that segments are deleted as expected with many repeating segments. // Verify that segments are deleted as expected with many repeating segments.
@ -1388,9 +1389,9 @@ TEST_F(RepresentationDeleteSegmentsTest, ManyRepeatingSegments) {
const int last_available_segment_index = const int last_available_segment_index =
kNumSegments - kMaxNumSegmentsAvailable + 1; kNumSegments - kMaxNumSegmentsAvailable + 1;
EXPECT_FALSE(SegmentDeleted( EXPECT_FALSE(SegmentDeleted(
base::StringPrintf(kStringPrintTemplate, last_available_segment_index))); absl::StrFormat(kStringPrintTemplate, last_available_segment_index)));
EXPECT_TRUE(SegmentDeleted(base::StringPrintf( EXPECT_TRUE(SegmentDeleted(
kStringPrintTemplate, last_available_segment_index - 1))); absl::StrFormat(kStringPrintTemplate, last_available_segment_index - 1)));
} }
} // namespace shaka } // namespace shaka

View File

@ -6,8 +6,7 @@
#include "packager/mpd/base/simple_mpd_notifier.h" #include "packager/mpd/base/simple_mpd_notifier.h"
#include "packager/base/logging.h" #include <glog/logging.h>
#include "packager/base/stl_util.h"
#include "packager/mpd/base/adaptation_set.h" #include "packager/mpd/base/adaptation_set.h"
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/mpd_notifier_util.h" #include "packager/mpd/base/mpd_notifier_util.h"
@ -44,7 +43,7 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
MediaInfo adjusted_media_info(media_info); MediaInfo adjusted_media_info(media_info);
MpdBuilder::MakePathsRelativeToMpd(output_path_, &adjusted_media_info); MpdBuilder::MakePathsRelativeToMpd(output_path_, &adjusted_media_info);
base::AutoLock auto_lock(lock_); absl::MutexLock auto_lock(&lock_);
const double kPeriodStartTimeSeconds = 0.0; const double kPeriodStartTimeSeconds = 0.0;
Period* period = mpd_builder_->GetOrCreatePeriod(kPeriodStartTimeSeconds); Period* period = mpd_builder_->GetOrCreatePeriod(kPeriodStartTimeSeconds);
DCHECK(period); DCHECK(period);
@ -72,7 +71,7 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
} }
bool SimpleMpdNotifier::NotifyAvailabilityTimeOffset(uint32_t container_id) { bool SimpleMpdNotifier::NotifyAvailabilityTimeOffset(uint32_t container_id) {
base::AutoLock auto_lock(lock_); absl::MutexLock lock(&lock_);
auto it = representation_map_.find(container_id); auto it = representation_map_.find(container_id);
if (it == representation_map_.end()) { if (it == representation_map_.end()) {
LOG(ERROR) << "Unexpected container_id: " << container_id; LOG(ERROR) << "Unexpected container_id: " << container_id;
@ -84,7 +83,7 @@ bool SimpleMpdNotifier::NotifyAvailabilityTimeOffset(uint32_t container_id) {
bool SimpleMpdNotifier::NotifySampleDuration(uint32_t container_id, bool SimpleMpdNotifier::NotifySampleDuration(uint32_t container_id,
int32_t sample_duration) { int32_t sample_duration) {
base::AutoLock auto_lock(lock_); absl::MutexLock lock(&lock_);
auto it = representation_map_.find(container_id); auto it = representation_map_.find(container_id);
if (it == representation_map_.end()) { if (it == representation_map_.end()) {
LOG(ERROR) << "Unexpected container_id: " << container_id; LOG(ERROR) << "Unexpected container_id: " << container_id;
@ -95,7 +94,7 @@ bool SimpleMpdNotifier::NotifySampleDuration(uint32_t container_id,
} }
bool SimpleMpdNotifier::NotifySegmentDuration(uint32_t container_id) { bool SimpleMpdNotifier::NotifySegmentDuration(uint32_t container_id) {
base::AutoLock auto_lock(lock_); absl::MutexLock lock(&lock_);
auto it = representation_map_.find(container_id); auto it = representation_map_.find(container_id);
if (it == representation_map_.end()) { if (it == representation_map_.end()) {
LOG(ERROR) << "Unexpected container_id: " << container_id; LOG(ERROR) << "Unexpected container_id: " << container_id;
@ -109,7 +108,7 @@ bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id,
int64_t start_time, int64_t start_time,
int64_t duration, int64_t duration,
uint64_t size) { uint64_t size) {
base::AutoLock auto_lock(lock_); absl::MutexLock lock(&lock_);
auto it = representation_map_.find(container_id); auto it = representation_map_.find(container_id);
if (it == representation_map_.end()) { if (it == representation_map_.end()) {
LOG(ERROR) << "Unexpected container_id: " << container_id; LOG(ERROR) << "Unexpected container_id: " << container_id;
@ -122,7 +121,7 @@ bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id,
bool SimpleMpdNotifier::NotifyCompletedSegment(uint32_t container_id, bool SimpleMpdNotifier::NotifyCompletedSegment(uint32_t container_id,
int64_t duration, int64_t duration,
uint64_t size) { uint64_t size) {
base::AutoLock auto_lock(lock_); absl::MutexLock lock(&lock_);
auto it = representation_map_.find(container_id); auto it = representation_map_.find(container_id);
if (it == representation_map_.end()) { if (it == representation_map_.end()) {
LOG(ERROR) << "Unexpected container_id: " << container_id; LOG(ERROR) << "Unexpected container_id: " << container_id;
@ -134,7 +133,7 @@ bool SimpleMpdNotifier::NotifyCompletedSegment(uint32_t container_id,
bool SimpleMpdNotifier::NotifyCueEvent(uint32_t container_id, bool SimpleMpdNotifier::NotifyCueEvent(uint32_t container_id,
int64_t timestamp) { int64_t timestamp) {
base::AutoLock auto_lock(lock_); absl::MutexLock lock(&lock_);
auto it = representation_map_.find(container_id); auto it = representation_map_.find(container_id);
if (it == representation_map_.end()) { if (it == representation_map_.end()) {
LOG(ERROR) << "Unexpected container_id: " << container_id; LOG(ERROR) << "Unexpected container_id: " << container_id;
@ -181,7 +180,7 @@ bool SimpleMpdNotifier::NotifyEncryptionUpdate(
const std::string& drm_uuid, const std::string& drm_uuid,
const std::vector<uint8_t>& new_key_id, const std::vector<uint8_t>& new_key_id,
const std::vector<uint8_t>& new_pssh) { const std::vector<uint8_t>& new_pssh) {
base::AutoLock auto_lock(lock_); absl::MutexLock lock(&lock_);
auto it = representation_map_.find(container_id); auto it = representation_map_.find(container_id);
if (it == representation_map_.end()) { if (it == representation_map_.end()) {
LOG(ERROR) << "Unexpected container_id: " << container_id; LOG(ERROR) << "Unexpected container_id: " << container_id;
@ -202,7 +201,7 @@ bool SimpleMpdNotifier::NotifyEncryptionUpdate(
bool SimpleMpdNotifier::NotifyMediaInfoUpdate(uint32_t container_id, bool SimpleMpdNotifier::NotifyMediaInfoUpdate(uint32_t container_id,
const MediaInfo& media_info) { const MediaInfo& media_info) {
base::AutoLock auto_lock(lock_); absl::MutexLock lock(&lock_);
auto it = representation_map_.find(container_id); auto it = representation_map_.find(container_id);
if (it == representation_map_.end()) { if (it == representation_map_.end()) {
LOG(ERROR) << "Unexpected container_id: " << container_id; LOG(ERROR) << "Unexpected container_id: " << container_id;
@ -217,7 +216,7 @@ bool SimpleMpdNotifier::NotifyMediaInfoUpdate(uint32_t container_id,
} }
bool SimpleMpdNotifier::Flush() { bool SimpleMpdNotifier::Flush() {
base::AutoLock auto_lock(lock_); absl::MutexLock lock(&lock_);
return WriteMpdToFile(output_path_, mpd_builder_.get()); return WriteMpdToFile(output_path_, mpd_builder_.get());
} }

View File

@ -12,7 +12,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "packager/base/synchronization/lock.h" #include "absl/synchronization/mutex.h"
#include "packager/mpd/base/mpd_notifier.h" #include "packager/mpd/base/mpd_notifier.h"
#include "packager/mpd/base/mpd_notifier_util.h" #include "packager/mpd/base/mpd_notifier_util.h"
@ -75,7 +75,7 @@ class SimpleMpdNotifier : public MpdNotifier {
std::string output_path_; std::string output_path_;
std::unique_ptr<MpdBuilder> mpd_builder_; std::unique_ptr<MpdBuilder> mpd_builder_;
bool content_protection_in_adaptation_set_ = true; bool content_protection_in_adaptation_set_ = true;
base::Lock lock_; absl::Mutex lock_;
uint32_t next_adaptation_set_id_ = 0; uint32_t next_adaptation_set_id_ = 0;
// Maps Representation ID to Representation. // Maps Representation ID to Representation.

View File

@ -7,9 +7,9 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <google/protobuf/util/message_differencer.h> #include <google/protobuf/util/message_differencer.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <filesystem>
#include "packager/base/files/file_path.h" #include "packager/file/file_test_util.h"
#include "packager/base/files/file_util.h"
#include "packager/mpd/base/mock_mpd_builder.h" #include "packager/mpd/base/mock_mpd_builder.h"
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/mpd_options.h" #include "packager/mpd/base/mpd_options.h"
@ -45,8 +45,7 @@ class SimpleMpdNotifierTest : public ::testing::Test {
default_mock_adaptation_set_(new MockAdaptationSet()) {} default_mock_adaptation_set_(new MockAdaptationSet()) {}
void SetUp() override { void SetUp() override {
ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_)); empty_mpd_option_.mpd_params.mpd_output = temp_file_.path();
empty_mpd_option_.mpd_params.mpd_output = temp_file_path_.AsUTF8Unsafe();
// Three valid media info. The actual data does not matter. // Three valid media info. The actual data does not matter.
const char kValidMediaInfo[] = const char kValidMediaInfo[] =
@ -68,9 +67,7 @@ class SimpleMpdNotifierTest : public ::testing::Test {
valid_media_info3_.mutable_video_info()->set_width(480); valid_media_info3_.mutable_video_info()->set_width(480);
} }
void TearDown() override { void TearDown() override {}
base::DeleteFile(temp_file_path_, false /* non recursive, just 1 file */);
}
void SetMpdBuilder(SimpleMpdNotifier* notifier, void SetMpdBuilder(SimpleMpdNotifier* notifier,
std::unique_ptr<MpdBuilder> mpd_builder) { std::unique_ptr<MpdBuilder> mpd_builder) {
@ -93,7 +90,7 @@ class SimpleMpdNotifierTest : public ::testing::Test {
MediaInfo valid_media_info3_; MediaInfo valid_media_info3_;
private: private:
base::FilePath temp_file_path_; TempFile temp_file_;
}; };
// Verify NotifyNewContainer() works as expected for VOD. // Verify NotifyNewContainer() works as expected for VOD.
@ -329,7 +326,7 @@ TEST_F(SimpleMpdNotifierTest,
0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e,
0x67, 0x65, 0x6c, 0x73, 0x65}; 0x67, 0x65, 0x6c, 0x73, 0x65};
const std::vector<uint8_t> kBogusNewPsshVector( const std::vector<uint8_t> kBogusNewPsshVector(
kBogusNewPssh, kBogusNewPssh + arraysize(kBogusNewPssh)); kBogusNewPssh, kBogusNewPssh + std::size(kBogusNewPssh));
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U="; const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
EXPECT_CALL(*default_mock_adaptation_set_, EXPECT_CALL(*default_mock_adaptation_set_,
@ -372,7 +369,7 @@ TEST_F(SimpleMpdNotifierTest,
0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e,
0x67, 0x65, 0x6c, 0x73, 0x65}; 0x67, 0x65, 0x6c, 0x73, 0x65};
const std::vector<uint8_t> kBogusNewPsshVector( const std::vector<uint8_t> kBogusNewPsshVector(
kBogusNewPssh, kBogusNewPssh + arraysize(kBogusNewPssh)); kBogusNewPssh, kBogusNewPssh + std::size(kBogusNewPssh));
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U="; const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
EXPECT_CALL(*mock_representation, EXPECT_CALL(*mock_representation,

View File

@ -6,29 +6,33 @@
#include "packager/mpd/base/xml/xml_node.h" #include "packager/mpd/base/xml/xml_node.h"
#include <gflags/gflags.h> #include <absl/base/internal/endian.h>
#include <absl/flags/flag.h>
#include <absl/strings/numbers.h>
#include <glog/logging.h>
#include <libxml/tree.h> #include <libxml/tree.h>
#include <cmath> #include <cmath>
#include <limits> #include <limits>
#include <set> #include <set>
#include "packager/base/logging.h" #include "absl/strings/escaping.h"
#include "packager/base/macros.h" #include "absl/strings/str_format.h"
#include "packager/base/strings/string_number_conversions.h" #include "packager/macros.h"
#include "packager/base/sys_byteorder.h"
#include "packager/media/base/rcheck.h" #include "packager/media/base/rcheck.h"
#include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/mpd_utils.h" #include "packager/mpd/base/mpd_utils.h"
#include "packager/mpd/base/segment_info.h" #include "packager/mpd/base/segment_info.h"
#include "packager/mpd/base/xml/scoped_xml_ptr.h" #include "packager/mpd/base/xml/scoped_xml_ptr.h"
DEFINE_bool(segment_template_constant_duration, ABSL_FLAG(bool,
segment_template_constant_duration,
false, false,
"Generates SegmentTemplate@duration if all segments except the " "Generates SegmentTemplate@duration if all segments except the "
"last one has the same duration if this flag is set to true."); "last one has the same duration if this flag is set to true.");
DEFINE_bool(dash_add_last_segment_number_when_needed, ABSL_FLAG(bool,
dash_add_last_segment_number_when_needed,
false, false,
"Adds a Supplemental Descriptor with @schemeIdUri " "Adds a Supplemental Descriptor with @schemeIdUri "
"set to http://dashif.org/guidelines/last-segment-number with " "set to http://dashif.org/guidelines/last-segment-number with "
@ -45,15 +49,14 @@ const char kEC3Codec[] = "ec-3";
const char kAC4Codec[] = "ac-4"; const char kAC4Codec[] = "ac-4";
std::string RangeToString(const Range& range) { std::string RangeToString(const Range& range) {
return base::Uint64ToString(range.begin()) + "-" + return absl::StrFormat("%u-%u", range.begin(), range.end());
base::Uint64ToString(range.end());
} }
// Check if segments are continuous and all segments except the last one are of // Check if segments are continuous and all segments except the last one are of
// the same duration. // the same duration.
bool IsTimelineConstantDuration(const std::list<SegmentInfo>& segment_infos, bool IsTimelineConstantDuration(const std::list<SegmentInfo>& segment_infos,
uint32_t start_number) { uint32_t start_number) {
if (!FLAGS_segment_template_constant_duration) if (!absl::GetFlag(FLAGS_segment_template_constant_duration))
return false; return false;
DCHECK(!segment_infos.empty()); DCHECK(!segment_infos.empty());
@ -145,7 +148,7 @@ bool XmlNode::AddChild(XmlNode child) {
// Reaching here means the ownership of |child| transfered to |node|. // Reaching here means the ownership of |child| transfered to |node|.
// Release the pointer so that it doesn't get destructed in this scope. // Release the pointer so that it doesn't get destructed in this scope.
ignore_result(child.impl_->node.release()); UNUSED(child.impl_->node.release());
return true; return true;
} }
@ -192,14 +195,15 @@ bool XmlNode::SetIntegerAttribute(const std::string& attribute_name,
uint64_t number) { uint64_t number) {
DCHECK(impl_->node); DCHECK(impl_->node);
return xmlSetProp(impl_->node.get(), BAD_CAST attribute_name.c_str(), return xmlSetProp(impl_->node.get(), BAD_CAST attribute_name.c_str(),
BAD_CAST(base::Uint64ToString(number).c_str())) != nullptr; BAD_CAST(absl::StrFormat("%" PRIu64, number).c_str())) !=
nullptr;
} }
bool XmlNode::SetFloatingPointAttribute(const std::string& attribute_name, bool XmlNode::SetFloatingPointAttribute(const std::string& attribute_name,
double number) { double number) {
DCHECK(impl_->node); DCHECK(impl_->node);
return xmlSetProp(impl_->node.get(), BAD_CAST attribute_name.c_str(), return xmlSetProp(impl_->node.get(), BAD_CAST attribute_name.c_str(),
BAD_CAST(base::DoubleToString(number).c_str())) != nullptr; BAD_CAST(FloatToXmlString(number).c_str())) != nullptr;
} }
bool XmlNode::SetId(uint32_t id) { bool XmlNode::SetId(uint32_t id) {
@ -345,9 +349,9 @@ bool RepresentationXmlNode::AddVideoInfo(const VideoInfo& video_info,
} }
if (video_info.has_pixel_width() && video_info.has_pixel_height()) { if (video_info.has_pixel_width() && video_info.has_pixel_height()) {
RCHECK(SetStringAttribute( RCHECK(SetStringAttribute("sar",
"sar", base::IntToString(video_info.pixel_width()) + ":" + absl::StrFormat("%d:%d", video_info.pixel_width(),
base::IntToString(video_info.pixel_height()))); video_info.pixel_height())));
} }
if (set_width) if (set_width)
@ -355,14 +359,14 @@ bool RepresentationXmlNode::AddVideoInfo(const VideoInfo& video_info,
if (set_height) if (set_height)
RCHECK(SetIntegerAttribute("height", video_info.height())); RCHECK(SetIntegerAttribute("height", video_info.height()));
if (set_frame_rate) { if (set_frame_rate) {
RCHECK(SetStringAttribute( RCHECK(SetStringAttribute("frameRate",
"frameRate", base::IntToString(video_info.time_scale()) + "/" + absl::StrFormat("%d/%d", video_info.time_scale(),
base::IntToString(video_info.frame_duration()))); video_info.frame_duration())));
} }
if (video_info.has_playback_rate()) { if (video_info.has_playback_rate()) {
RCHECK(SetStringAttribute("maxPlayoutRate", RCHECK(SetStringAttribute(
base::IntToString(video_info.playback_rate()))); "maxPlayoutRate", absl::StrFormat("%d", video_info.playback_rate())));
// Since the trick play stream contains only key frames, there is no coding // Since the trick play stream contains only key frames, there is no coding
// dependency on the main stream. Simply set the codingDependency to false. // dependency on the main stream. Simply set the codingDependency to false.
// TODO(hmchen): propagate this attribute up to the AdaptationSet, since // TODO(hmchen): propagate this attribute up to the AdaptationSet, since
@ -416,7 +420,7 @@ bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info,
media_info.reference_time_scale())); media_info.reference_time_scale()));
if (use_segment_list && !use_single_segment_url_with_media) { if (use_segment_list && !use_single_segment_url_with_media) {
const int64_t duration_seconds = static_cast<int64_t>( const auto duration_seconds = static_cast<int64_t>(
floor(target_segment_duration * media_info.reference_time_scale())); floor(target_segment_duration * media_info.reference_time_scale()));
RCHECK(child.SetIntegerAttribute("duration", duration_seconds)); RCHECK(child.SetIntegerAttribute("duration", duration_seconds));
} }
@ -505,7 +509,7 @@ bool RepresentationXmlNode::AddLiveOnlyInfo(
if (IsTimelineConstantDuration(segment_infos, start_number)) { if (IsTimelineConstantDuration(segment_infos, start_number)) {
RCHECK(segment_template.SetIntegerAttribute( RCHECK(segment_template.SetIntegerAttribute(
"duration", segment_infos.front().duration)); "duration", segment_infos.front().duration));
if (FLAGS_dash_add_last_segment_number_when_needed) { if (absl::GetFlag(FLAGS_dash_add_last_segment_number_when_needed)) {
uint32_t last_segment_number = start_number - 1; uint32_t last_segment_number = start_number - 1;
for (const auto& segment_info_element : segment_infos) for (const auto& segment_info_element : segment_infos)
last_segment_number += segment_info_element.repeat + 1; last_segment_number += segment_info_element.repeat + 1;
@ -537,19 +541,18 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
const uint32_t ec3_channel_mpeg_value = codec_data.channel_mpeg_value(); const uint32_t ec3_channel_mpeg_value = codec_data.channel_mpeg_value();
const uint32_t NO_MAPPING = 0xFFFFFFFF; const uint32_t NO_MAPPING = 0xFFFFFFFF;
if (ec3_channel_mpeg_value == NO_MAPPING) { if (ec3_channel_mpeg_value == NO_MAPPING) {
// Convert EC3 channel map into string of hexadecimal digits. Spec: DASH-IF // Convert EC3 channel map into string of hexadecimal digits. Spec:
// Interoperability Points v3.0 9.2.1.2. // DASH-IF Interoperability Points v3.0 9.2.1.2.
const uint16_t ec3_channel_map =
base::HostToNet16(codec_data.channel_mask());
audio_channel_config_value = audio_channel_config_value =
base::HexEncode(&ec3_channel_map, sizeof(ec3_channel_map)); absl::StrFormat("%04X", codec_data.channel_mask());
audio_channel_config_scheme = audio_channel_config_scheme =
"tag:dolby.com,2014:dash:audio_channel_configuration:2011"; "tag:dolby.com,2014:dash:audio_channel_configuration:2011";
} else { } else {
// Calculate EC3 channel configuration descriptor value with MPEG scheme. // Calculate EC3 channel configuration descriptor value with MPEG scheme.
// Spec: ETSI TS 102 366 V1.4.1 Digital Audio Compression // Spec: ETSI TS 102 366 V1.4.1 Digital Audio Compression
// (AC-3, Enhanced AC-3) I.1.2. // (AC-3, Enhanced AC-3) I.1.2.
audio_channel_config_value = base::UintToString(ec3_channel_mpeg_value); audio_channel_config_value =
absl::StrFormat("%u", ec3_channel_mpeg_value);
audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration"; audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration";
} }
bool ret = AddDescriptor("AudioChannelConfiguration", bool ret = AddDescriptor("AudioChannelConfiguration",
@ -560,7 +563,7 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
// D.2.2. // D.2.2.
if (codec_data.ec3_joc_complexity() != 0) { if (codec_data.ec3_joc_complexity() != 0) {
std::string ec3_joc_complexity = std::string ec3_joc_complexity =
base::UintToString(codec_data.ec3_joc_complexity()); absl::StrFormat("%u", codec_data.ec3_joc_complexity());
ret &= AddDescriptor("SupplementalProperty", ret &= AddDescriptor("SupplementalProperty",
"tag:dolby.com,2018:dash:EC3_ExtensionType:2018", "tag:dolby.com,2018:dash:EC3_ExtensionType:2018",
"JOC"); "JOC");
@ -582,10 +585,10 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
// Calculate AC-4 channel mask. Spec: ETSI TS 103 190-2 V1.2.1 Digital // Calculate AC-4 channel mask. Spec: ETSI TS 103 190-2 V1.2.1 Digital
// Audio Compression (AC-4) Standard; Part 2: Immersive and personalized // Audio Compression (AC-4) Standard; Part 2: Immersive and personalized
// audio G.3.1. // audio G.3.1.
const uint32_t ac4_channel_mask = //
base::HostToNet32(codec_data.channel_mask() << 8); // this needs to print only 3 bytes of the 32-bit value
audio_channel_config_value = audio_channel_config_value =
base::HexEncode(&ac4_channel_mask, sizeof(ac4_channel_mask) - 1); absl::StrFormat("%06X", codec_data.channel_mask());
// Note that the channel config schemes for EC-3 and AC-4 are different. // Note that the channel config schemes for EC-3 and AC-4 are different.
// See https://github.com/Dash-Industry-Forum/DASH-IF-IOP/issues/268. // See https://github.com/Dash-Industry-Forum/DASH-IF-IOP/issues/268.
audio_channel_config_scheme = audio_channel_config_scheme =
@ -594,7 +597,8 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
// Calculate AC-4 channel configuration descriptor value with MPEG scheme. // Calculate AC-4 channel configuration descriptor value with MPEG scheme.
// Spec: ETSI TS 103 190-2 V1.2.1 Digital Audio Compression (AC-4) Standard; // Spec: ETSI TS 103 190-2 V1.2.1 Digital Audio Compression (AC-4) Standard;
// Part 2: Immersive and personalized audio G.3.2. // Part 2: Immersive and personalized audio G.3.2.
audio_channel_config_value = base::UintToString(ac4_channel_mpeg_value); audio_channel_config_value =
absl::StrFormat("%u", ac4_channel_mpeg_value);
audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration"; audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration";
} }
bool ret = AddDescriptor("AudioChannelConfiguration", bool ret = AddDescriptor("AudioChannelConfiguration",
@ -607,7 +611,8 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
} }
return ret; return ret;
} else { } else {
audio_channel_config_value = base::UintToString(audio_info.num_channels()); audio_channel_config_value =
absl::StrFormat("%u", audio_info.num_channels());
audio_channel_config_scheme = audio_channel_config_scheme =
"urn:mpeg:dash:23003:3:audio_channel_configuration:2011"; "urn:mpeg:dash:23003:3:audio_channel_configuration:2011";
} }

View File

@ -17,8 +17,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "packager/base/compiler_specific.h" #include "packager/macros.h"
#include "packager/base/macros.h"
#include "packager/mpd/base/content_protection_element.h" #include "packager/mpd/base/content_protection_element.h"
#include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/media_info.pb.h"
@ -54,32 +53,33 @@ class XmlNode {
/// Add a child element to this element. /// Add a child element to this element.
/// @param child is an XmlNode to add as a child for this element. /// @param child is an XmlNode to add as a child for this element.
/// @return true on success, false otherwise. /// @return true on success, false otherwise.
bool AddChild(XmlNode child) WARN_UNUSED_RESULT; [[nodiscard]] bool AddChild(XmlNode child);
/// Adds Elements to this node using the Element struct. /// Adds Elements to this node using the Element struct.
bool AddElements(const std::vector<Element>& elements) WARN_UNUSED_RESULT; [[nodiscard]] bool AddElements(const std::vector<Element>& elements);
/// Set a string attribute. /// Set a string attribute.
/// @param attribute_name The name (lhs) of the attribute. /// @param attribute_name The name (lhs) of the attribute.
/// @param attribute The value (rhs) of the attribute. /// @param attribute The value (rhs) of the attribute.
bool SetStringAttribute(const std::string& attribute_name, [[nodiscard]] bool SetStringAttribute(const std::string& attribute_name,
const std::string& attribute) WARN_UNUSED_RESULT; const std::string& attribute);
/// Sets an integer attribute. /// Sets an integer attribute.
/// @param attribute_name The name (lhs) of the attribute. /// @param attribute_name The name (lhs) of the attribute.
/// @param number The value (rhs) of the attribute. /// @param number The value (rhs) of the attribute.
bool SetIntegerAttribute(const std::string& attribute_name, [[nodiscard]] bool SetIntegerAttribute(const std::string& attribute_name,
uint64_t number) WARN_UNUSED_RESULT; uint64_t number);
/// Set a floating point number attribute. /// Set a floating point number attribute.
/// @param attribute_name is the name of the attribute to set. /// @param attribute_name is the name of the attribute to set.
/// @param number is the value (rhs) of the attribute. /// @param number is the value (rhs) of the attribute.
bool SetFloatingPointAttribute(const std::string& attribute_name, [[nodiscard]] bool SetFloatingPointAttribute(
double number) WARN_UNUSED_RESULT; const std::string& attribute_name,
double number);
/// Sets 'id=@a id' attribute. /// Sets 'id=@a id' attribute.
/// @param id is the ID for this element. /// @param id is the ID for this element.
bool SetId(uint32_t id) WARN_UNUSED_RESULT; [[nodiscard]] bool SetId(uint32_t id);
/// Similar to SetContent, but appends to the end of existing content. /// Similar to SetContent, but appends to the end of existing content.
void AddContent(const std::string& content); void AddContent(const std::string& content);
@ -125,19 +125,18 @@ class XmlNode {
class RepresentationBaseXmlNode : public XmlNode { class RepresentationBaseXmlNode : public XmlNode {
public: public:
~RepresentationBaseXmlNode() override; ~RepresentationBaseXmlNode() override;
bool AddContentProtectionElements( [[nodiscard]] bool AddContentProtectionElements(
const std::list<ContentProtectionElement>& content_protection_elements) const std::list<ContentProtectionElement>& content_protection_elements);
WARN_UNUSED_RESULT;
/// @param scheme_id_uri is content of the schemeIdUri attribute. /// @param scheme_id_uri is content of the schemeIdUri attribute.
/// @param value is the content of value attribute. /// @param value is the content of value attribute.
bool AddSupplementalProperty(const std::string& scheme_id_uri, [[nodiscard]] bool AddSupplementalProperty(const std::string& scheme_id_uri,
const std::string& value) WARN_UNUSED_RESULT; const std::string& value);
/// @param scheme_id_uri is content of the schemeIdUri attribute. /// @param scheme_id_uri is content of the schemeIdUri attribute.
/// @param value is the content of value attribute. /// @param value is the content of value attribute.
bool AddEssentialProperty(const std::string& scheme_id_uri, [[nodiscard]] bool AddEssentialProperty(const std::string& scheme_id_uri,
const std::string& value) WARN_UNUSED_RESULT; const std::string& value);
protected: protected:
explicit RepresentationBaseXmlNode(const std::string& name); explicit RepresentationBaseXmlNode(const std::string& name);
@ -146,14 +145,13 @@ class RepresentationBaseXmlNode : public XmlNode {
/// @param descriptor_name is the name of the descriptor. /// @param descriptor_name is the name of the descriptor.
/// @param scheme_id_uri is content of the schemeIdUri attribute. /// @param scheme_id_uri is content of the schemeIdUri attribute.
/// @param value is the content of value attribute. /// @param value is the content of value attribute.
bool AddDescriptor(const std::string& descriptor_name, [[nodiscard]] bool AddDescriptor(const std::string& descriptor_name,
const std::string& scheme_id_uri, const std::string& scheme_id_uri,
const std::string& value) WARN_UNUSED_RESULT; const std::string& value);
private: private:
bool AddContentProtectionElement( [[nodiscard]] bool AddContentProtectionElement(
const ContentProtectionElement& content_protection_element) const ContentProtectionElement& content_protection_element);
WARN_UNUSED_RESULT;
DISALLOW_COPY_AND_ASSIGN(RepresentationBaseXmlNode); DISALLOW_COPY_AND_ASSIGN(RepresentationBaseXmlNode);
}; };
@ -166,13 +164,13 @@ class AdaptationSetXmlNode : public RepresentationBaseXmlNode {
/// @param scheme_id_uri is content of the schemeIdUri attribute. /// @param scheme_id_uri is content of the schemeIdUri attribute.
/// @param value is the content of value attribute. /// @param value is the content of value attribute.
bool AddAccessibilityElement(const std::string& scheme_id_uri, [[nodiscard]] bool AddAccessibilityElement(const std::string& scheme_id_uri,
const std::string& value) WARN_UNUSED_RESULT; const std::string& value);
/// @param scheme_id_uri is content of the schemeIdUri attribute. /// @param scheme_id_uri is content of the schemeIdUri attribute.
/// @param value is the content of value attribute. /// @param value is the content of value attribute.
bool AddRoleElement(const std::string& scheme_id_uri, [[nodiscard]] bool AddRoleElement(const std::string& scheme_id_uri,
const std::string& value) WARN_UNUSED_RESULT; const std::string& value);
private: private:
DISALLOW_COPY_AND_ASSIGN(AdaptationSetXmlNode); DISALLOW_COPY_AND_ASSIGN(AdaptationSetXmlNode);
@ -191,16 +189,16 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode {
/// @param set_frame_rate is a flag for setting the frameRate attribute. /// @param set_frame_rate is a flag for setting the frameRate attribute.
/// @return true if successfully set attributes and children elements (if /// @return true if successfully set attributes and children elements (if
/// applicable), false otherwise. /// applicable), false otherwise.
bool AddVideoInfo(const MediaInfo::VideoInfo& video_info, [[nodiscard]] bool AddVideoInfo(const MediaInfo::VideoInfo& video_info,
bool set_width, bool set_width,
bool set_height, bool set_height,
bool set_frame_rate) WARN_UNUSED_RESULT; bool set_frame_rate);
/// Adds audio metadata to the MPD. /// Adds audio metadata to the MPD.
/// @param audio_info constains the AudioInfos for a Representation. /// @param audio_info constains the AudioInfos for a Representation.
/// @return true if successfully set attributes and children elements (if /// @return true if successfully set attributes and children elements (if
/// applicable), false otherwise. /// applicable), false otherwise.
bool AddAudioInfo(const MediaInfo::AudioInfo& audio_info) WARN_UNUSED_RESULT; [[nodiscard]] bool AddAudioInfo(const MediaInfo::AudioInfo& audio_info);
/// Adds fields that are specific to VOD. This ignores @a media_info fields /// Adds fields that are specific to VOD. This ignores @a media_info fields
/// for Live. /// for Live.
@ -211,26 +209,27 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode {
// duration of media segments. This is only used when use_segment_list // duration of media segments. This is only used when use_segment_list
// is true. // is true.
/// @return true on success, false otherwise. /// @return true on success, false otherwise.
bool AddVODOnlyInfo(const MediaInfo& media_info, [[nodiscard]] bool AddVODOnlyInfo(const MediaInfo& media_info,
bool use_segment_list, bool use_segment_list,
double target_segment_duration) WARN_UNUSED_RESULT; double target_segment_duration);
/// @param segment_infos is a set of SegmentInfos. This method assumes that /// @param segment_infos is a set of SegmentInfos. This method assumes that
/// SegmentInfos are sorted by its start time. /// SegmentInfos are sorted by its start time.
bool AddLiveOnlyInfo(const MediaInfo& media_info, [[nodiscard]] bool AddLiveOnlyInfo(
const MediaInfo& media_info,
const std::list<SegmentInfo>& segment_infos, const std::list<SegmentInfo>& segment_infos,
uint32_t start_number, uint32_t start_number,
bool low_latency_dash_mode) WARN_UNUSED_RESULT; bool low_latency_dash_mode);
private: private:
// Add AudioChannelConfiguration element. Note that it is a required element // Add AudioChannelConfiguration element. Note that it is a required element
// for audio Representations. // for audio Representations.
bool AddAudioChannelInfo(const MediaInfo::AudioInfo& audio_info) [[nodiscard]] bool AddAudioChannelInfo(
WARN_UNUSED_RESULT; const MediaInfo::AudioInfo& audio_info);
// Add audioSamplingRate attribute to this element, if present. // Add audioSamplingRate attribute to this element, if present.
bool AddAudioSamplingRateInfo(const MediaInfo::AudioInfo& audio_info) [[nodiscard]] bool AddAudioSamplingRateInfo(
WARN_UNUSED_RESULT; const MediaInfo::AudioInfo& audio_info);
DISALLOW_COPY_AND_ASSIGN(RepresentationXmlNode); DISALLOW_COPY_AND_ASSIGN(RepresentationXmlNode);
}; };

View File

@ -4,23 +4,23 @@
// 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 <gflags/gflags.h> #include <absl/flags/declare.h>
#include <absl/flags/flag.h>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <libxml/tree.h> #include <libxml/tree.h>
#include <list> #include <list>
#include "packager/base/logging.h" #include <glog/logging.h>
#include "packager/base/strings/string_util.h" #include "packager/flag_saver.h"
#include "packager/mpd/base/segment_info.h" #include "packager/mpd/base/segment_info.h"
#include "packager/mpd/base/xml/xml_node.h" #include "packager/mpd/base/xml/xml_node.h"
#include "packager/mpd/test/mpd_builder_test_helper.h" #include "packager/mpd/test/mpd_builder_test_helper.h"
#include "packager/mpd/test/xml_compare.h" #include "packager/mpd/test/xml_compare.h"
DECLARE_bool(segment_template_constant_duration); ABSL_DECLARE_FLAG(bool, segment_template_constant_duration);
DECLARE_bool(dash_add_last_segment_number_when_needed); ABSL_DECLARE_FLAG(bool, dash_add_last_segment_number_when_needed);
using ::testing::ElementsAre; using ::testing::ElementsAre;
@ -352,15 +352,20 @@ TEST(XmlNodeTest, AddAC4AudioInfoMPEGSchemeIMS) {
} }
class LiveSegmentTimelineTest : public ::testing::Test { class LiveSegmentTimelineTest : public ::testing::Test {
public:
LiveSegmentTimelineTest()
: saver(&FLAGS_segment_template_constant_duration) {}
protected: protected:
void SetUp() override { void SetUp() override {
FLAGS_segment_template_constant_duration = true; absl::SetFlag(&FLAGS_segment_template_constant_duration, true);
media_info_.set_segment_template_url("$Number$.m4s"); media_info_.set_segment_template_url("$Number$.m4s");
} }
void TearDown() override { FLAGS_segment_template_constant_duration = false; }
MediaInfo media_info_; MediaInfo media_info_;
private:
FlagSaver<bool> saver;
}; };
TEST_F(LiveSegmentTimelineTest, OneSegmentInfo) { TEST_F(LiveSegmentTimelineTest, OneSegmentInfo) {
@ -536,7 +541,9 @@ TEST_F(LiveSegmentTimelineTest, LastSegmentNumberSupplementalProperty) {
{kStartTime, kDuration, kRepeat}, {kStartTime, kDuration, kRepeat},
}; };
RepresentationXmlNode representation; RepresentationXmlNode representation;
FLAGS_dash_add_last_segment_number_when_needed = true; FlagSaver<bool> segment_number_saver(
&FLAGS_dash_add_last_segment_number_when_needed);
absl::SetFlag(&FLAGS_dash_add_last_segment_number_when_needed, true);
ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos,
kStartNumber, kIsLowLatency)); kStartNumber, kIsLowLatency));
@ -549,7 +556,6 @@ TEST_F(LiveSegmentTimelineTest, LastSegmentNumberSupplementalProperty) {
" <SegmentTemplate media=\"$Number$.m4s\" " " <SegmentTemplate media=\"$Number$.m4s\" "
" startNumber=\"1\" duration=\"100\"/>" " startNumber=\"1\" duration=\"100\"/>"
"</Representation>")); "</Representation>"));
FLAGS_dash_add_last_segment_number_when_needed = false;
} }
// Creating a separate Test Suite for RepresentationXmlNode::AddVODOnlyInfo // Creating a separate Test Suite for RepresentationXmlNode::AddVODOnlyInfo
@ -751,7 +757,7 @@ TEST_F(LowLatencySegmentTest, LowLatencySegmentTemplate) {
representation, representation,
XmlNodeEqual("<Representation>" XmlNodeEqual("<Representation>"
" <SegmentTemplate timescale=\"90000\" duration=\"450000\" " " <SegmentTemplate timescale=\"90000\" duration=\"450000\" "
" availabilityTimeOffset=\"4.9750987314\" " " availabilityTimeOffset=\"4.9751\" "
" availabilityTimeComplete=\"false\" " " availabilityTimeComplete=\"false\" "
" initialization=\"init.m4s\" " " initialization=\"init.m4s\" "
" media=\"$Number$.m4s\" " " media=\"$Number$.m4s\" "

View File

@ -1,141 +0,0 @@
# Copyright 2014 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
#
# GYP file for any MPD generation targets.
{
'variables': {
'shaka_code': 1,
},
'targets': [
{
'target_name': 'media_info_proto',
'type': 'static_library',
'sources': [
'base/media_info.proto',
],
'variables': {
'proto_in_dir': 'base',
'proto_out_dir': 'packager/mpd/base',
},
'includes': ['../protoc.gypi'],
},
{
# Used by both MPD and HLS. It should really be moved to a common
# directory shared by MPD and HLS.
'target_name': 'manifest_base',
'type': 'static_library',
'sources': [
'base/bandwidth_estimator.cc',
'base/bandwidth_estimator.h',
],
'dependencies': [
'../base/base.gyp:base',
],
},
{
'target_name': 'mpd_builder',
'type': 'static_library',
'sources': [
'base/adaptation_set.cc',
'base/adaptation_set.h',
'base/content_protection_element.cc',
'base/content_protection_element.h',
'base/mpd_builder.cc',
'base/mpd_builder.h',
'base/mpd_notifier_util.cc',
'base/mpd_notifier_util.h',
'base/mpd_notifier.h',
'base/mpd_options.h',
'base/mpd_utils.cc',
'base/mpd_utils.h',
'base/period.cc',
'base/period.h',
'base/representation.cc',
'base/representation.h',
'base/segment_info.h',
'base/simple_mpd_notifier.cc',
'base/simple_mpd_notifier.h',
'base/xml/scoped_xml_ptr.h',
'base/xml/xml_node.cc',
'base/xml/xml_node.h',
'public/mpd_params.h',
],
'dependencies': [
'../base/base.gyp:base',
'../file/file.gyp:file',
'../media/base/media_base.gyp:media_base',
'../third_party/gflags/gflags.gyp:gflags',
'../third_party/libxml/libxml.gyp:libxml',
'../version/version.gyp:version',
'manifest_base',
'media_info_proto',
],
'export_dependent_settings': [
'../third_party/libxml/libxml.gyp:libxml',
'media_info_proto',
],
},
{
'target_name': 'mpd_mocks',
'type': '<(component)',
'sources': [
'base/mock_mpd_builder.cc',
'base/mock_mpd_builder.h',
'base/mock_mpd_notifier.cc',
'base/mock_mpd_notifier.h',
],
'dependencies': [
'../testing/gmock.gyp:gmock',
'mpd_builder',
],
},
{
'target_name': 'mpd_unittest',
'type': '<(gtest_target_type)',
'sources': [
'base/adaptation_set_unittest.cc',
'base/bandwidth_estimator_unittest.cc',
'base/mpd_builder_unittest.cc',
'base/mpd_utils_unittest.cc',
'base/period_unittest.cc',
'base/representation_unittest.cc',
'base/simple_mpd_notifier_unittest.cc',
'base/xml/xml_node_unittest.cc',
'test/mpd_builder_test_helper.cc',
'test/mpd_builder_test_helper.h',
'test/xml_compare.cc',
'test/xml_compare.h',
'util/mpd_writer_unittest.cc',
],
'dependencies': [
'../base/base.gyp:base',
'../file/file.gyp:file',
'../media/test/media_test.gyp:run_tests_with_atexit_manager',
'../testing/gmock.gyp:gmock',
'../testing/gtest.gyp:gtest',
'../third_party/gflags/gflags.gyp:gflags',
'mpd_builder',
'mpd_mocks',
'mpd_util',
],
},
{
'target_name': 'mpd_util',
'type': '<(component)',
'sources': [
'util/mpd_writer.cc',
'util/mpd_writer.h',
],
'dependencies': [
'../file/file.gyp:file',
'../third_party/gflags/gflags.gyp:gflags',
'mpd_builder',
'mpd_mocks',
],
},
],
}

View File

@ -6,47 +6,26 @@
#include "packager/mpd/test/mpd_builder_test_helper.h" #include "packager/mpd/test/mpd_builder_test_helper.h"
#include <glog/logging.h>
#include <google/protobuf/text_format.h> #include <google/protobuf/text_format.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <filesystem>
#include "packager/base/files/file_util.h" #include "packager/media/test/test_data_util.h"
#include "packager/base/path_service.h"
#include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/xml/scoped_xml_ptr.h" #include "packager/mpd/base/xml/scoped_xml_ptr.h"
#include "packager/mpd/test/xml_compare.h" #include "packager/mpd/test/xml_compare.h"
namespace shaka { namespace shaka {
base::FilePath GetTestDataFilePath(const std::string& file_name) { std::filesystem::path GetTestDataFilePath(const std::string& name) {
base::FilePath file_path; auto data_dir = std::filesystem::path(TEST_DATA_DIR);
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); return data_dir / name;
file_path = file_path.Append(FILE_PATH_LITERAL("packager"))
.Append(FILE_PATH_LITERAL("mpd"))
.Append(FILE_PATH_LITERAL("test"))
.Append(FILE_PATH_LITERAL("data"))
.AppendASCII(file_name);
return file_path;
} }
base::FilePath GetSchemaPath() { std::filesystem::path GetSchemaPath() {
base::FilePath file_path; auto schema_dir = std::filesystem::path(TEST_SCHEMA_DIR);
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); return schema_dir / "DASH-MPD.xsd";
file_path = file_path.Append(FILE_PATH_LITERAL("packager"))
.Append(FILE_PATH_LITERAL("mpd"))
.Append(FILE_PATH_LITERAL("test"))
.Append(FILE_PATH_LITERAL("schema"))
.Append(FILE_PATH_LITERAL("DASH-MPD.xsd"));
return file_path;
}
std::string GetPathContent(const base::FilePath& file_path) {
std::string content;
bool file_read_to_string = base::ReadFileToString(file_path, &content);
DCHECK(file_read_to_string) << file_path.value();
return content;
} }
MediaInfo ConvertToMediaInfo(const std::string& media_info_string) { MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
@ -57,8 +36,8 @@ MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
} }
MediaInfo GetTestMediaInfo(const std::string& media_info_file_name) { MediaInfo GetTestMediaInfo(const std::string& media_info_file_name) {
return ConvertToMediaInfo( std::filesystem::path test_path = GetTestDataFilePath(media_info_file_name);
GetPathContent(GetTestDataFilePath(media_info_file_name))); return ConvertToMediaInfo(GetPathContent(test_path));
} }
bool ValidateMpdSchema(const std::string& mpd) { bool ValidateMpdSchema(const std::string& mpd) {
@ -69,18 +48,15 @@ bool ValidateMpdSchema(const std::string& mpd) {
return false; return false;
} }
base::FilePath schema_path = GetSchemaPath(); std::filesystem::path schema_path = GetSchemaPath();
std::string schema_str = GetPathContent(schema_path); std::string schema_str = GetPathContent(schema_path);
// First, I need to load the schema as a xmlDoc so that I can pass the path of // First, I need to load the schema as a xmlDoc so that I can pass the path of
// the DASH-MPD.xsd. Then it can resolve the relative path included from the // the DASH-MPD.xsd. Then it can resolve the relative path included from the
// XSD when creating xmlSchemaParserCtxt. // XSD when creating xmlSchemaParserCtxt.
xml::scoped_xml_ptr<xmlDoc> schema_as_doc( xml::scoped_xml_ptr<xmlDoc> schema_as_doc(
xmlReadMemory(schema_str.data(), xmlReadMemory(schema_str.data(), schema_str.size(),
schema_str.size(), schema_path.string().c_str(), NULL, 0));
schema_path.AsUTF8Unsafe().c_str(),
NULL,
0));
DCHECK(schema_as_doc); DCHECK(schema_as_doc);
xml::scoped_xml_ptr<xmlSchemaParserCtxt> xml::scoped_xml_ptr<xmlSchemaParserCtxt>
schema_parser_ctxt(xmlSchemaNewDocParserCtxt(schema_as_doc.get())); schema_parser_ctxt(xmlSchemaNewDocParserCtxt(schema_as_doc.get()));
@ -102,9 +78,11 @@ bool ValidateMpdSchema(const std::string& mpd) {
void ExpectMpdToEqualExpectedOutputFile( void ExpectMpdToEqualExpectedOutputFile(
const std::string& mpd_string, const std::string& mpd_string,
const std::string& expected_output_file) { const std::string& expected_output_file) {
std::string expected_mpd; std::filesystem::path expected_output_file_path =
ASSERT_TRUE(base::ReadFileToString(GetTestDataFilePath(expected_output_file), GetTestDataFilePath(expected_output_file);
&expected_mpd)) std::string expected_mpd = GetPathContent(expected_output_file_path);
ASSERT_TRUE(!expected_mpd.empty())
<< "Failed to read: " << expected_output_file; << "Failed to read: " << expected_output_file;
// Adding extra << here to get a formatted output. // Adding extra << here to get a formatted output.

View File

@ -7,9 +7,9 @@
#ifndef MPD_TEST_MPD_BUILDER_TEST_HELPER_H_ #ifndef MPD_TEST_MPD_BUILDER_TEST_HELPER_H_
#define MPD_TEST_MPD_BUILDER_TEST_HELPER_H_ #define MPD_TEST_MPD_BUILDER_TEST_HELPER_H_
#include <filesystem>
#include <string> #include <string>
#include "packager/base/files/file_path.h"
#include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/media_info.pb.h"
namespace shaka { namespace shaka {
@ -39,13 +39,10 @@ const char kFileNameExpectedMpdOutputAudio1AndVideo1[] =
// Returns the path to test data with |file_name|. Use constants above to get // Returns the path to test data with |file_name|. Use constants above to get
// path to the test files. // path to the test files.
base::FilePath GetTestDataFilePath(const std::string& file_name); std::filesystem::path GetTestDataFilePath(const std::string& file_name);
// Get path to DASH MPD schema. // Get path to DASH MPD schema.
base::FilePath GetSchemaPath(); std::filesystem::path GetSchemaPath();
// Get the content of |file_path|. Returns empty string on error.
std::string GetPathContent(const base::FilePath& file_path);
// Convert |media_info_string| to MediaInfo. // Convert |media_info_string| to MediaInfo.
MediaInfo ConvertToMediaInfo(const std::string& media_info_string); MediaInfo ConvertToMediaInfo(const std::string& media_info_string);

View File

@ -1,16 +1,14 @@
#include "packager/mpd/test/xml_compare.h" #include "packager/mpd/test/xml_compare.h"
#include <absl/strings/strip.h>
#include <glog/logging.h>
#include <libxml/parser.h> #include <libxml/parser.h>
#include <libxml/tree.h> #include <libxml/tree.h>
#include <algorithm> #include <algorithm>
#include <map> #include <map>
#include <string> #include <string>
#include <utility> #include <utility>
#include "packager/base/logging.h"
#include "packager/base/strings/string_util.h"
namespace shaka { namespace shaka {
namespace { namespace {
@ -80,10 +78,17 @@ bool CompareContents(xmlNodePtr node1, xmlNodePtr node2) {
reinterpret_cast<const char*>(node1_content_ptr.get()); reinterpret_cast<const char*>(node1_content_ptr.get());
std::string node2_content = std::string node2_content =
reinterpret_cast<const char*>(node2_content_ptr.get()); reinterpret_cast<const char*>(node2_content_ptr.get());
base::ReplaceChars(node1_content, "\n", "", &node1_content);
base::TrimString(node1_content, " ", &node1_content); node1_content.erase(
base::ReplaceChars(node2_content, "\n", "", &node2_content); std::remove(node1_content.begin(), node1_content.end(), '\n'),
base::TrimString(node2_content, " ", &node2_content); node1_content.end());
node2_content.erase(
std::remove(node2_content.begin(), node2_content.end(), '\n'),
node2_content.end());
node1_content = absl::StripAsciiWhitespace(node1_content);
node2_content = absl::StripAsciiWhitespace(node2_content);
DVLOG(2) << "Comparing contents of " DVLOG(2) << "Comparing contents of "
<< reinterpret_cast<const char*>(node1->name) << "\n" << reinterpret_cast<const char*>(node1->name) << "\n"
<< "First node's content:\n" << node1_content << "\n" << "First node's content:\n" << node1_content << "\n"
@ -157,7 +162,7 @@ bool XmlEqual(const std::string& xml1, const std::string& xml2) {
} }
bool XmlEqual(const std::string& xml1, bool XmlEqual(const std::string& xml1,
const base::Optional<xml::XmlNode>& xml2) { const std::optional<xml::XmlNode>& xml2) {
return xml2 && XmlEqual(xml1, *xml2); return xml2 && XmlEqual(xml1, *xml2);
} }
@ -173,7 +178,7 @@ bool XmlEqual(const std::string& xml1, const xml::XmlNode& xml2) {
return CompareNodes(xml1_root_element, xml2.GetRawPtr()); return CompareNodes(xml1_root_element, xml2.GetRawPtr());
} }
std::string XmlNodeToString(const base::Optional<xml::XmlNode>& xml_node) { std::string XmlNodeToString(const std::optional<xml::XmlNode>& xml_node) {
return xml_node ? XmlNodeToString(*xml_node) : "$ERROR$"; return xml_node ? XmlNodeToString(*xml_node) : "$ERROR$";
} }

View File

@ -6,7 +6,7 @@
#include <string> #include <string>
#include "packager/base/optional.h" #include <optional>
#include "packager/mpd/base/xml/scoped_xml_ptr.h" #include "packager/mpd/base/xml/scoped_xml_ptr.h"
#include "packager/mpd/base/xml/xml_node.h" #include "packager/mpd/base/xml/xml_node.h"
@ -22,12 +22,11 @@ namespace shaka {
/// @return true if @a xml1 and @a xml2 are equivalent, false otherwise. /// @return true if @a xml1 and @a xml2 are equivalent, false otherwise.
bool XmlEqual(const std::string& xml1, const std::string& xml2); bool XmlEqual(const std::string& xml1, const std::string& xml2);
bool XmlEqual(const std::string& xml1, const xml::XmlNode& xml2); bool XmlEqual(const std::string& xml1, const xml::XmlNode& xml2);
bool XmlEqual(const std::string& xml1, bool XmlEqual(const std::string& xml1, const std::optional<xml::XmlNode>& xml2);
const base::Optional<xml::XmlNode>& xml2);
/// Get string representation of the xml node. /// Get string representation of the xml node.
std::string XmlNodeToString(const xml::XmlNode& xml_node); std::string XmlNodeToString(const xml::XmlNode& xml_node);
std::string XmlNodeToString(const base::Optional<xml::XmlNode>& xml_node); std::string XmlNodeToString(const std::optional<xml::XmlNode>& xml_node);
/// Match an xmlNodePtr with an xml in string representation. /// Match an xmlNodePtr with an xml in string representation.
MATCHER_P(XmlNodeEqual, MATCHER_P(XmlNodeEqual,

View File

@ -6,19 +6,19 @@
#include "packager/mpd/util/mpd_writer.h" #include "packager/mpd/util/mpd_writer.h"
#include <gflags/gflags.h> #include <absl/flags/flag.h>
#include <glog/logging.h>
#include <google/protobuf/text_format.h> #include <google/protobuf/text_format.h>
#include <stdint.h> #include <stdint.h>
#include "packager/base/files/file_path.h"
#include "packager/base/files/file_util.h"
#include "packager/file/file.h" #include "packager/file/file.h"
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/mpd_notifier.h" #include "packager/mpd/base/mpd_notifier.h"
#include "packager/mpd/base/mpd_utils.h" #include "packager/mpd/base/mpd_utils.h"
#include "packager/mpd/base/simple_mpd_notifier.h" #include "packager/mpd/base/simple_mpd_notifier.h"
DEFINE_bool(generate_dash_if_iop_compliant_mpd, ABSL_FLAG(bool,
generate_dash_if_iop_compliant_mpd,
true, true,
"Try to generate DASH-IF IOP compliant MPD. This is best effort " "Try to generate DASH-IF IOP compliant MPD. This is best effort "
"and does not guarantee compliance."); "and does not guarantee compliance.");
@ -71,7 +71,7 @@ bool MpdWriter::WriteMpdToFile(const char* file_name) {
mpd_options.mpd_params.base_urls = base_urls_; mpd_options.mpd_params.base_urls = base_urls_;
mpd_options.mpd_params.mpd_output = file_name; mpd_options.mpd_params.mpd_output = file_name;
mpd_options.mpd_params.generate_dash_if_iop_compliant_mpd = mpd_options.mpd_params.generate_dash_if_iop_compliant_mpd =
FLAGS_generate_dash_if_iop_compliant_mpd; absl::GetFlag(FLAGS_generate_dash_if_iop_compliant_mpd);
std::unique_ptr<MpdNotifier> notifier = std::unique_ptr<MpdNotifier> notifier =
notifier_factory_->Create(mpd_options); notifier_factory_->Create(mpd_options);
if (!notifier->Init()) { if (!notifier->Init()) {

View File

@ -14,7 +14,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "packager/base/macros.h" #include "packager/macros.h"
#include "packager/mpd/base/mpd_notifier.h" #include "packager/mpd/base/mpd_notifier.h"
#include "packager/mpd/base/mpd_options.h" #include "packager/mpd/base/mpd_options.h"

View File

@ -6,9 +6,9 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <filesystem>
#include "packager/base/files/file_util.h" #include "packager/file/file_test_util.h"
#include "packager/base/path_service.h"
#include "packager/mpd/base/mock_mpd_notifier.h" #include "packager/mpd/base/mock_mpd_notifier.h"
#include "packager/mpd/base/mpd_options.h" #include "packager/mpd/base/mpd_options.h"
#include "packager/mpd/test/mpd_builder_test_helper.h" #include "packager/mpd/test/mpd_builder_test_helper.h"
@ -43,7 +43,7 @@ class TestMpdNotifierFactory : public MpdNotifierFactory {
.Times(2) .Times(2)
.WillRepeatedly(Return(true)); .WillRepeatedly(Return(true));
EXPECT_CALL(*mock_notifier, Flush()).WillOnce(Return(true)); EXPECT_CALL(*mock_notifier, Flush()).WillOnce(Return(true));
return std::move(mock_notifier); return mock_notifier;
} }
void SetExpectedBaseUrls(const std::vector<std::string>& base_urls) { void SetExpectedBaseUrls(const std::vector<std::string>& base_urls) {
@ -77,25 +77,24 @@ TEST_F(MpdWriterTest, WriteMpdToFile) {
const char kBaseUrl1[] = "http://cdn1.mydomain.com/"; const char kBaseUrl1[] = "http://cdn1.mydomain.com/";
const char kBaseUrl2[] = "http://cdn2.mydomain.com/"; const char kBaseUrl2[] = "http://cdn2.mydomain.com/";
std::vector<std::string> base_urls_; std::vector<std::string> base_urls_;
base_urls_.push_back(kBaseUrl1); base_urls_.emplace_back(kBaseUrl1);
base_urls_.push_back(kBaseUrl2); base_urls_.emplace_back(kBaseUrl2);
notifier_factory_->SetExpectedBaseUrls(base_urls_); notifier_factory_->SetExpectedBaseUrls(base_urls_);
base::FilePath media_info_file1 = std::filesystem::path media_info_file1 =
GetTestDataFilePath(kFileNameVideoMediaInfo1); GetTestDataFilePath(kFileNameVideoMediaInfo1);
base::FilePath media_info_file2 = std::filesystem::path media_info_file2 =
GetTestDataFilePath(kFileNameVideoMediaInfo2); GetTestDataFilePath(kFileNameVideoMediaInfo2);
SetMpdNotifierFactoryForTest(); SetMpdNotifierFactoryForTest();
EXPECT_TRUE(mpd_writer_.AddFile(media_info_file1.AsUTF8Unsafe())); EXPECT_TRUE(mpd_writer_.AddFile(media_info_file1.string()));
EXPECT_TRUE(mpd_writer_.AddFile(media_info_file2.AsUTF8Unsafe())); EXPECT_TRUE(mpd_writer_.AddFile(media_info_file2.string()));
mpd_writer_.AddBaseUrl(kBaseUrl1); mpd_writer_.AddBaseUrl(kBaseUrl1);
mpd_writer_.AddBaseUrl(kBaseUrl2); mpd_writer_.AddBaseUrl(kBaseUrl2);
base::FilePath mpd_file_path; auto temp = new TempFile();
ASSERT_TRUE(base::CreateTemporaryFile(&mpd_file_path)); EXPECT_TRUE(mpd_writer_.WriteMpdToFile(temp->path().c_str()));
EXPECT_TRUE(mpd_writer_.WriteMpdToFile(mpd_file_path.AsUTF8Unsafe().c_str()));
} }
} // namespace shaka } // namespace shaka

View File

@ -4,6 +4,16 @@
# 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
add_library(utils_clock STATIC
clock.h
clock.cc)
add_library(utils_test_clock STATIC
test_clock.cc
test_clock.h)
target_link_libraries(utils_test_clock utils_clock absl::strings)
add_library(hex_parser STATIC add_library(hex_parser STATIC
hex_parser.cc) hex_parser.cc)

9
packager/utils/clock.cc Normal file
View File

@ -0,0 +1,9 @@
#include "packager/utils/clock.h"
namespace shaka {
Clock::time_point Clock::now() noexcept {
return std::chrono::system_clock::now();
}
} // namespace shaka

25
packager/utils/clock.h Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2023 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
#ifndef SHAKA_PACKAGER_CLOCK_H
#define SHAKA_PACKAGER_CLOCK_H
#include <chrono>
namespace shaka {
class Clock {
public:
using time_point = std::chrono::system_clock::time_point;
virtual ~Clock() = default;
virtual time_point now() noexcept;
};
} // namespace shaka
#endif // SHAKA_PACKAGER_CLOCK_H

View File

@ -0,0 +1,41 @@
// Copyright 2023 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
#include "packager/utils/test_clock.h"
#include <absl/strings/str_split.h>
#include <absl/strings/string_view.h>
#include <ctime>
#include <iostream>
#include <string>
std::tm parseISO8601(const std::string& date_string) {
std::tm tm = {};
std::vector<absl::string_view> date_time_parts =
absl::StrSplit(date_string, 'T');
if (date_time_parts.size() == 2) {
std::vector<absl::string_view> date_parts =
absl::StrSplit(date_time_parts[0], '-');
std::vector<absl::string_view> time_parts =
absl::StrSplit(date_time_parts[1], ':');
if (date_parts.size() == 3 && time_parts.size() == 3) {
tm.tm_year = std::stoi(std::string(date_parts[0])) - 1900;
tm.tm_mon = std::stoi(std::string(date_parts[1])) - 1;
tm.tm_mday = std::stoi(std::string(date_parts[2]));
tm.tm_hour = std::stoi(std::string(time_parts[0]));
tm.tm_min = std::stoi(std::string(time_parts[1]));
tm.tm_sec = std::stoi(std::string(time_parts[2]));
}
}
return tm;
}
shaka::TestClock::TestClock(std::string utc_time_8601) {
std::tm tm = parseISO8601(utc_time_8601);
std::time_t utc_time_t = std::mktime(&tm);
std::time_t offset = utc_time_t - std::mktime(std::gmtime(&utc_time_t));
mock_time_ = std::chrono::system_clock::from_time_t(utc_time_t + offset);
}

View File

@ -0,0 +1,27 @@
// Copyright 2023 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
#ifndef SHAKA_PACKAGER_TEST_CLOCK_H
#define SHAKA_PACKAGER_TEST_CLOCK_H
#include <chrono>
#include <string>
#include "packager/utils/clock.h"
namespace shaka {
class TestClock : public Clock {
public:
explicit TestClock(std::string utc_time_8601);
time_point now() noexcept override { return mock_time_; }
private:
time_point mock_time_;
};
} // namespace shaka
#endif