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
// 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

View File

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

View File

@ -5,3 +5,111 @@
# https://developers.google.com/open-source/licenses/bsd
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 <absl/strings/numbers.h>
#include <glog/logging.h>
#include <cmath>
#include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h"
#include "absl/strings/str_format.h"
#include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/mpd_options.h"
#include "packager/mpd/base/mpd_utils.h"
@ -30,8 +31,8 @@ AdaptationSet::Role MediaInfoTextTypeToRole(
case MediaInfo::TextInfo::SUBTITLE:
return AdaptationSet::kRoleSubtitle;
default:
NOTREACHED() << "Unknown MediaInfo TextType: " << type
<< " assuming subtitle.";
NOTIMPLEMENTED() << "Unknown MediaInfo TextType: " << type
<< " assuming subtitle.";
return AdaptationSet::kRoleSubtitle;
}
}
@ -98,7 +99,7 @@ std::string GetPictureAspectRatio(uint32_t width,
<< scaled_height << ") reduced to " << par_num << ":" << par_den
<< " 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
@ -239,7 +240,7 @@ void AdaptationSet::AddRole(Role role) {
// can be passed to Representation to avoid setting redundant attributes. For
// example, if AdaptationSet@width is set, then Representation@width is
// redundant and should not be set.
base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
std::optional<xml::XmlNode> AdaptationSet::GetXml() {
xml::AdaptationSetXmlNode adaptation_set;
bool suppress_representation_width = false;
@ -247,33 +248,33 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
bool suppress_representation_frame_rate = false;
if (id_ && !adaptation_set.SetId(id_.value()))
return base::nullopt;
return std::nullopt;
if (!adaptation_set.SetStringAttribute("contentType", content_type_))
return base::nullopt;
return std::nullopt;
if (!language_.empty() && language_ != "und" &&
!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.
if (video_widths_.size() == 1) {
suppress_representation_width = true;
if (!adaptation_set.SetIntegerAttribute("width", *video_widths_.begin()))
return base::nullopt;
return std::nullopt;
} else if (video_widths_.size() > 1) {
if (!adaptation_set.SetIntegerAttribute("maxWidth",
*video_widths_.rbegin())) {
return base::nullopt;
return std::nullopt;
}
}
if (video_heights_.size() == 1) {
suppress_representation_height = true;
if (!adaptation_set.SetIntegerAttribute("height", *video_heights_.begin()))
return base::nullopt;
return std::nullopt;
} else if (video_heights_.size() > 1) {
if (!adaptation_set.SetIntegerAttribute("maxHeight",
*video_heights_.rbegin())) {
return base::nullopt;
return std::nullopt;
}
}
@ -281,12 +282,12 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
suppress_representation_frame_rate = true;
if (!adaptation_set.SetStringAttribute(
"frameRate", video_frame_rates_.begin()->second)) {
return base::nullopt;
return std::nullopt;
}
} else if (video_frame_rates_.size() > 1) {
if (!adaptation_set.SetStringAttribute(
"maxFrameRate", video_frame_rates_.rbegin()->second)) {
return base::nullopt;
return std::nullopt;
}
}
@ -302,60 +303,60 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
? "subsegmentAlignment"
: "segmentAlignment",
"true")) {
return base::nullopt;
return std::nullopt;
}
}
if (picture_aspect_ratio_.size() == 1 &&
!adaptation_set.SetStringAttribute("par",
*picture_aspect_ratio_.begin())) {
return base::nullopt;
return std::nullopt;
}
if (!adaptation_set.AddContentProtectionElements(
content_protection_elements_)) {
return base::nullopt;
return std::nullopt;
}
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.
if (!trick_play_reference_ids.empty())
trick_play_reference_ids += ' ';
CHECK(adaptation_set->has_id());
trick_play_reference_ids += std::to_string(adaptation_set->id());
CHECK(tp_adaptation_set->has_id());
trick_play_reference_ids += std::to_string(tp_adaptation_set->id());
}
if (!trick_play_reference_ids.empty() &&
!adaptation_set.AddEssentialProperty(
"http://dashif.org/guidelines/trickmode", trick_play_reference_ids)) {
return base::nullopt;
return std::nullopt;
}
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.
if (!switching_ids.empty())
switching_ids += ',';
CHECK(adaptation_set->has_id());
switching_ids += std::to_string(adaptation_set->id());
CHECK(s_adaptation_set->has_id());
switching_ids += std::to_string(s_adaptation_set->id());
}
if (!switching_ids.empty() &&
!adaptation_set.AddSupplementalProperty(
"urn:mpeg:dash:adaptation-set-switching:2016", switching_ids)) {
return base::nullopt;
return std::nullopt;
}
for (const AdaptationSet::Accessibility& accessibility : accessibilities_) {
if (!adaptation_set.AddAccessibilityElement(accessibility.scheme,
accessibility.value)) {
return base::nullopt;
return std::nullopt;
}
}
for (AdaptationSet::Role role : roles_) {
if (!adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011",
RoleToText(role))) {
return base::nullopt;
return std::nullopt;
}
}
@ -369,10 +370,10 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
representation->SuppressOnce(Representation::kSuppressFrameRate);
auto child = representation->GetXml();
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) {
@ -593,7 +594,7 @@ void AdaptationSet::RecordFrameRate(int32_t frame_duration, int32_t timescale) {
return;
}
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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,15 +13,16 @@
#include <libxml/tree.h>
#include <chrono>
#include <list>
#include <memory>
#include <optional>
#include <string>
#include "packager/base/compiler_specific.h"
#include "packager/base/optional.h"
#include "packager/base/time/clock.h"
#include "packager/macros.h"
#include "packager/mpd/base/mpd_options.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
// 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.
/// @return true on success, false otherwise.
// 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
/// specified MPD path.
@ -68,7 +69,7 @@ class MpdBuilder {
// Inject a |clock| that returns the current time.
/// This is for testing.
void InjectClockForTesting(std::unique_ptr<base::Clock> clock) {
void InjectClockForTesting(std::unique_ptr<Clock> 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
// using appropriate xmlDocPtr freeing function.
// 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 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
// 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.
bool AddDynamicMpdInfo(xml::XmlNode* mpd_node) WARN_UNUSED_RESULT;
[[nodiscard]] bool AddDynamicMpdInfo(xml::XmlNode* mpd_node);
// 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();
@ -109,7 +110,7 @@ class MpdBuilder {
// Gets the earliest, normalized segment timestamp. Returns true if
// successful, false otherwise.
bool GetEarliestTimestamp(double* timestamp_seconds) WARN_UNUSED_RESULT;
[[nodiscard]] bool GetEarliestTimestamp(double* timestamp_seconds);
// Update Period durations and presentation timestamps.
void UpdatePeriodDurationAndPresentationTimestamp();
@ -125,7 +126,7 @@ class MpdBuilder {
// By default, this returns the current time. This can be injected for
// testing.
std::unique_ptr<base::Clock> clock_;
std::unique_ptr<Clock> clock_;
};
} // namespace shaka

View File

@ -7,31 +7,21 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include "packager/mpd/base/adaptation_set.h"
#include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/period.h"
#include "packager/mpd/base/representation.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"
using ::testing::HasSubstr;
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>
class MpdBuilderTest : public ::testing::Test {
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.
void InjectTestClock() {
base::Time::Exploded test_time = { 2016, // year.
1, // month
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))));
mpd_.InjectClockForTesting(
std::make_unique<TestClock>("2016-01-11T15:10:24"));
}
};

View File

@ -6,8 +6,7 @@
#include "packager/mpd/base/mpd_notifier_util.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/string_util.h"
#include "glog/logging.h"
#include "packager/file/file.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 output;
std::string input_in_string(input.begin(), input.end());
base::Base64Encode(input_in_string, &output);
absl::Base64Escape(input_in_string, &output);
return output;
}

View File

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

View File

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

View File

@ -50,6 +50,8 @@ std::string GetBaseCodec(const MediaInfo& media_info);
// Returns a key made from the characteristics that separate AdaptationSets.
std::string GetAdaptationSetKey(const MediaInfo& media_info, bool ignore_codec);
std::string FloatToXmlString(double number);
std::string SecondsToXmlDuration(double seconds);
// 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
// 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 <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/mpd_options.h"
#include "packager/mpd/base/mpd_utils.h"
#include "packager/mpd/test/mpd_builder_test_helper.h"
#include "packager/mpd/test/xml_compare.h"
@ -122,8 +122,8 @@ TEST_F(MpdUtilsTest, ContentProtectionPlayReadyCencMspr) {
"98404286AB92E65BE0885F9500000001"
"11223344556677889900AABBCCDDEEFF"
"0000000430313233");
std::vector<uint8_t> pssh;
base::HexStringToBytes(pssh_str, &pssh);
std::string pssh = absl::HexStringToBytes(pssh_str);
const char kMediaInfoWithContentProtection[] =
"video_info {"
@ -179,8 +179,13 @@ TEST_F(MpdUtilsTest, ContentProtectionPlayReadyCenc) {
"98404286AB92E65BE0885F9500000001"
"11223344556677889900AABBCCDDEEFF"
"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[] =
"video_info {"
@ -219,10 +224,12 @@ TEST_F(MpdUtilsTest, ContentProtectionPlayReadyCenc) {
" <ContentProtection"
" schemeIdUri='urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95'>"
" <cenc:pssh>"
"AAAAOHBzc2gBAAAAmgTweZhAQoarkuZb4IhflQAAAAERIjNEVWZ3iJkAqrvM3e7/AAAABDAxMjM="
"AAAAOHBzc2gBAAAAmgTweZhAQoarkuZb4IhflQAAAAERIjNEVWZ3iJkAqrvM3e7/"
"AAAABDAxMjM="
" </cenc:pssh>"
" </ContentProtection>"
" <Representation id='0' bandwidth='0' codecs='avc1' mimeType='video/mp4'/>"
" <Representation id='0' bandwidth='0' codecs='avc1' "
"mimeType='video/mp4'/>"
"</AdaptationSet>";
EXPECT_THAT(adaptation_set_.GetXml(), XmlNodeEqual(kExpectedOutput));

View File

@ -5,8 +5,8 @@
// https://developers.google.com/open-source/licenses/bsd
#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/mpd_options.h"
#include "packager/mpd/base/mpd_utils.h"
@ -122,7 +122,7 @@ AdaptationSet* Period::GetOrCreateAdaptationSet(
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(
[](const std::unique_ptr<AdaptationSet>& adaptation_set_a,
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.
if (!period.SetId(id_))
return base::nullopt;
return std::nullopt;
// Required for LL-DASH MPDs.
if (mpd_options_.mpd_params.low_latency_dash_mode) {
// Create ServiceDescription element.
xml::XmlNode service_description_node("ServiceDescription");
if (!service_description_node.SetIntegerAttribute("id", id_))
return base::nullopt;
return std::nullopt;
// Insert Latency into ServiceDescription element.
xml::XmlNode latency_node("Latency");
uint64_t target_latency_ms =
mpd_options_.mpd_params.target_latency_seconds * 1000;
if (!latency_node.SetIntegerAttribute("target", target_latency_ms))
return base::nullopt;
return std::nullopt;
if (!service_description_node.AddChild(std::move(latency_node)))
return base::nullopt;
return std::nullopt;
// Insert ServiceDescription into Period element.
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.
for (const auto& adaptation_set : adaptation_sets_) {
auto child = adaptation_set->GetXml();
if (!child || !period.AddChild(std::move(*child)))
return base::nullopt;
return std::nullopt;
}
if (output_period_duration) {
if (!period.SetStringAttribute("duration",
SecondsToXmlDuration(duration_seconds_))) {
return base::nullopt;
return std::nullopt;
}
} else if (mpd_options_.mpd_type == MpdType::kDynamic) {
if (!period.SetStringAttribute(
"start", SecondsToXmlDuration(start_time_in_seconds_))) {
return base::nullopt;
return std::nullopt;
}
}
return period;
@ -320,7 +320,8 @@ std::string Period::GetAdaptationSetKeyForTrickPlay(
void Period::ProtectedAdaptationSetMap::Register(
const AdaptationSet& adaptation_set,
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();
}

View File

@ -12,7 +12,7 @@
#include <list>
#include <map>
#include "packager/base/optional.h"
#include <optional>
#include "packager/mpd/base/adaptation_set.h"
#include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/xml/xml_node.h"
@ -46,7 +46,7 @@ class Period {
/// Generates <Period> xml element with its child AdaptationSet elements.
/// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a
/// 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.
const std::list<AdaptationSet*> GetAdaptationSets() const;

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@
#include <string>
#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_util.h"
@ -75,7 +75,7 @@ class SimpleMpdNotifier : public MpdNotifier {
std::string output_path_;
std::unique_ptr<MpdBuilder> mpd_builder_;
bool content_protection_in_adaptation_set_ = true;
base::Lock lock_;
absl::Mutex lock_;
uint32_t next_adaptation_set_id_ = 0;
// Maps Representation ID to Representation.

View File

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

View File

@ -6,33 +6,37 @@
#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 <cmath>
#include <limits>
#include <set>
#include "packager/base/logging.h"
#include "packager/base/macros.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/sys_byteorder.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_format.h"
#include "packager/macros.h"
#include "packager/media/base/rcheck.h"
#include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/mpd_utils.h"
#include "packager/mpd/base/segment_info.h"
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
DEFINE_bool(segment_template_constant_duration,
false,
"Generates SegmentTemplate@duration if all segments except the "
"last one has the same duration if this flag is set to true.");
ABSL_FLAG(bool,
segment_template_constant_duration,
false,
"Generates SegmentTemplate@duration if all segments except the "
"last one has the same duration if this flag is set to true.");
DEFINE_bool(dash_add_last_segment_number_when_needed,
false,
"Adds a Supplemental Descriptor with @schemeIdUri "
"set to http://dashif.org/guidelines/last-segment-number with "
"the @value set to the last segment number.");
ABSL_FLAG(bool,
dash_add_last_segment_number_when_needed,
false,
"Adds a Supplemental Descriptor with @schemeIdUri "
"set to http://dashif.org/guidelines/last-segment-number with "
"the @value set to the last segment number.");
namespace shaka {
@ -45,15 +49,14 @@ const char kEC3Codec[] = "ec-3";
const char kAC4Codec[] = "ac-4";
std::string RangeToString(const Range& range) {
return base::Uint64ToString(range.begin()) + "-" +
base::Uint64ToString(range.end());
return absl::StrFormat("%u-%u", range.begin(), range.end());
}
// Check if segments are continuous and all segments except the last one are of
// the same duration.
bool IsTimelineConstantDuration(const std::list<SegmentInfo>& segment_infos,
uint32_t start_number) {
if (!FLAGS_segment_template_constant_duration)
if (!absl::GetFlag(FLAGS_segment_template_constant_duration))
return false;
DCHECK(!segment_infos.empty());
@ -145,7 +148,7 @@ bool XmlNode::AddChild(XmlNode child) {
// Reaching here means the ownership of |child| transfered to |node|.
// 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;
}
@ -192,14 +195,15 @@ bool XmlNode::SetIntegerAttribute(const std::string& attribute_name,
uint64_t number) {
DCHECK(impl_->node);
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,
double number) {
DCHECK(impl_->node);
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) {
@ -345,9 +349,9 @@ bool RepresentationXmlNode::AddVideoInfo(const VideoInfo& video_info,
}
if (video_info.has_pixel_width() && video_info.has_pixel_height()) {
RCHECK(SetStringAttribute(
"sar", base::IntToString(video_info.pixel_width()) + ":" +
base::IntToString(video_info.pixel_height())));
RCHECK(SetStringAttribute("sar",
absl::StrFormat("%d:%d", video_info.pixel_width(),
video_info.pixel_height())));
}
if (set_width)
@ -355,14 +359,14 @@ bool RepresentationXmlNode::AddVideoInfo(const VideoInfo& video_info,
if (set_height)
RCHECK(SetIntegerAttribute("height", video_info.height()));
if (set_frame_rate) {
RCHECK(SetStringAttribute(
"frameRate", base::IntToString(video_info.time_scale()) + "/" +
base::IntToString(video_info.frame_duration())));
RCHECK(SetStringAttribute("frameRate",
absl::StrFormat("%d/%d", video_info.time_scale(),
video_info.frame_duration())));
}
if (video_info.has_playback_rate()) {
RCHECK(SetStringAttribute("maxPlayoutRate",
base::IntToString(video_info.playback_rate())));
RCHECK(SetStringAttribute(
"maxPlayoutRate", absl::StrFormat("%d", video_info.playback_rate())));
// Since the trick play stream contains only key frames, there is no coding
// dependency on the main stream. Simply set the codingDependency to false.
// 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()));
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()));
RCHECK(child.SetIntegerAttribute("duration", duration_seconds));
}
@ -505,7 +509,7 @@ bool RepresentationXmlNode::AddLiveOnlyInfo(
if (IsTimelineConstantDuration(segment_infos, start_number)) {
RCHECK(segment_template.SetIntegerAttribute(
"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;
for (const auto& segment_info_element : segment_infos)
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 NO_MAPPING = 0xFFFFFFFF;
if (ec3_channel_mpeg_value == NO_MAPPING) {
// Convert EC3 channel map into string of hexadecimal digits. Spec: DASH-IF
// Interoperability Points v3.0 9.2.1.2.
const uint16_t ec3_channel_map =
base::HostToNet16(codec_data.channel_mask());
// Convert EC3 channel map into string of hexadecimal digits. Spec:
// DASH-IF Interoperability Points v3.0 9.2.1.2.
audio_channel_config_value =
base::HexEncode(&ec3_channel_map, sizeof(ec3_channel_map));
absl::StrFormat("%04X", codec_data.channel_mask());
audio_channel_config_scheme =
"tag:dolby.com,2014:dash:audio_channel_configuration:2011";
} else {
// Calculate EC3 channel configuration descriptor value with MPEG scheme.
// Spec: ETSI TS 102 366 V1.4.1 Digital Audio Compression
// (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";
}
bool ret = AddDescriptor("AudioChannelConfiguration",
@ -560,7 +563,7 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
// D.2.2.
if (codec_data.ec3_joc_complexity() != 0) {
std::string ec3_joc_complexity =
base::UintToString(codec_data.ec3_joc_complexity());
absl::StrFormat("%u", codec_data.ec3_joc_complexity());
ret &= AddDescriptor("SupplementalProperty",
"tag:dolby.com,2018:dash:EC3_ExtensionType:2018",
"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
// Audio Compression (AC-4) Standard; Part 2: Immersive and personalized
// 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 =
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.
// See https://github.com/Dash-Industry-Forum/DASH-IF-IOP/issues/268.
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.
// Spec: ETSI TS 103 190-2 V1.2.1 Digital Audio Compression (AC-4) Standard;
// 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";
}
bool ret = AddDescriptor("AudioChannelConfiguration",
@ -607,7 +611,8 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
}
return ret;
} 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 =
"urn:mpeg:dash:23003:3:audio_channel_configuration:2011";
}

View File

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

View File

@ -4,23 +4,23 @@
// license that can be found in the LICENSE file or at
// 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 <gtest/gtest.h>
#include <libxml/tree.h>
#include <list>
#include "packager/base/logging.h"
#include "packager/base/strings/string_util.h"
#include <glog/logging.h>
#include "packager/flag_saver.h"
#include "packager/mpd/base/segment_info.h"
#include "packager/mpd/base/xml/xml_node.h"
#include "packager/mpd/test/mpd_builder_test_helper.h"
#include "packager/mpd/test/xml_compare.h"
DECLARE_bool(segment_template_constant_duration);
DECLARE_bool(dash_add_last_segment_number_when_needed);
ABSL_DECLARE_FLAG(bool, segment_template_constant_duration);
ABSL_DECLARE_FLAG(bool, dash_add_last_segment_number_when_needed);
using ::testing::ElementsAre;
@ -352,15 +352,20 @@ TEST(XmlNodeTest, AddAC4AudioInfoMPEGSchemeIMS) {
}
class LiveSegmentTimelineTest : public ::testing::Test {
public:
LiveSegmentTimelineTest()
: saver(&FLAGS_segment_template_constant_duration) {}
protected:
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");
}
void TearDown() override { FLAGS_segment_template_constant_duration = false; }
MediaInfo media_info_;
private:
FlagSaver<bool> saver;
};
TEST_F(LiveSegmentTimelineTest, OneSegmentInfo) {
@ -536,7 +541,9 @@ TEST_F(LiveSegmentTimelineTest, LastSegmentNumberSupplementalProperty) {
{kStartTime, kDuration, kRepeat},
};
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,
kStartNumber, kIsLowLatency));
@ -549,7 +556,6 @@ TEST_F(LiveSegmentTimelineTest, LastSegmentNumberSupplementalProperty) {
" <SegmentTemplate media=\"$Number$.m4s\" "
" startNumber=\"1\" duration=\"100\"/>"
"</Representation>"));
FLAGS_dash_add_last_segment_number_when_needed = false;
}
// Creating a separate Test Suite for RepresentationXmlNode::AddVODOnlyInfo
@ -751,7 +757,7 @@ TEST_F(LowLatencySegmentTest, LowLatencySegmentTemplate) {
representation,
XmlNodeEqual("<Representation>"
" <SegmentTemplate timescale=\"90000\" duration=\"450000\" "
" availabilityTimeOffset=\"4.9750987314\" "
" availabilityTimeOffset=\"4.9751\" "
" availabilityTimeComplete=\"false\" "
" initialization=\"init.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 <glog/logging.h>
#include <google/protobuf/text_format.h>
#include <gtest/gtest.h>
#include <filesystem>
#include "packager/base/files/file_util.h"
#include "packager/base/path_service.h"
#include "packager/media/test/test_data_util.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/test/xml_compare.h"
namespace shaka {
base::FilePath GetTestDataFilePath(const std::string& file_name) {
base::FilePath file_path;
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
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;
std::filesystem::path GetTestDataFilePath(const std::string& name) {
auto data_dir = std::filesystem::path(TEST_DATA_DIR);
return data_dir / name;
}
base::FilePath GetSchemaPath() {
base::FilePath file_path;
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
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;
std::filesystem::path GetSchemaPath() {
auto schema_dir = std::filesystem::path(TEST_SCHEMA_DIR);
return schema_dir / "DASH-MPD.xsd";
}
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) {
return ConvertToMediaInfo(
GetPathContent(GetTestDataFilePath(media_info_file_name)));
std::filesystem::path test_path = GetTestDataFilePath(media_info_file_name);
return ConvertToMediaInfo(GetPathContent(test_path));
}
bool ValidateMpdSchema(const std::string& mpd) {
@ -69,18 +48,15 @@ bool ValidateMpdSchema(const std::string& mpd) {
return false;
}
base::FilePath schema_path = GetSchemaPath();
std::filesystem::path schema_path = GetSchemaPath();
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
// the DASH-MPD.xsd. Then it can resolve the relative path included from the
// XSD when creating xmlSchemaParserCtxt.
xml::scoped_xml_ptr<xmlDoc> schema_as_doc(
xmlReadMemory(schema_str.data(),
schema_str.size(),
schema_path.AsUTF8Unsafe().c_str(),
NULL,
0));
xmlReadMemory(schema_str.data(), schema_str.size(),
schema_path.string().c_str(), NULL, 0));
DCHECK(schema_as_doc);
xml::scoped_xml_ptr<xmlSchemaParserCtxt>
schema_parser_ctxt(xmlSchemaNewDocParserCtxt(schema_as_doc.get()));
@ -102,9 +78,11 @@ bool ValidateMpdSchema(const std::string& mpd) {
void ExpectMpdToEqualExpectedOutputFile(
const std::string& mpd_string,
const std::string& expected_output_file) {
std::string expected_mpd;
ASSERT_TRUE(base::ReadFileToString(GetTestDataFilePath(expected_output_file),
&expected_mpd))
std::filesystem::path expected_output_file_path =
GetTestDataFilePath(expected_output_file);
std::string expected_mpd = GetPathContent(expected_output_file_path);
ASSERT_TRUE(!expected_mpd.empty())
<< "Failed to read: " << expected_output_file;
// Adding extra << here to get a formatted output.

View File

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

View File

@ -1,16 +1,14 @@
#include "packager/mpd/test/xml_compare.h"
#include <absl/strings/strip.h>
#include <glog/logging.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <algorithm>
#include <map>
#include <string>
#include <utility>
#include "packager/base/logging.h"
#include "packager/base/strings/string_util.h"
namespace shaka {
namespace {
@ -80,10 +78,17 @@ bool CompareContents(xmlNodePtr node1, xmlNodePtr node2) {
reinterpret_cast<const char*>(node1_content_ptr.get());
std::string node2_content =
reinterpret_cast<const char*>(node2_content_ptr.get());
base::ReplaceChars(node1_content, "\n", "", &node1_content);
base::TrimString(node1_content, " ", &node1_content);
base::ReplaceChars(node2_content, "\n", "", &node2_content);
base::TrimString(node2_content, " ", &node2_content);
node1_content.erase(
std::remove(node1_content.begin(), node1_content.end(), '\n'),
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 "
<< reinterpret_cast<const char*>(node1->name) << "\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,
const base::Optional<xml::XmlNode>& xml2) {
const std::optional<xml::XmlNode>& 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());
}
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$";
}

View File

@ -6,7 +6,7 @@
#include <string>
#include "packager/base/optional.h"
#include <optional>
#include "packager/mpd/base/xml/scoped_xml_ptr.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.
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 base::Optional<xml::XmlNode>& xml2);
bool XmlEqual(const std::string& xml1, const std::optional<xml::XmlNode>& xml2);
/// Get string representation of the 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.
MATCHER_P(XmlNodeEqual,

View File

@ -6,22 +6,22 @@
#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 <stdint.h>
#include "packager/base/files/file_path.h"
#include "packager/base/files/file_util.h"
#include "packager/file/file.h"
#include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/mpd_notifier.h"
#include "packager/mpd/base/mpd_utils.h"
#include "packager/mpd/base/simple_mpd_notifier.h"
DEFINE_bool(generate_dash_if_iop_compliant_mpd,
true,
"Try to generate DASH-IF IOP compliant MPD. This is best effort "
"and does not guarantee compliance.");
ABSL_FLAG(bool,
generate_dash_if_iop_compliant_mpd,
true,
"Try to generate DASH-IF IOP compliant MPD. This is best effort "
"and does not guarantee compliance.");
namespace shaka {
@ -71,7 +71,7 @@ bool MpdWriter::WriteMpdToFile(const char* file_name) {
mpd_options.mpd_params.base_urls = base_urls_;
mpd_options.mpd_params.mpd_output = file_name;
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 =
notifier_factory_->Create(mpd_options);
if (!notifier->Init()) {

View File

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

View File

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

View File

@ -4,6 +4,16 @@
# license that can be found in the LICENSE file or at
# 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
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