parent
97cf7c0a5b
commit
96acd1ecfd
|
@ -43,4 +43,23 @@ std::vector<uint8_t> ReadTestDataFile(const std::string& name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
||||||
|
// Get the content of |file_path|. Returns empty string on error.
|
||||||
|
std::string GetPathContent(std::filesystem::path& file_path) {
|
||||||
|
std::string content;
|
||||||
|
|
||||||
|
FILE* f = fopen(file_path.string().c_str(), "rb");
|
||||||
|
if (!f) {
|
||||||
|
LOG(FATAL) << "Failed to read test data from " << file_path;
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
content.resize(std::filesystem::file_size(file_path));
|
||||||
|
size_t size = fread(content.data(), 1, content.size(), f);
|
||||||
|
content.resize(size);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -24,6 +24,10 @@ std::filesystem::path GetAppTestDataFilePath(const std::string& name);
|
||||||
std::vector<uint8_t> ReadTestDataFile(const std::string& name);
|
std::vector<uint8_t> ReadTestDataFile(const std::string& name);
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
||||||
|
// Get the content of |file_path|. Returns empty string on error.
|
||||||
|
std::string GetPathContent(std::filesystem::path& file_path);
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
||||||
#endif // PACKAGER_MEDIA_TEST_TEST_DATA_UTIL_H_
|
#endif // PACKAGER_MEDIA_TEST_TEST_DATA_UTIL_H_
|
||||||
|
|
|
@ -5,3 +5,111 @@
|
||||||
# https://developers.google.com/open-source/licenses/bsd
|
# https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
add_subdirectory(base)
|
add_subdirectory(base)
|
||||||
|
|
||||||
|
add_library(manifest_base STATIC
|
||||||
|
base/bandwidth_estimator.cc
|
||||||
|
base/bandwidth_estimator.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(manifest_base
|
||||||
|
glog
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(mpd_builder STATIC
|
||||||
|
base/adaptation_set.cc
|
||||||
|
base/adaptation_set.h
|
||||||
|
base/content_protection_element.cc
|
||||||
|
base/content_protection_element.h
|
||||||
|
base/mpd_builder.cc
|
||||||
|
base/mpd_builder.h
|
||||||
|
base/mpd_notifier_util.cc
|
||||||
|
base/mpd_notifier_util.h
|
||||||
|
base/mpd_notifier.h
|
||||||
|
base/mpd_options.h
|
||||||
|
base/mpd_utils.cc
|
||||||
|
base/mpd_utils.h
|
||||||
|
base/period.cc
|
||||||
|
base/period.h
|
||||||
|
base/representation.cc
|
||||||
|
base/representation.h
|
||||||
|
base/segment_info.h
|
||||||
|
base/simple_mpd_notifier.cc
|
||||||
|
base/simple_mpd_notifier.h
|
||||||
|
base/xml/scoped_xml_ptr.h
|
||||||
|
base/xml/xml_node.cc
|
||||||
|
base/xml/xml_node.h
|
||||||
|
public/mpd_params.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(mpd_builder
|
||||||
|
absl::flags
|
||||||
|
absl::strings
|
||||||
|
absl::str_format
|
||||||
|
glog
|
||||||
|
LibXml2
|
||||||
|
file
|
||||||
|
media_base
|
||||||
|
manifest_base
|
||||||
|
mpd_media_info_proto
|
||||||
|
utils_clock
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
add_library(mpd_mocks STATIC
|
||||||
|
base/mock_mpd_builder.cc
|
||||||
|
base/mock_mpd_builder.h
|
||||||
|
base/mock_mpd_notifier.cc
|
||||||
|
base/mock_mpd_notifier.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(mpd_mocks gmock absl::synchronization LibXml2 mpd_media_info_proto)
|
||||||
|
|
||||||
|
add_executable(mpd_unittest
|
||||||
|
base/adaptation_set_unittest.cc
|
||||||
|
base/bandwidth_estimator_unittest.cc
|
||||||
|
base/mpd_builder_unittest.cc
|
||||||
|
base/mpd_utils_unittest.cc
|
||||||
|
base/period_unittest.cc
|
||||||
|
base/representation_unittest.cc
|
||||||
|
base/simple_mpd_notifier_unittest.cc
|
||||||
|
base/xml/xml_node_unittest.cc
|
||||||
|
test/mpd_builder_test_helper.cc
|
||||||
|
test/mpd_builder_test_helper.h
|
||||||
|
test/xml_compare.cc
|
||||||
|
test/xml_compare.h
|
||||||
|
util/mpd_writer_unittest.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(mpd_unittest
|
||||||
|
PRIVATE
|
||||||
|
# We used to build off of __FILE__, but that is not always an absolute
|
||||||
|
# path, depending on the version of CMake. This is consistent.
|
||||||
|
TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/test/data"
|
||||||
|
TEST_SCHEMA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/test/schema")
|
||||||
|
|
||||||
|
target_link_libraries(mpd_unittest
|
||||||
|
file
|
||||||
|
file_test_util
|
||||||
|
test_data_util
|
||||||
|
absl::flags
|
||||||
|
mpd_builder
|
||||||
|
mpd_mocks
|
||||||
|
mpd_util
|
||||||
|
gmock
|
||||||
|
gtest
|
||||||
|
utils_test_clock
|
||||||
|
gtest_main)
|
||||||
|
|
||||||
|
add_test(NAME mpd_unittest COMMAND mpd_unittest)
|
||||||
|
|
||||||
|
add_library(mpd_util STATIC
|
||||||
|
util/mpd_writer.cc
|
||||||
|
util/mpd_writer.h)
|
||||||
|
|
||||||
|
target_link_libraries(mpd_util
|
||||||
|
file
|
||||||
|
absl::flags
|
||||||
|
mpd_builder
|
||||||
|
mpd_mocks
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
|
|
||||||
#include "packager/mpd/base/adaptation_set.h"
|
#include "packager/mpd/base/adaptation_set.h"
|
||||||
|
|
||||||
|
#include <absl/strings/numbers.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
#include "absl/strings/str_format.h"
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
|
||||||
#include "packager/mpd/base/media_info.pb.h"
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
#include "packager/mpd/base/mpd_options.h"
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
#include "packager/mpd/base/mpd_utils.h"
|
#include "packager/mpd/base/mpd_utils.h"
|
||||||
|
@ -30,7 +31,7 @@ AdaptationSet::Role MediaInfoTextTypeToRole(
|
||||||
case MediaInfo::TextInfo::SUBTITLE:
|
case MediaInfo::TextInfo::SUBTITLE:
|
||||||
return AdaptationSet::kRoleSubtitle;
|
return AdaptationSet::kRoleSubtitle;
|
||||||
default:
|
default:
|
||||||
NOTREACHED() << "Unknown MediaInfo TextType: " << type
|
NOTIMPLEMENTED() << "Unknown MediaInfo TextType: " << type
|
||||||
<< " assuming subtitle.";
|
<< " assuming subtitle.";
|
||||||
return AdaptationSet::kRoleSubtitle;
|
return AdaptationSet::kRoleSubtitle;
|
||||||
}
|
}
|
||||||
|
@ -98,7 +99,7 @@ std::string GetPictureAspectRatio(uint32_t width,
|
||||||
<< scaled_height << ") reduced to " << par_num << ":" << par_den
|
<< scaled_height << ") reduced to " << par_num << ":" << par_den
|
||||||
<< " with error " << min_error << ".";
|
<< " with error " << min_error << ".";
|
||||||
|
|
||||||
return base::IntToString(par_num) + ":" + base::IntToString(par_den);
|
return absl::StrFormat("%d:%d", par_num, par_den);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds an entry to picture_aspect_ratio if the size of picture_aspect_ratio is
|
// Adds an entry to picture_aspect_ratio if the size of picture_aspect_ratio is
|
||||||
|
@ -239,7 +240,7 @@ void AdaptationSet::AddRole(Role role) {
|
||||||
// can be passed to Representation to avoid setting redundant attributes. For
|
// can be passed to Representation to avoid setting redundant attributes. For
|
||||||
// example, if AdaptationSet@width is set, then Representation@width is
|
// example, if AdaptationSet@width is set, then Representation@width is
|
||||||
// redundant and should not be set.
|
// redundant and should not be set.
|
||||||
base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
|
std::optional<xml::XmlNode> AdaptationSet::GetXml() {
|
||||||
xml::AdaptationSetXmlNode adaptation_set;
|
xml::AdaptationSetXmlNode adaptation_set;
|
||||||
|
|
||||||
bool suppress_representation_width = false;
|
bool suppress_representation_width = false;
|
||||||
|
@ -247,33 +248,33 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
|
||||||
bool suppress_representation_frame_rate = false;
|
bool suppress_representation_frame_rate = false;
|
||||||
|
|
||||||
if (id_ && !adaptation_set.SetId(id_.value()))
|
if (id_ && !adaptation_set.SetId(id_.value()))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
if (!adaptation_set.SetStringAttribute("contentType", content_type_))
|
if (!adaptation_set.SetStringAttribute("contentType", content_type_))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
if (!language_.empty() && language_ != "und" &&
|
if (!language_.empty() && language_ != "und" &&
|
||||||
!adaptation_set.SetStringAttribute("lang", language_)) {
|
!adaptation_set.SetStringAttribute("lang", language_)) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that std::{set,map} are ordered, so the last element is the max value.
|
// Note that std::{set,map} are ordered, so the last element is the max value.
|
||||||
if (video_widths_.size() == 1) {
|
if (video_widths_.size() == 1) {
|
||||||
suppress_representation_width = true;
|
suppress_representation_width = true;
|
||||||
if (!adaptation_set.SetIntegerAttribute("width", *video_widths_.begin()))
|
if (!adaptation_set.SetIntegerAttribute("width", *video_widths_.begin()))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
} else if (video_widths_.size() > 1) {
|
} else if (video_widths_.size() > 1) {
|
||||||
if (!adaptation_set.SetIntegerAttribute("maxWidth",
|
if (!adaptation_set.SetIntegerAttribute("maxWidth",
|
||||||
*video_widths_.rbegin())) {
|
*video_widths_.rbegin())) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (video_heights_.size() == 1) {
|
if (video_heights_.size() == 1) {
|
||||||
suppress_representation_height = true;
|
suppress_representation_height = true;
|
||||||
if (!adaptation_set.SetIntegerAttribute("height", *video_heights_.begin()))
|
if (!adaptation_set.SetIntegerAttribute("height", *video_heights_.begin()))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
} else if (video_heights_.size() > 1) {
|
} else if (video_heights_.size() > 1) {
|
||||||
if (!adaptation_set.SetIntegerAttribute("maxHeight",
|
if (!adaptation_set.SetIntegerAttribute("maxHeight",
|
||||||
*video_heights_.rbegin())) {
|
*video_heights_.rbegin())) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,12 +282,12 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
|
||||||
suppress_representation_frame_rate = true;
|
suppress_representation_frame_rate = true;
|
||||||
if (!adaptation_set.SetStringAttribute(
|
if (!adaptation_set.SetStringAttribute(
|
||||||
"frameRate", video_frame_rates_.begin()->second)) {
|
"frameRate", video_frame_rates_.begin()->second)) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
} else if (video_frame_rates_.size() > 1) {
|
} else if (video_frame_rates_.size() > 1) {
|
||||||
if (!adaptation_set.SetStringAttribute(
|
if (!adaptation_set.SetStringAttribute(
|
||||||
"maxFrameRate", video_frame_rates_.rbegin()->second)) {
|
"maxFrameRate", video_frame_rates_.rbegin()->second)) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,60 +303,60 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
|
||||||
? "subsegmentAlignment"
|
? "subsegmentAlignment"
|
||||||
: "segmentAlignment",
|
: "segmentAlignment",
|
||||||
"true")) {
|
"true")) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (picture_aspect_ratio_.size() == 1 &&
|
if (picture_aspect_ratio_.size() == 1 &&
|
||||||
!adaptation_set.SetStringAttribute("par",
|
!adaptation_set.SetStringAttribute("par",
|
||||||
*picture_aspect_ratio_.begin())) {
|
*picture_aspect_ratio_.begin())) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!adaptation_set.AddContentProtectionElements(
|
if (!adaptation_set.AddContentProtectionElements(
|
||||||
content_protection_elements_)) {
|
content_protection_elements_)) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string trick_play_reference_ids;
|
std::string trick_play_reference_ids;
|
||||||
for (const AdaptationSet* adaptation_set : trick_play_references_) {
|
for (const AdaptationSet* tp_adaptation_set : trick_play_references_) {
|
||||||
// Should be a whitespace-separated list, see DASH-IOP 3.2.9.
|
// Should be a whitespace-separated list, see DASH-IOP 3.2.9.
|
||||||
if (!trick_play_reference_ids.empty())
|
if (!trick_play_reference_ids.empty())
|
||||||
trick_play_reference_ids += ' ';
|
trick_play_reference_ids += ' ';
|
||||||
CHECK(adaptation_set->has_id());
|
CHECK(tp_adaptation_set->has_id());
|
||||||
trick_play_reference_ids += std::to_string(adaptation_set->id());
|
trick_play_reference_ids += std::to_string(tp_adaptation_set->id());
|
||||||
}
|
}
|
||||||
if (!trick_play_reference_ids.empty() &&
|
if (!trick_play_reference_ids.empty() &&
|
||||||
!adaptation_set.AddEssentialProperty(
|
!adaptation_set.AddEssentialProperty(
|
||||||
"http://dashif.org/guidelines/trickmode", trick_play_reference_ids)) {
|
"http://dashif.org/guidelines/trickmode", trick_play_reference_ids)) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string switching_ids;
|
std::string switching_ids;
|
||||||
for (const AdaptationSet* adaptation_set : switchable_adaptation_sets_) {
|
for (const AdaptationSet* s_adaptation_set : switchable_adaptation_sets_) {
|
||||||
// Should be a comma-separated list, see DASH-IOP 3.8.
|
// Should be a comma-separated list, see DASH-IOP 3.8.
|
||||||
if (!switching_ids.empty())
|
if (!switching_ids.empty())
|
||||||
switching_ids += ',';
|
switching_ids += ',';
|
||||||
CHECK(adaptation_set->has_id());
|
CHECK(s_adaptation_set->has_id());
|
||||||
switching_ids += std::to_string(adaptation_set->id());
|
switching_ids += std::to_string(s_adaptation_set->id());
|
||||||
}
|
}
|
||||||
if (!switching_ids.empty() &&
|
if (!switching_ids.empty() &&
|
||||||
!adaptation_set.AddSupplementalProperty(
|
!adaptation_set.AddSupplementalProperty(
|
||||||
"urn:mpeg:dash:adaptation-set-switching:2016", switching_ids)) {
|
"urn:mpeg:dash:adaptation-set-switching:2016", switching_ids)) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const AdaptationSet::Accessibility& accessibility : accessibilities_) {
|
for (const AdaptationSet::Accessibility& accessibility : accessibilities_) {
|
||||||
if (!adaptation_set.AddAccessibilityElement(accessibility.scheme,
|
if (!adaptation_set.AddAccessibilityElement(accessibility.scheme,
|
||||||
accessibility.value)) {
|
accessibility.value)) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (AdaptationSet::Role role : roles_) {
|
for (AdaptationSet::Role role : roles_) {
|
||||||
if (!adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011",
|
if (!adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011",
|
||||||
RoleToText(role))) {
|
RoleToText(role))) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,10 +370,10 @@ base::Optional<xml::XmlNode> AdaptationSet::GetXml() {
|
||||||
representation->SuppressOnce(Representation::kSuppressFrameRate);
|
representation->SuppressOnce(Representation::kSuppressFrameRate);
|
||||||
auto child = representation->GetXml();
|
auto child = representation->GetXml();
|
||||||
if (!child || !adaptation_set.AddChild(std::move(*child)))
|
if (!child || !adaptation_set.AddChild(std::move(*child)))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::move(adaptation_set);
|
return adaptation_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdaptationSet::ForceSetSegmentAlignment(bool segment_alignment) {
|
void AdaptationSet::ForceSetSegmentAlignment(bool segment_alignment) {
|
||||||
|
@ -593,7 +594,7 @@ void AdaptationSet::RecordFrameRate(int32_t frame_duration, int32_t timescale) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
video_frame_rates_[static_cast<double>(timescale) / frame_duration] =
|
video_frame_rates_[static_cast<double>(timescale) / frame_duration] =
|
||||||
base::IntToString(timescale) + "/" + base::IntToString(frame_duration);
|
absl::StrFormat("%d/%d", timescale, frame_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/optional.h"
|
|
||||||
#include "packager/mpd/base/xml/xml_node.h"
|
#include "packager/mpd/base/xml/xml_node.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -109,7 +109,7 @@ class AdaptationSet {
|
||||||
/// and ContentProtection elements.
|
/// and ContentProtection elements.
|
||||||
/// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a
|
/// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a
|
||||||
/// NULL scoped_xml_ptr.
|
/// NULL scoped_xml_ptr.
|
||||||
base::Optional<xml::XmlNode> GetXml();
|
std::optional<xml::XmlNode> GetXml();
|
||||||
|
|
||||||
/// Forces the (sub)segmentAlignment field to be set to @a segment_alignment.
|
/// Forces the (sub)segmentAlignment field to be set to @a segment_alignment.
|
||||||
/// Use this if you are certain that the (sub)segments are alinged/unaligned
|
/// Use this if you are certain that the (sub)segments are alinged/unaligned
|
||||||
|
@ -248,7 +248,7 @@ class AdaptationSet {
|
||||||
|
|
||||||
uint32_t* const representation_counter_;
|
uint32_t* const representation_counter_;
|
||||||
|
|
||||||
base::Optional<uint32_t> id_;
|
std::optional<uint32_t> id_;
|
||||||
const std::string language_;
|
const std::string language_;
|
||||||
const MpdOptions& mpd_options_;
|
const MpdOptions& mpd_options_;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
#include "glog/logging.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ uint64_t BandwidthEstimator::GetBitrate(const Block& block,
|
||||||
VLOG(1) << "Exclude short segment (duration " << block.duration
|
VLOG(1) << "Exclude short segment (duration " << block.duration
|
||||||
<< ", target_duration " << target_block_duration
|
<< ", target_duration " << target_block_duration
|
||||||
<< ") in peak bandwidth computation.";
|
<< ") in peak bandwidth computation.";
|
||||||
return 0.0;
|
return 0;
|
||||||
}
|
}
|
||||||
return static_cast<uint64_t>(ceil(block.size_in_bits / block.duration));
|
return static_cast<uint64_t>(ceil(block.size_in_bits / block.duration));
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
#include "packager/base/compiler_specific.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "packager/base/synchronization/lock.h"
|
#include "packager/macros.h"
|
||||||
#include "packager/mpd/base/adaptation_set.h"
|
#include "packager/mpd/base/adaptation_set.h"
|
||||||
#include "packager/mpd/base/content_protection_element.h"
|
#include "packager/mpd/base/content_protection_element.h"
|
||||||
#include "packager/mpd/base/mpd_builder.h"
|
#include "packager/mpd/base/mpd_builder.h"
|
||||||
|
|
|
@ -6,16 +6,15 @@
|
||||||
|
|
||||||
#include "packager/mpd/base/mpd_builder.h"
|
#include "packager/mpd/base/mpd_builder.h"
|
||||||
|
|
||||||
|
#include <absl/strings/numbers.h>
|
||||||
|
#include <absl/strings/str_format.h>
|
||||||
|
#include <absl/synchronization/mutex.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "packager/base/files/file_path.h"
|
|
||||||
#include "packager/base/logging.h"
|
|
||||||
#include "packager/base/optional.h"
|
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
|
||||||
#include "packager/base/strings/stringprintf.h"
|
|
||||||
#include "packager/base/synchronization/lock.h"
|
|
||||||
#include "packager/base/time/default_clock.h"
|
|
||||||
#include "packager/base/time/time.h"
|
|
||||||
#include "packager/media/base/rcheck.h"
|
#include "packager/media/base/rcheck.h"
|
||||||
#include "packager/mpd/base/adaptation_set.h"
|
#include "packager/mpd/base/adaptation_set.h"
|
||||||
#include "packager/mpd/base/mpd_utils.h"
|
#include "packager/mpd/base/mpd_utils.h"
|
||||||
|
@ -25,7 +24,6 @@
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
using base::FilePath;
|
|
||||||
using xml::XmlNode;
|
using xml::XmlNode;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -63,7 +61,7 @@ bool AddMpdNameSpaceInfo(XmlNode* mpd) {
|
||||||
CHECK(iter != uris.end()) << " unexpected namespace " << namespace_name;
|
CHECK(iter != uris.end()) << " unexpected namespace " << namespace_name;
|
||||||
|
|
||||||
RCHECK(mpd->SetStringAttribute(
|
RCHECK(mpd->SetStringAttribute(
|
||||||
base::StringPrintf("xmlns:%s", namespace_name.c_str()).c_str(),
|
absl::StrFormat("xmlns:%s", namespace_name.c_str()).c_str(),
|
||||||
iter->second));
|
iter->second));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -75,18 +73,14 @@ bool Positive(double d) {
|
||||||
|
|
||||||
// Return current time in XML DateTime format. The value is in UTC, so the
|
// Return current time in XML DateTime format. The value is in UTC, so the
|
||||||
// string ends with a 'Z'.
|
// string ends with a 'Z'.
|
||||||
std::string XmlDateTimeNowWithOffset(
|
std::string XmlDateTimeNowWithOffset(int32_t offset_seconds, Clock* clock) {
|
||||||
int32_t offset_seconds,
|
auto time_t = std::chrono::system_clock::to_time_t(
|
||||||
base::Clock* clock) {
|
clock->now() + std::chrono::seconds(offset_seconds));
|
||||||
base::Time time = clock->Now();
|
std::tm* tm = std::gmtime(&time_t);
|
||||||
time += base::TimeDelta::FromSeconds(offset_seconds);
|
|
||||||
base::Time::Exploded time_exploded;
|
|
||||||
time.UTCExplode(&time_exploded);
|
|
||||||
|
|
||||||
return base::StringPrintf("%4d-%02d-%02dT%02d:%02d:%02dZ", time_exploded.year,
|
std::stringstream ss;
|
||||||
time_exploded.month, time_exploded.day_of_month,
|
ss << std::put_time(tm, "%Y-%m-%dT%H:%M:%SZ");
|
||||||
time_exploded.hour, time_exploded.minute,
|
return ss.str();
|
||||||
time_exploded.second);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetIfPositive(const char* attr_name, double value, XmlNode* mpd) {
|
bool SetIfPositive(const char* attr_name, double value, XmlNode* mpd) {
|
||||||
|
@ -94,22 +88,22 @@ bool SetIfPositive(const char* attr_name, double value, XmlNode* mpd) {
|
||||||
mpd->SetStringAttribute(attr_name, SecondsToXmlDuration(value));
|
mpd->SetStringAttribute(attr_name, SecondsToXmlDuration(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MakePathRelative(const std::string& media_path,
|
std::string MakePathRelative(const std::filesystem::path& media_path,
|
||||||
const FilePath& parent_path) {
|
const std::filesystem::path& parent_path) {
|
||||||
FilePath relative_path;
|
auto relative_path = std::filesystem::relative(media_path, parent_path);
|
||||||
const FilePath child_path = FilePath::FromUTF8Unsafe(media_path);
|
if (relative_path.empty() || *relative_path.begin() == "..") {
|
||||||
const bool is_child =
|
// Not related.
|
||||||
parent_path.AppendRelativePath(child_path, &relative_path);
|
relative_path = media_path;
|
||||||
if (!is_child)
|
}
|
||||||
relative_path = child_path;
|
|
||||||
return relative_path.NormalizePathSeparatorsTo('/').AsUTF8Unsafe();
|
return relative_path.lexically_normal().generic_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spooky static initialization/cleanup of libxml.
|
// Spooky static initialization/cleanup of libxml.
|
||||||
class LibXmlInitializer {
|
class LibXmlInitializer {
|
||||||
public:
|
public:
|
||||||
LibXmlInitializer() : initialized_(false) {
|
LibXmlInitializer() : initialized_(false) {
|
||||||
base::AutoLock lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
xmlInitParser();
|
xmlInitParser();
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
|
@ -117,7 +111,7 @@ class LibXmlInitializer {
|
||||||
}
|
}
|
||||||
|
|
||||||
~LibXmlInitializer() {
|
~LibXmlInitializer() {
|
||||||
base::AutoLock lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
xmlCleanupParser();
|
xmlCleanupParser();
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
|
@ -125,7 +119,7 @@ class LibXmlInitializer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
base::Lock lock_;
|
absl::Mutex lock_;
|
||||||
bool initialized_;
|
bool initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(LibXmlInitializer);
|
DISALLOW_COPY_AND_ASSIGN(LibXmlInitializer);
|
||||||
|
@ -134,7 +128,7 @@ class LibXmlInitializer {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
MpdBuilder::MpdBuilder(const MpdOptions& mpd_options)
|
MpdBuilder::MpdBuilder(const MpdOptions& mpd_options)
|
||||||
: mpd_options_(mpd_options), clock_(new base::DefaultClock()) {}
|
: mpd_options_(mpd_options), clock_(new Clock{}) {}
|
||||||
|
|
||||||
MpdBuilder::~MpdBuilder() {}
|
MpdBuilder::~MpdBuilder() {}
|
||||||
|
|
||||||
|
@ -166,15 +160,14 @@ bool MpdBuilder::ToString(std::string* output) {
|
||||||
|
|
||||||
std::string version = GetPackagerVersion();
|
std::string version = GetPackagerVersion();
|
||||||
if (!version.empty()) {
|
if (!version.empty()) {
|
||||||
version =
|
version = absl::StrFormat("Generated with %s version %s",
|
||||||
base::StringPrintf("Generated with %s version %s",
|
|
||||||
GetPackagerProjectUrl().c_str(), version.c_str());
|
GetPackagerProjectUrl().c_str(), version.c_str());
|
||||||
}
|
}
|
||||||
*output = mpd->ToString(version);
|
*output = mpd->ToString(version);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
base::Optional<xml::XmlNode> MpdBuilder::GenerateMpd() {
|
std::optional<xml::XmlNode> MpdBuilder::GenerateMpd() {
|
||||||
XmlNode mpd("MPD");
|
XmlNode mpd("MPD");
|
||||||
|
|
||||||
// Add baseurls to MPD.
|
// Add baseurls to MPD.
|
||||||
|
@ -183,7 +176,7 @@ base::Optional<xml::XmlNode> MpdBuilder::GenerateMpd() {
|
||||||
xml_base_url.SetContent(base_url);
|
xml_base_url.SetContent(base_url);
|
||||||
|
|
||||||
if (!mpd.AddChild(std::move(xml_base_url)))
|
if (!mpd.AddChild(std::move(xml_base_url)))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool output_period_duration = false;
|
bool output_period_duration = false;
|
||||||
|
@ -198,11 +191,11 @@ base::Optional<xml::XmlNode> MpdBuilder::GenerateMpd() {
|
||||||
for (const auto& period : periods_) {
|
for (const auto& period : periods_) {
|
||||||
auto period_node = period->GetXml(output_period_duration);
|
auto period_node = period->GetXml(output_period_duration);
|
||||||
if (!period_node || !mpd.AddChild(std::move(*period_node)))
|
if (!period_node || !mpd.AddChild(std::move(*period_node)))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AddMpdNameSpaceInfo(&mpd))
|
if (!AddMpdNameSpaceInfo(&mpd))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
static const char kOnDemandProfile[] =
|
static const char kOnDemandProfile[] =
|
||||||
"urn:mpeg:dash:profile:isoff-on-demand:2011";
|
"urn:mpeg:dash:profile:isoff-on-demand:2011";
|
||||||
|
@ -211,34 +204,34 @@ base::Optional<xml::XmlNode> MpdBuilder::GenerateMpd() {
|
||||||
switch (mpd_options_.dash_profile) {
|
switch (mpd_options_.dash_profile) {
|
||||||
case DashProfile::kOnDemand:
|
case DashProfile::kOnDemand:
|
||||||
if (!mpd.SetStringAttribute("profiles", kOnDemandProfile))
|
if (!mpd.SetStringAttribute("profiles", kOnDemandProfile))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
break;
|
break;
|
||||||
case DashProfile::kLive:
|
case DashProfile::kLive:
|
||||||
if (!mpd.SetStringAttribute("profiles", kLiveProfile))
|
if (!mpd.SetStringAttribute("profiles", kLiveProfile))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOTREACHED() << "Unknown DASH profile: "
|
NOTIMPLEMENTED() << "Unknown DASH profile: "
|
||||||
<< static_cast<int>(mpd_options_.dash_profile);
|
<< static_cast<int>(mpd_options_.dash_profile);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AddCommonMpdInfo(&mpd))
|
if (!AddCommonMpdInfo(&mpd))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
switch (mpd_options_.mpd_type) {
|
switch (mpd_options_.mpd_type) {
|
||||||
case MpdType::kStatic:
|
case MpdType::kStatic:
|
||||||
if (!AddStaticMpdInfo(&mpd))
|
if (!AddStaticMpdInfo(&mpd))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
break;
|
break;
|
||||||
case MpdType::kDynamic:
|
case MpdType::kDynamic:
|
||||||
if (!AddDynamicMpdInfo(&mpd))
|
if (!AddDynamicMpdInfo(&mpd))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
// Must be after Period element.
|
// Must be after Period element.
|
||||||
if (!AddUtcTiming(&mpd))
|
if (!AddUtcTiming(&mpd))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOTREACHED() << "Unknown MPD type: "
|
NOTIMPLEMENTED() << "Unknown MPD type: "
|
||||||
<< static_cast<int>(mpd_options_.mpd_type);
|
<< static_cast<int>(mpd_options_.mpd_type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -259,7 +252,8 @@ bool MpdBuilder::AddCommonMpdInfo(XmlNode* mpd_node) {
|
||||||
|
|
||||||
bool MpdBuilder::AddStaticMpdInfo(XmlNode* mpd_node) {
|
bool MpdBuilder::AddStaticMpdInfo(XmlNode* mpd_node) {
|
||||||
DCHECK(mpd_node);
|
DCHECK(mpd_node);
|
||||||
DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
|
DCHECK_EQ(static_cast<int>(MpdType::kStatic),
|
||||||
|
static_cast<int>(mpd_options_.mpd_type));
|
||||||
|
|
||||||
static const char kStaticMpdType[] = "static";
|
static const char kStaticMpdType[] = "static";
|
||||||
return mpd_node->SetStringAttribute("type", kStaticMpdType) &&
|
return mpd_node->SetStringAttribute("type", kStaticMpdType) &&
|
||||||
|
@ -270,7 +264,8 @@ bool MpdBuilder::AddStaticMpdInfo(XmlNode* mpd_node) {
|
||||||
|
|
||||||
bool MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
|
bool MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
|
||||||
DCHECK(mpd_node);
|
DCHECK(mpd_node);
|
||||||
DCHECK_EQ(MpdType::kDynamic, mpd_options_.mpd_type);
|
DCHECK_EQ(static_cast<int>(MpdType::kDynamic),
|
||||||
|
static_cast<int>(mpd_options_.mpd_type));
|
||||||
|
|
||||||
static const char kDynamicMpdType[] = "dynamic";
|
static const char kDynamicMpdType[] = "dynamic";
|
||||||
RCHECK(mpd_node->SetStringAttribute("type", kDynamicMpdType));
|
RCHECK(mpd_node->SetStringAttribute("type", kDynamicMpdType));
|
||||||
|
@ -316,7 +311,8 @@ bool MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) {
|
||||||
|
|
||||||
bool MpdBuilder::AddUtcTiming(XmlNode* mpd_node) {
|
bool MpdBuilder::AddUtcTiming(XmlNode* mpd_node) {
|
||||||
DCHECK(mpd_node);
|
DCHECK(mpd_node);
|
||||||
DCHECK_EQ(MpdType::kDynamic, mpd_options_.mpd_type);
|
DCHECK_EQ(static_cast<int>(MpdType::kDynamic),
|
||||||
|
static_cast<int>(mpd_options_.mpd_type));
|
||||||
|
|
||||||
for (const MpdParams::UtcTiming& utc_timing :
|
for (const MpdParams::UtcTiming& utc_timing :
|
||||||
mpd_options_.mpd_params.utc_timings) {
|
mpd_options_.mpd_params.utc_timings) {
|
||||||
|
@ -330,7 +326,8 @@ bool MpdBuilder::AddUtcTiming(XmlNode* mpd_node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float MpdBuilder::GetStaticMpdDuration() {
|
float MpdBuilder::GetStaticMpdDuration() {
|
||||||
DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
|
DCHECK_EQ(static_cast<int>(MpdType::kStatic),
|
||||||
|
static_cast<int>(mpd_options_.mpd_type));
|
||||||
|
|
||||||
float total_duration = 0.0f;
|
float total_duration = 0.0f;
|
||||||
for (const auto& period : periods_) {
|
for (const auto& period : periods_) {
|
||||||
|
@ -365,7 +362,8 @@ bool MpdBuilder::GetEarliestTimestamp(double* timestamp_seconds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MpdBuilder::UpdatePeriodDurationAndPresentationTimestamp() {
|
void MpdBuilder::UpdatePeriodDurationAndPresentationTimestamp() {
|
||||||
DCHECK_EQ(MpdType::kStatic, mpd_options_.mpd_type);
|
DCHECK_EQ(static_cast<int>(MpdType::kStatic),
|
||||||
|
static_cast<int>(mpd_options_.mpd_type));
|
||||||
|
|
||||||
for (const auto& period : periods_) {
|
for (const auto& period : periods_) {
|
||||||
std::list<Representation*> video_representations;
|
std::list<Representation*> video_representations;
|
||||||
|
@ -383,8 +381,8 @@ void MpdBuilder::UpdatePeriodDurationAndPresentationTimestamp() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
base::Optional<double> earliest_start_time;
|
std::optional<double> earliest_start_time;
|
||||||
base::Optional<double> latest_end_time;
|
std::optional<double> latest_end_time;
|
||||||
// The timestamps are based on Video Representations if exist.
|
// The timestamps are based on Video Representations if exist.
|
||||||
const auto& representations = video_representations.size() > 0
|
const auto& representations = video_representations.size() > 0
|
||||||
? video_representations
|
? video_representations
|
||||||
|
@ -418,14 +416,13 @@ void MpdBuilder::MakePathsRelativeToMpd(const std::string& mpd_path,
|
||||||
MediaInfo* media_info) {
|
MediaInfo* media_info) {
|
||||||
DCHECK(media_info);
|
DCHECK(media_info);
|
||||||
const std::string kFileProtocol("file://");
|
const std::string kFileProtocol("file://");
|
||||||
std::string mpd_file_path = (mpd_path.find(kFileProtocol) == 0)
|
std::filesystem::path mpd_file_path =
|
||||||
|
(mpd_path.find(kFileProtocol) == 0)
|
||||||
? mpd_path.substr(kFileProtocol.size())
|
? mpd_path.substr(kFileProtocol.size())
|
||||||
: mpd_path;
|
: mpd_path;
|
||||||
|
|
||||||
if (!mpd_file_path.empty()) {
|
if (!mpd_file_path.empty()) {
|
||||||
const FilePath mpd_dir(FilePath::FromUTF8Unsafe(mpd_file_path)
|
const std::filesystem::path mpd_dir(mpd_file_path.parent_path());
|
||||||
.DirName()
|
|
||||||
.AsEndingWithSeparator());
|
|
||||||
if (!mpd_dir.empty()) {
|
if (!mpd_dir.empty()) {
|
||||||
if (media_info->has_media_file_name()) {
|
if (media_info->has_media_file_name()) {
|
||||||
media_info->set_media_file_url(
|
media_info->set_media_file_url(
|
||||||
|
|
|
@ -13,15 +13,16 @@
|
||||||
|
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "packager/base/compiler_specific.h"
|
#include "packager/macros.h"
|
||||||
#include "packager/base/optional.h"
|
|
||||||
#include "packager/base/time/clock.h"
|
|
||||||
#include "packager/mpd/base/mpd_options.h"
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
#include "packager/mpd/base/xml/xml_node.h"
|
#include "packager/mpd/base/xml/xml_node.h"
|
||||||
|
#include "packager/utils/clock.h"
|
||||||
|
|
||||||
// TODO(rkuroiwa): For classes with |id_|, consider removing the field and let
|
// TODO(rkuroiwa): For classes with |id_|, consider removing the field and let
|
||||||
// the MPD (XML) generation functions take care of assigning an ID to each
|
// the MPD (XML) generation functions take care of assigning an ID to each
|
||||||
|
@ -56,7 +57,7 @@ class MpdBuilder {
|
||||||
/// @param[out] output is an output string where the MPD gets written.
|
/// @param[out] output is an output string where the MPD gets written.
|
||||||
/// @return true on success, false otherwise.
|
/// @return true on success, false otherwise.
|
||||||
// TODO(kqyang): Handle file IO in this class as in HLS media_playlist?
|
// TODO(kqyang): Handle file IO in this class as in HLS media_playlist?
|
||||||
virtual bool ToString(std::string* output) WARN_UNUSED_RESULT;
|
[[nodiscard]] virtual bool ToString(std::string* output);
|
||||||
|
|
||||||
/// Adjusts the fields of MediaInfo so that paths are relative to the
|
/// Adjusts the fields of MediaInfo so that paths are relative to the
|
||||||
/// specified MPD path.
|
/// specified MPD path.
|
||||||
|
@ -68,7 +69,7 @@ class MpdBuilder {
|
||||||
|
|
||||||
// Inject a |clock| that returns the current time.
|
// Inject a |clock| that returns the current time.
|
||||||
/// This is for testing.
|
/// This is for testing.
|
||||||
void InjectClockForTesting(std::unique_ptr<base::Clock> clock) {
|
void InjectClockForTesting(std::unique_ptr<Clock> clock) {
|
||||||
clock_ = std::move(clock);
|
clock_ = std::move(clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,21 +86,21 @@ class MpdBuilder {
|
||||||
// Returns the document pointer to the MPD. This must be freed by the caller
|
// Returns the document pointer to the MPD. This must be freed by the caller
|
||||||
// using appropriate xmlDocPtr freeing function.
|
// using appropriate xmlDocPtr freeing function.
|
||||||
// On failure, this returns NULL.
|
// On failure, this returns NULL.
|
||||||
base::Optional<xml::XmlNode> GenerateMpd();
|
std::optional<xml::XmlNode> GenerateMpd();
|
||||||
|
|
||||||
// Set MPD attributes common to all profiles. Uses non-zero |mpd_options_| to
|
// Set MPD attributes common to all profiles. Uses non-zero |mpd_options_| to
|
||||||
// set attributes for the MPD.
|
// set attributes for the MPD.
|
||||||
bool AddCommonMpdInfo(xml::XmlNode* mpd_node) WARN_UNUSED_RESULT;
|
[[nodiscard]] bool AddCommonMpdInfo(xml::XmlNode* mpd_node);
|
||||||
|
|
||||||
// Adds 'static' MPD attributes and elements to |mpd_node|. This assumes that
|
// Adds 'static' MPD attributes and elements to |mpd_node|. This assumes that
|
||||||
// the first child element is a Period element.
|
// the first child element is a Period element.
|
||||||
bool AddStaticMpdInfo(xml::XmlNode* mpd_node) WARN_UNUSED_RESULT;
|
[[nodiscard]] bool AddStaticMpdInfo(xml::XmlNode* mpd_node);
|
||||||
|
|
||||||
// Same as AddStaticMpdInfo() but for 'dynamic' MPDs.
|
// Same as AddStaticMpdInfo() but for 'dynamic' MPDs.
|
||||||
bool AddDynamicMpdInfo(xml::XmlNode* mpd_node) WARN_UNUSED_RESULT;
|
[[nodiscard]] bool AddDynamicMpdInfo(xml::XmlNode* mpd_node);
|
||||||
|
|
||||||
// Add UTCTiming element if utc timing is provided.
|
// Add UTCTiming element if utc timing is provided.
|
||||||
bool AddUtcTiming(xml::XmlNode* mpd_node) WARN_UNUSED_RESULT;
|
[[nodiscard]] bool AddUtcTiming(xml::XmlNode* mpd_node);
|
||||||
|
|
||||||
float GetStaticMpdDuration();
|
float GetStaticMpdDuration();
|
||||||
|
|
||||||
|
@ -109,7 +110,7 @@ class MpdBuilder {
|
||||||
|
|
||||||
// Gets the earliest, normalized segment timestamp. Returns true if
|
// Gets the earliest, normalized segment timestamp. Returns true if
|
||||||
// successful, false otherwise.
|
// successful, false otherwise.
|
||||||
bool GetEarliestTimestamp(double* timestamp_seconds) WARN_UNUSED_RESULT;
|
[[nodiscard]] bool GetEarliestTimestamp(double* timestamp_seconds);
|
||||||
|
|
||||||
// Update Period durations and presentation timestamps.
|
// Update Period durations and presentation timestamps.
|
||||||
void UpdatePeriodDurationAndPresentationTimestamp();
|
void UpdatePeriodDurationAndPresentationTimestamp();
|
||||||
|
@ -125,7 +126,7 @@ class MpdBuilder {
|
||||||
|
|
||||||
// By default, this returns the current time. This can be injected for
|
// By default, this returns the current time. This can be injected for
|
||||||
// testing.
|
// testing.
|
||||||
std::unique_ptr<base::Clock> clock_;
|
std::unique_ptr<Clock> clock_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -7,31 +7,21 @@
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "packager/mpd/base/adaptation_set.h"
|
#include "packager/mpd/base/adaptation_set.h"
|
||||||
#include "packager/mpd/base/mpd_builder.h"
|
#include "packager/mpd/base/mpd_builder.h"
|
||||||
#include "packager/mpd/base/period.h"
|
#include "packager/mpd/base/period.h"
|
||||||
#include "packager/mpd/base/representation.h"
|
#include "packager/mpd/base/representation.h"
|
||||||
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||||
|
#include "packager/utils/clock.h"
|
||||||
|
#include "packager/utils/test_clock.h"
|
||||||
#include "packager/version/version.h"
|
#include "packager/version/version.h"
|
||||||
|
|
||||||
using ::testing::HasSubstr;
|
using ::testing::HasSubstr;
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class TestClock : public base::Clock {
|
|
||||||
public:
|
|
||||||
explicit TestClock(const base::Time& t) : time_(t) {}
|
|
||||||
~TestClock() override {}
|
|
||||||
base::Time Now() override { return time_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
base::Time time_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
template <DashProfile profile>
|
template <DashProfile profile>
|
||||||
class MpdBuilderTest : public ::testing::Test {
|
class MpdBuilderTest : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
|
@ -117,17 +107,8 @@ class LiveMpdBuilderTest : public MpdBuilderTest<DashProfile::kLive> {
|
||||||
|
|
||||||
// Injects a clock that always returns 2016 Jan 11 15:10:24 in UTC.
|
// Injects a clock that always returns 2016 Jan 11 15:10:24 in UTC.
|
||||||
void InjectTestClock() {
|
void InjectTestClock() {
|
||||||
base::Time::Exploded test_time = { 2016, // year.
|
mpd_.InjectClockForTesting(
|
||||||
1, // month
|
std::make_unique<TestClock>("2016-01-11T15:10:24"));
|
||||||
1, // day_of_week = Monday.
|
|
||||||
11, // day_of_month
|
|
||||||
15, // hour.
|
|
||||||
10, // minute.
|
|
||||||
24, // second.
|
|
||||||
0 }; // millisecond.
|
|
||||||
ASSERT_TRUE(test_time.HasValidValues());
|
|
||||||
mpd_.InjectClockForTesting(std::unique_ptr<base::Clock>(
|
|
||||||
new TestClock(base::Time::FromUTCExploded(test_time))));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
|
|
||||||
#include "packager/mpd/base/mpd_notifier_util.h"
|
#include "packager/mpd/base/mpd_notifier_util.h"
|
||||||
|
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "glog/logging.h"
|
||||||
#include "packager/base/strings/string_util.h"
|
|
||||||
#include "packager/file/file.h"
|
#include "packager/file/file.h"
|
||||||
#include "packager/mpd/base/mpd_utils.h"
|
#include "packager/mpd/base/mpd_utils.h"
|
||||||
|
|
||||||
|
@ -49,7 +48,7 @@ ContentType GetContentType(const MediaInfo& media_info) {
|
||||||
std::string Uint8VectorToBase64(const std::vector<uint8_t>& input) {
|
std::string Uint8VectorToBase64(const std::vector<uint8_t>& input) {
|
||||||
std::string output;
|
std::string output;
|
||||||
std::string input_in_string(input.begin(), input.end());
|
std::string input_in_string(input.begin(), input.end());
|
||||||
base::Base64Encode(input_in_string, &output);
|
absl::Base64Escape(input_in_string, &output);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/base64.h"
|
#include <absl/strings/escaping.h>
|
||||||
#include "packager/mpd/base/media_info.pb.h"
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
#include "packager/mpd/base/mpd_builder.h"
|
#include "packager/mpd/base/mpd_builder.h"
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
|
|
||||||
#include "packager/mpd/base/mpd_utils.h"
|
#include "packager/mpd/base/mpd_utils.h"
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <absl/flags/flag.h>
|
||||||
|
#include <absl/strings/escaping.h>
|
||||||
|
#include <absl/strings/numbers.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
#include "packager/base/base64.h"
|
#include "absl/strings/str_format.h"
|
||||||
#include "packager/base/logging.h"
|
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
|
||||||
#include "packager/base/strings/string_util.h"
|
|
||||||
#include "packager/media/base/language_utils.h"
|
#include "packager/media/base/language_utils.h"
|
||||||
#include "packager/media/base/protection_system_specific_info.h"
|
#include "packager/media/base/protection_system_specific_info.h"
|
||||||
#include "packager/mpd/base/adaptation_set.h"
|
#include "packager/mpd/base/adaptation_set.h"
|
||||||
|
@ -20,7 +20,8 @@
|
||||||
#include "packager/mpd/base/representation.h"
|
#include "packager/mpd/base/representation.h"
|
||||||
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
||||||
|
|
||||||
DEFINE_bool(
|
ABSL_FLAG(
|
||||||
|
bool,
|
||||||
use_legacy_vp9_codec_string,
|
use_legacy_vp9_codec_string,
|
||||||
false,
|
false,
|
||||||
"Use legacy vp9 codec string 'vp9' if set to true; otherwise new style "
|
"Use legacy vp9 codec string 'vp9' if set to true; otherwise new style "
|
||||||
|
@ -107,7 +108,7 @@ std::string GetCodecs(const MediaInfo& media_info) {
|
||||||
// new codec strings.
|
// new codec strings.
|
||||||
if (codec == "vp08")
|
if (codec == "vp08")
|
||||||
return "vp8";
|
return "vp8";
|
||||||
if (FLAGS_use_legacy_vp9_codec_string) {
|
if (absl::GetFlag(FLAGS_use_legacy_vp9_codec_string)) {
|
||||||
if (codec == "vp09")
|
if (codec == "vp09")
|
||||||
return "vp9";
|
return "vp9";
|
||||||
}
|
}
|
||||||
|
@ -121,7 +122,7 @@ std::string GetCodecs(const MediaInfo& media_info) {
|
||||||
if (media_info.has_text_info())
|
if (media_info.has_text_info())
|
||||||
return TextCodecString(media_info);
|
return TextCodecString(media_info);
|
||||||
|
|
||||||
NOTREACHED();
|
NOTIMPLEMENTED();
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,13 +188,30 @@ std::string GetAdaptationSetKey(const MediaInfo& media_info,
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string FloatToXmlString(double number) {
|
||||||
|
// Keep up to microsecond accuracy but trim trailing 0s
|
||||||
|
std::string formatted = absl::StrFormat("%.6g", number);
|
||||||
|
size_t decimalPos = formatted.find('.');
|
||||||
|
if (decimalPos != std::string::npos) {
|
||||||
|
size_t lastNonZeroPos = formatted.find_last_not_of('0');
|
||||||
|
if (lastNonZeroPos >= decimalPos) {
|
||||||
|
formatted.erase(lastNonZeroPos + 1);
|
||||||
|
}
|
||||||
|
if (formatted.back() == '.') {
|
||||||
|
formatted.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
|
||||||
std::string SecondsToXmlDuration(double seconds) {
|
std::string SecondsToXmlDuration(double seconds) {
|
||||||
// Chrome internally uses time accurate to microseconds, which is implemented
|
// Chrome internally uses time accurate to microseconds, which is implemented
|
||||||
// per MSE spec (https://www.w3.org/TR/media-source/).
|
// per MSE spec (https://www.w3.org/TR/media-source/).
|
||||||
// We need a string formatter that has at least microseconds accuracy for a
|
// We need a string formatter that has at least microseconds accuracy for a
|
||||||
// normal video (with duration up to 3 hours). Chrome's DoubleToString
|
// normal video (with duration up to 3 hours). FloatToXmlString
|
||||||
// implementation meets the requirement.
|
// implementation meets the requirement.
|
||||||
return "PT" + base::DoubleToString(seconds) + "S";
|
return absl::StrFormat("PT%sS", FloatToXmlString(seconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetDurationAttribute(xmlNodePtr node, float* duration) {
|
bool GetDurationAttribute(xmlNodePtr node, float* duration) {
|
||||||
|
@ -207,7 +225,7 @@ bool GetDurationAttribute(xmlNodePtr node, float* duration) {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
double duration_double_precision = 0.0;
|
double duration_double_precision = 0.0;
|
||||||
if (!base::StringToDouble(reinterpret_cast<const char*>(duration_value.get()),
|
if (!absl::SimpleAtod(reinterpret_cast<const char*>(duration_value.get()),
|
||||||
&duration_double_precision)) {
|
&duration_double_precision)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -235,35 +253,28 @@ bool HexToUUID(const std::string& data, std::string* uuid_format) {
|
||||||
if (data.size() != kExpectedUUIDSize) {
|
if (data.size() != kExpectedUUIDSize) {
|
||||||
LOG(ERROR) << "UUID size is expected to be " << kExpectedUUIDSize
|
LOG(ERROR) << "UUID size is expected to be " << kExpectedUUIDSize
|
||||||
<< " but is " << data.size() << " and the data in hex is "
|
<< " but is " << data.size() << " and the data in hex is "
|
||||||
<< base::HexEncode(data.data(), data.size());
|
<< absl::BytesToHexString(data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string hex_encoded =
|
const std::string hex_encoded =
|
||||||
base::ToLowerASCII(base::HexEncode(data.data(), data.size()));
|
absl::AsciiStrToLower(absl::BytesToHexString(data));
|
||||||
DCHECK_EQ(hex_encoded.size(), kExpectedUUIDSize * 2);
|
DCHECK_EQ(hex_encoded.size(), kExpectedUUIDSize * 2);
|
||||||
base::StringPiece all(hex_encoded);
|
std::string_view all(hex_encoded);
|
||||||
// Note UUID has 5 parts separated with dashes.
|
// Note UUID has 5 parts separated with dashes.
|
||||||
// e.g. 123e4567-e89b-12d3-a456-426655440000
|
// e.g. 123e4567-e89b-12d3-a456-426655440000
|
||||||
// These StringPieces have each part.
|
// These StringPieces have each part.
|
||||||
base::StringPiece first = all.substr(0, 8);
|
std::string_view first = all.substr(0, 8);
|
||||||
base::StringPiece second = all.substr(8, 4);
|
std::string_view second = all.substr(8, 4);
|
||||||
base::StringPiece third = all.substr(12, 4);
|
std::string_view third = all.substr(12, 4);
|
||||||
base::StringPiece fourth = all.substr(16, 4);
|
std::string_view fourth = all.substr(16, 4);
|
||||||
base::StringPiece fifth = all.substr(20, 12);
|
std::string_view fifth = all.substr(20, 12);
|
||||||
|
|
||||||
// 32 hexadecimal characters with 4 hyphens.
|
// 32 hexadecimal characters with 4 hyphens.
|
||||||
const size_t kHumanReadableUUIDSize = 36;
|
const size_t kHumanReadableUUIDSize = 36;
|
||||||
uuid_format->reserve(kHumanReadableUUIDSize);
|
uuid_format->reserve(kHumanReadableUUIDSize);
|
||||||
first.CopyToString(uuid_format);
|
absl::StrAppendFormat(uuid_format, "%s-%s-%s-%s-%s", first, second, third,
|
||||||
uuid_format->append("-");
|
fourth, fifth);
|
||||||
second.AppendToString(uuid_format);
|
|
||||||
uuid_format->append("-");
|
|
||||||
third.AppendToString(uuid_format);
|
|
||||||
uuid_format->append("-");
|
|
||||||
fourth.AppendToString(uuid_format);
|
|
||||||
uuid_format->append("-");
|
|
||||||
fifth.AppendToString(uuid_format);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,8 +338,9 @@ const char kMarlinUUID[] = "5e629af5-38da-4063-8977-97ffbd9902d4";
|
||||||
const char kFairPlayUUID[] = "29701fe4-3cc7-4a34-8c5b-ae90c7439a47";
|
const char kFairPlayUUID[] = "29701fe4-3cc7-4a34-8c5b-ae90c7439a47";
|
||||||
// String representation of media::kPlayReadySystemId.
|
// String representation of media::kPlayReadySystemId.
|
||||||
const char kPlayReadyUUID[] = "9a04f079-9840-4286-ab92-e65be0885f95";
|
const char kPlayReadyUUID[] = "9a04f079-9840-4286-ab92-e65be0885f95";
|
||||||
// It is RECOMMENDED to include the @value attribute with name and version "MSPR 2.0".
|
// It is RECOMMENDED to include the @value attribute with name and version
|
||||||
// See https://docs.microsoft.com/en-us/playready/specifications/mpeg-dash-playready#221-general.
|
// "MSPR 2.0". See
|
||||||
|
// https://docs.microsoft.com/en-us/playready/specifications/mpeg-dash-playready#221-general.
|
||||||
const char kContentProtectionValueMSPR20[] = "MSPR 2.0";
|
const char kContentProtectionValueMSPR20[] = "MSPR 2.0";
|
||||||
|
|
||||||
Element GenerateMarlinContentIds(const std::string& key_id) {
|
Element GenerateMarlinContentIds(const std::string& key_id) {
|
||||||
|
@ -341,7 +353,7 @@ Element GenerateMarlinContentIds(const std::string& key_id) {
|
||||||
marlin_content_id.name = kMarlinContentIdName;
|
marlin_content_id.name = kMarlinContentIdName;
|
||||||
marlin_content_id.content =
|
marlin_content_id.content =
|
||||||
kMarlinContentIdPrefix +
|
kMarlinContentIdPrefix +
|
||||||
base::ToLowerASCII(base::HexEncode(key_id.data(), key_id.size()));
|
absl::AsciiStrToLower(absl::BytesToHexString(key_id));
|
||||||
|
|
||||||
Element marlin_content_ids;
|
Element marlin_content_ids;
|
||||||
marlin_content_ids.name = kMarlinContentIdsName;
|
marlin_content_ids.name = kMarlinContentIdsName;
|
||||||
|
@ -352,7 +364,7 @@ Element GenerateMarlinContentIds(const std::string& key_id) {
|
||||||
|
|
||||||
Element GenerateCencPsshElement(const std::string& pssh) {
|
Element GenerateCencPsshElement(const std::string& pssh) {
|
||||||
std::string base64_encoded_pssh;
|
std::string base64_encoded_pssh;
|
||||||
base::Base64Encode(base::StringPiece(pssh.data(), pssh.size()),
|
absl::Base64Escape(std::string_view(pssh.data(), pssh.size()),
|
||||||
&base64_encoded_pssh);
|
&base64_encoded_pssh);
|
||||||
Element cenc_pssh;
|
Element cenc_pssh;
|
||||||
cenc_pssh.name = kPsshElementName;
|
cenc_pssh.name = kPsshElementName;
|
||||||
|
@ -371,12 +383,10 @@ Element GenerateMsprProElement(const std::string& pssh) {
|
||||||
|
|
||||||
const std::vector<uint8_t> *p_pssh = &b->pssh_data();
|
const std::vector<uint8_t> *p_pssh = &b->pssh_data();
|
||||||
std::string base64_encoded_mspr;
|
std::string base64_encoded_mspr;
|
||||||
base::Base64Encode(
|
absl::Base64Escape(
|
||||||
base::StringPiece(
|
std::string_view(reinterpret_cast<const char*>(p_pssh->data()),
|
||||||
reinterpret_cast<const char*>(p_pssh->data()),
|
|
||||||
p_pssh->size()),
|
p_pssh->size()),
|
||||||
&base64_encoded_mspr
|
&base64_encoded_mspr);
|
||||||
);
|
|
||||||
Element mspr_pro;
|
Element mspr_pro;
|
||||||
mspr_pro.name = kMsproElementName;
|
mspr_pro.name = kMsproElementName;
|
||||||
mspr_pro.content = base64_encoded_mspr;
|
mspr_pro.content = base64_encoded_mspr;
|
||||||
|
@ -440,7 +450,7 @@ void AddContentProtectionElementsHelperTemplated(
|
||||||
} else if (entry.uuid() == kMarlinUUID) {
|
} else if (entry.uuid() == kMarlinUUID) {
|
||||||
// Marlin requires its uuid to be in upper case. See #525 for details.
|
// Marlin requires its uuid to be in upper case. See #525 for details.
|
||||||
drm_content_protection.scheme_id_uri =
|
drm_content_protection.scheme_id_uri =
|
||||||
"urn:uuid:" + base::ToUpperASCII(entry.uuid());
|
"urn:uuid:" + absl::AsciiStrToUpper(entry.uuid());
|
||||||
drm_content_protection.subelements.push_back(
|
drm_content_protection.subelements.push_back(
|
||||||
GenerateMarlinContentIds(protected_content.default_key_id()));
|
GenerateMarlinContentIds(protected_content.default_key_id()));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -50,6 +50,8 @@ std::string GetBaseCodec(const MediaInfo& media_info);
|
||||||
// Returns a key made from the characteristics that separate AdaptationSets.
|
// Returns a key made from the characteristics that separate AdaptationSets.
|
||||||
std::string GetAdaptationSetKey(const MediaInfo& media_info, bool ignore_codec);
|
std::string GetAdaptationSetKey(const MediaInfo& media_info, bool ignore_codec);
|
||||||
|
|
||||||
|
std::string FloatToXmlString(double number);
|
||||||
|
|
||||||
std::string SecondsToXmlDuration(double seconds);
|
std::string SecondsToXmlDuration(double seconds);
|
||||||
|
|
||||||
// Tries to get "duration" attribute from |node|. On success |duration| is set.
|
// Tries to get "duration" attribute from |node|. On success |duration| is set.
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://developers.google.com/open-source/licenses/bsd
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
#include "packager/mpd/base/mpd_utils.h"
|
#include <absl/strings/escaping.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "absl/types/span.h"
|
||||||
#include "packager/mpd/base/adaptation_set.h"
|
#include "packager/mpd/base/adaptation_set.h"
|
||||||
#include "packager/mpd/base/mpd_options.h"
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
|
#include "packager/mpd/base/mpd_utils.h"
|
||||||
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||||
#include "packager/mpd/test/xml_compare.h"
|
#include "packager/mpd/test/xml_compare.h"
|
||||||
|
|
||||||
|
@ -122,8 +122,8 @@ TEST_F(MpdUtilsTest, ContentProtectionPlayReadyCencMspr) {
|
||||||
"98404286AB92E65BE0885F9500000001"
|
"98404286AB92E65BE0885F9500000001"
|
||||||
"11223344556677889900AABBCCDDEEFF"
|
"11223344556677889900AABBCCDDEEFF"
|
||||||
"0000000430313233");
|
"0000000430313233");
|
||||||
std::vector<uint8_t> pssh;
|
|
||||||
base::HexStringToBytes(pssh_str, &pssh);
|
std::string pssh = absl::HexStringToBytes(pssh_str);
|
||||||
|
|
||||||
const char kMediaInfoWithContentProtection[] =
|
const char kMediaInfoWithContentProtection[] =
|
||||||
"video_info {"
|
"video_info {"
|
||||||
|
@ -179,8 +179,13 @@ TEST_F(MpdUtilsTest, ContentProtectionPlayReadyCenc) {
|
||||||
"98404286AB92E65BE0885F9500000001"
|
"98404286AB92E65BE0885F9500000001"
|
||||||
"11223344556677889900AABBCCDDEEFF"
|
"11223344556677889900AABBCCDDEEFF"
|
||||||
"0000000430313233");
|
"0000000430313233");
|
||||||
std::vector<uint8_t> pssh;
|
|
||||||
base::HexStringToBytes(pssh_str, &pssh);
|
std::string pssh_hex_str = absl::HexStringToBytes(pssh_str);
|
||||||
|
absl::string_view pssh_str_view(pssh_hex_str);
|
||||||
|
absl::Span<const uint8_t> span(
|
||||||
|
reinterpret_cast<const uint8_t*>(pssh_str_view.data()),
|
||||||
|
pssh_str_view.size());
|
||||||
|
std::vector<uint8_t> pssh = std::vector<uint8_t>(span.begin(), span.end());
|
||||||
|
|
||||||
const char kMediaInfoWithContentProtection[] =
|
const char kMediaInfoWithContentProtection[] =
|
||||||
"video_info {"
|
"video_info {"
|
||||||
|
@ -219,10 +224,12 @@ TEST_F(MpdUtilsTest, ContentProtectionPlayReadyCenc) {
|
||||||
" <ContentProtection"
|
" <ContentProtection"
|
||||||
" schemeIdUri='urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95'>"
|
" schemeIdUri='urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95'>"
|
||||||
" <cenc:pssh>"
|
" <cenc:pssh>"
|
||||||
"AAAAOHBzc2gBAAAAmgTweZhAQoarkuZb4IhflQAAAAERIjNEVWZ3iJkAqrvM3e7/AAAABDAxMjM="
|
"AAAAOHBzc2gBAAAAmgTweZhAQoarkuZb4IhflQAAAAERIjNEVWZ3iJkAqrvM3e7/"
|
||||||
|
"AAAABDAxMjM="
|
||||||
" </cenc:pssh>"
|
" </cenc:pssh>"
|
||||||
" </ContentProtection>"
|
" </ContentProtection>"
|
||||||
" <Representation id='0' bandwidth='0' codecs='avc1' mimeType='video/mp4'/>"
|
" <Representation id='0' bandwidth='0' codecs='avc1' "
|
||||||
|
"mimeType='video/mp4'/>"
|
||||||
"</AdaptationSet>";
|
"</AdaptationSet>";
|
||||||
|
|
||||||
EXPECT_THAT(adaptation_set_.GetXml(), XmlNodeEqual(kExpectedOutput));
|
EXPECT_THAT(adaptation_set_.GetXml(), XmlNodeEqual(kExpectedOutput));
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
// https://developers.google.com/open-source/licenses/bsd
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
#include "packager/mpd/base/period.h"
|
#include "packager/mpd/base/period.h"
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
#include "packager/base/stl_util.h"
|
|
||||||
#include "packager/mpd/base/adaptation_set.h"
|
#include "packager/mpd/base/adaptation_set.h"
|
||||||
#include "packager/mpd/base/mpd_options.h"
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
#include "packager/mpd/base/mpd_utils.h"
|
#include "packager/mpd/base/mpd_utils.h"
|
||||||
|
@ -122,7 +122,7 @@ AdaptationSet* Period::GetOrCreateAdaptationSet(
|
||||||
return adaptation_set_ptr;
|
return adaptation_set_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
base::Optional<xml::XmlNode> Period::GetXml(bool output_period_duration) {
|
std::optional<xml::XmlNode> Period::GetXml(bool output_period_duration) {
|
||||||
adaptation_sets_.sort(
|
adaptation_sets_.sort(
|
||||||
[](const std::unique_ptr<AdaptationSet>& adaptation_set_a,
|
[](const std::unique_ptr<AdaptationSet>& adaptation_set_a,
|
||||||
const std::unique_ptr<AdaptationSet>& adaptation_set_b) {
|
const std::unique_ptr<AdaptationSet>& adaptation_set_b) {
|
||||||
|
@ -137,45 +137,45 @@ base::Optional<xml::XmlNode> Period::GetXml(bool output_period_duration) {
|
||||||
|
|
||||||
// Required for 'dynamic' MPDs.
|
// Required for 'dynamic' MPDs.
|
||||||
if (!period.SetId(id_))
|
if (!period.SetId(id_))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
// Required for LL-DASH MPDs.
|
// Required for LL-DASH MPDs.
|
||||||
if (mpd_options_.mpd_params.low_latency_dash_mode) {
|
if (mpd_options_.mpd_params.low_latency_dash_mode) {
|
||||||
// Create ServiceDescription element.
|
// Create ServiceDescription element.
|
||||||
xml::XmlNode service_description_node("ServiceDescription");
|
xml::XmlNode service_description_node("ServiceDescription");
|
||||||
if (!service_description_node.SetIntegerAttribute("id", id_))
|
if (!service_description_node.SetIntegerAttribute("id", id_))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
// Insert Latency into ServiceDescription element.
|
// Insert Latency into ServiceDescription element.
|
||||||
xml::XmlNode latency_node("Latency");
|
xml::XmlNode latency_node("Latency");
|
||||||
uint64_t target_latency_ms =
|
uint64_t target_latency_ms =
|
||||||
mpd_options_.mpd_params.target_latency_seconds * 1000;
|
mpd_options_.mpd_params.target_latency_seconds * 1000;
|
||||||
if (!latency_node.SetIntegerAttribute("target", target_latency_ms))
|
if (!latency_node.SetIntegerAttribute("target", target_latency_ms))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
if (!service_description_node.AddChild(std::move(latency_node)))
|
if (!service_description_node.AddChild(std::move(latency_node)))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
// Insert ServiceDescription into Period element.
|
// Insert ServiceDescription into Period element.
|
||||||
if (!period.AddChild(std::move(service_description_node)))
|
if (!period.AddChild(std::move(service_description_node)))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate thru AdaptationSets and add them to one big Period element.
|
// Iterate thru AdaptationSets and add them to one big Period element.
|
||||||
for (const auto& adaptation_set : adaptation_sets_) {
|
for (const auto& adaptation_set : adaptation_sets_) {
|
||||||
auto child = adaptation_set->GetXml();
|
auto child = adaptation_set->GetXml();
|
||||||
if (!child || !period.AddChild(std::move(*child)))
|
if (!child || !period.AddChild(std::move(*child)))
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output_period_duration) {
|
if (output_period_duration) {
|
||||||
if (!period.SetStringAttribute("duration",
|
if (!period.SetStringAttribute("duration",
|
||||||
SecondsToXmlDuration(duration_seconds_))) {
|
SecondsToXmlDuration(duration_seconds_))) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
} else if (mpd_options_.mpd_type == MpdType::kDynamic) {
|
} else if (mpd_options_.mpd_type == MpdType::kDynamic) {
|
||||||
if (!period.SetStringAttribute(
|
if (!period.SetStringAttribute(
|
||||||
"start", SecondsToXmlDuration(start_time_in_seconds_))) {
|
"start", SecondsToXmlDuration(start_time_in_seconds_))) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return period;
|
return period;
|
||||||
|
@ -320,7 +320,8 @@ std::string Period::GetAdaptationSetKeyForTrickPlay(
|
||||||
void Period::ProtectedAdaptationSetMap::Register(
|
void Period::ProtectedAdaptationSetMap::Register(
|
||||||
const AdaptationSet& adaptation_set,
|
const AdaptationSet& adaptation_set,
|
||||||
const MediaInfo& media_info) {
|
const MediaInfo& media_info) {
|
||||||
DCHECK(!ContainsKey(protected_content_map_, &adaptation_set));
|
CHECK(protected_content_map_.find(&adaptation_set) ==
|
||||||
|
protected_content_map_.end());
|
||||||
protected_content_map_[&adaptation_set] = media_info.protected_content();
|
protected_content_map_[&adaptation_set] = media_info.protected_content();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "packager/base/optional.h"
|
#include <optional>
|
||||||
#include "packager/mpd/base/adaptation_set.h"
|
#include "packager/mpd/base/adaptation_set.h"
|
||||||
#include "packager/mpd/base/media_info.pb.h"
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
#include "packager/mpd/base/xml/xml_node.h"
|
#include "packager/mpd/base/xml/xml_node.h"
|
||||||
|
@ -46,7 +46,7 @@ class Period {
|
||||||
/// Generates <Period> xml element with its child AdaptationSet elements.
|
/// Generates <Period> xml element with its child AdaptationSet elements.
|
||||||
/// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a
|
/// @return On success returns a non-NULL scoped_xml_ptr. Otherwise returns a
|
||||||
/// NULL scoped_xml_ptr.
|
/// NULL scoped_xml_ptr.
|
||||||
base::Optional<xml::XmlNode> GetXml(bool output_period_duration);
|
std::optional<xml::XmlNode> GetXml(bool output_period_duration);
|
||||||
|
|
||||||
/// @return The list of AdaptationSets in this Period.
|
/// @return The list of AdaptationSets in this Period.
|
||||||
const std::list<AdaptationSet*> GetAdaptationSets() const;
|
const std::list<AdaptationSet*> GetAdaptationSets() const;
|
||||||
|
|
|
@ -6,12 +6,11 @@
|
||||||
|
|
||||||
#include "packager/mpd/base/representation.h"
|
#include "packager/mpd/base/representation.h"
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <absl/flags/declare.h>
|
||||||
|
#include <absl/strings/str_format.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
|
||||||
#include "packager/base/strings/stringprintf.h"
|
|
||||||
#include "packager/file/file.h"
|
#include "packager/file/file.h"
|
||||||
#include "packager/media/base/muxer_util.h"
|
#include "packager/media/base/muxer_util.h"
|
||||||
#include "packager/mpd/base/mpd_options.h"
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
|
@ -249,10 +248,10 @@ const MediaInfo& Representation::GetMediaInfo() const {
|
||||||
// AddVideoInfo() (possibly adds FramePacking elements), AddAudioInfo() (Adds
|
// AddVideoInfo() (possibly adds FramePacking elements), AddAudioInfo() (Adds
|
||||||
// AudioChannelConfig elements), AddContentProtectionElements*(), and
|
// AudioChannelConfig elements), AddContentProtectionElements*(), and
|
||||||
// AddVODOnlyInfo() (Adds segment info).
|
// AddVODOnlyInfo() (Adds segment info).
|
||||||
base::Optional<xml::XmlNode> Representation::GetXml() {
|
std::optional<xml::XmlNode> Representation::GetXml() {
|
||||||
if (!HasRequiredMediaInfoFields()) {
|
if (!HasRequiredMediaInfoFields()) {
|
||||||
LOG(ERROR) << "MediaInfo missing required fields.";
|
LOG(ERROR) << "MediaInfo missing required fields.";
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64_t bandwidth = media_info_.has_bandwidth()
|
const uint64_t bandwidth = media_info_.has_bandwidth()
|
||||||
|
@ -268,7 +267,7 @@ base::Optional<xml::XmlNode> Representation::GetXml() {
|
||||||
!(codecs_.empty() ||
|
!(codecs_.empty() ||
|
||||||
representation.SetStringAttribute("codecs", codecs_)) ||
|
representation.SetStringAttribute("codecs", codecs_)) ||
|
||||||
!representation.SetStringAttribute("mimeType", mime_type_)) {
|
!representation.SetStringAttribute("mimeType", mime_type_)) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool has_video_info = media_info_.has_video_info();
|
const bool has_video_info = media_info_.has_video_info();
|
||||||
|
@ -281,18 +280,18 @@ base::Optional<xml::XmlNode> Representation::GetXml() {
|
||||||
!(output_suppression_flags_ & kSuppressHeight),
|
!(output_suppression_flags_ & kSuppressHeight),
|
||||||
!(output_suppression_flags_ & kSuppressFrameRate))) {
|
!(output_suppression_flags_ & kSuppressFrameRate))) {
|
||||||
LOG(ERROR) << "Failed to add video info to Representation XML.";
|
LOG(ERROR) << "Failed to add video info to Representation XML.";
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_audio_info &&
|
if (has_audio_info &&
|
||||||
!representation.AddAudioInfo(media_info_.audio_info())) {
|
!representation.AddAudioInfo(media_info_.audio_info())) {
|
||||||
LOG(ERROR) << "Failed to add audio info to Representation XML.";
|
LOG(ERROR) << "Failed to add audio info to Representation XML.";
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!representation.AddContentProtectionElements(
|
if (!representation.AddContentProtectionElements(
|
||||||
content_protection_elements_)) {
|
content_protection_elements_)) {
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasVODOnlyFields(media_info_) &&
|
if (HasVODOnlyFields(media_info_) &&
|
||||||
|
@ -300,7 +299,7 @@ base::Optional<xml::XmlNode> Representation::GetXml() {
|
||||||
media_info_, mpd_options_.mpd_params.use_segment_list,
|
media_info_, mpd_options_.mpd_params.use_segment_list,
|
||||||
mpd_options_.mpd_params.target_segment_duration)) {
|
mpd_options_.mpd_params.target_segment_duration)) {
|
||||||
LOG(ERROR) << "Failed to add VOD info.";
|
LOG(ERROR) << "Failed to add VOD info.";
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasLiveOnlyFields(media_info_) &&
|
if (HasLiveOnlyFields(media_info_) &&
|
||||||
|
@ -308,13 +307,13 @@ base::Optional<xml::XmlNode> Representation::GetXml() {
|
||||||
media_info_, segment_infos_, start_number_,
|
media_info_, segment_infos_, start_number_,
|
||||||
mpd_options_.mpd_params.low_latency_dash_mode)) {
|
mpd_options_.mpd_params.low_latency_dash_mode)) {
|
||||||
LOG(ERROR) << "Failed to add Live info.";
|
LOG(ERROR) << "Failed to add Live info.";
|
||||||
return base::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
// TODO(rkuroiwa): It is likely that all representations have the exact same
|
// TODO(rkuroiwa): It is likely that all representations have the exact same
|
||||||
// SegmentTemplate. Optimize and propagate the tag up to AdaptationSet level.
|
// SegmentTemplate. Optimize and propagate the tag up to AdaptationSet level.
|
||||||
|
|
||||||
output_suppression_flags_ = 0;
|
output_suppression_flags_ = 0;
|
||||||
return std::move(representation);
|
return representation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Representation::SuppressOnce(SuppressFlag flag) {
|
void Representation::SuppressOnce(SuppressFlag flag) {
|
||||||
|
@ -567,24 +566,24 @@ std::string Representation::GetTextMimeType() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Representation::RepresentationAsString() const {
|
std::string Representation::RepresentationAsString() const {
|
||||||
std::string s = base::StringPrintf("Representation (id=%d,", id_);
|
std::string s = absl::StrFormat("Representation (id=%d,", id_);
|
||||||
if (media_info_.has_video_info()) {
|
if (media_info_.has_video_info()) {
|
||||||
const MediaInfo_VideoInfo& video_info = media_info_.video_info();
|
const MediaInfo_VideoInfo& video_info = media_info_.video_info();
|
||||||
base::StringAppendF(&s, "codec='%s',width=%d,height=%d",
|
absl::StrAppendFormat(&s, "codec='%s',width=%d,height=%d",
|
||||||
video_info.codec().c_str(), video_info.width(),
|
video_info.codec().c_str(), video_info.width(),
|
||||||
video_info.height());
|
video_info.height());
|
||||||
} else if (media_info_.has_audio_info()) {
|
} else if (media_info_.has_audio_info()) {
|
||||||
const MediaInfo_AudioInfo& audio_info = media_info_.audio_info();
|
const MediaInfo_AudioInfo& audio_info = media_info_.audio_info();
|
||||||
base::StringAppendF(
|
absl::StrAppendFormat(
|
||||||
&s, "codec='%s',frequency=%d,language='%s'", audio_info.codec().c_str(),
|
&s, "codec='%s',frequency=%d,language='%s'", audio_info.codec().c_str(),
|
||||||
audio_info.sampling_frequency(), audio_info.language().c_str());
|
audio_info.sampling_frequency(), audio_info.language().c_str());
|
||||||
} else if (media_info_.has_text_info()) {
|
} else if (media_info_.has_text_info()) {
|
||||||
const MediaInfo_TextInfo& text_info = media_info_.text_info();
|
const MediaInfo_TextInfo& text_info = media_info_.text_info();
|
||||||
base::StringAppendF(&s, "codec='%s',language='%s'",
|
absl::StrAppendFormat(&s, "codec='%s',language='%s'",
|
||||||
text_info.codec().c_str(),
|
text_info.codec().c_str(),
|
||||||
text_info.language().c_str());
|
text_info.language().c_str());
|
||||||
}
|
}
|
||||||
base::StringAppendF(&s, ")");
|
absl::StrAppendFormat(&s, ")");
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "packager/base/optional.h"
|
|
||||||
#include "packager/mpd/base/bandwidth_estimator.h"
|
#include "packager/mpd/base/bandwidth_estimator.h"
|
||||||
#include "packager/mpd/base/media_info.pb.h"
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
#include "packager/mpd/base/segment_info.h"
|
#include "packager/mpd/base/segment_info.h"
|
||||||
|
@ -125,7 +125,7 @@ class Representation {
|
||||||
virtual const MediaInfo& GetMediaInfo() const;
|
virtual const MediaInfo& GetMediaInfo() const;
|
||||||
|
|
||||||
/// @return Copy of <Representation>.
|
/// @return Copy of <Representation>.
|
||||||
base::Optional<xml::XmlNode> GetXml();
|
std::optional<xml::XmlNode> GetXml();
|
||||||
|
|
||||||
/// By calling this methods, the next time GetXml() is
|
/// By calling this methods, the next time GetXml() is
|
||||||
/// called, the corresponding attributes will not be set.
|
/// called, the corresponding attributes will not be set.
|
||||||
|
|
|
@ -6,14 +6,16 @@
|
||||||
|
|
||||||
#include "packager/mpd/base/representation.h"
|
#include "packager/mpd/base/representation.h"
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <absl/flags/declare.h>
|
||||||
|
#include <absl/flags/flag.h>
|
||||||
|
#include <absl/strings/str_format.h>
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "packager/base/strings/stringprintf.h"
|
|
||||||
#include "packager/file/file.h"
|
#include "packager/file/file.h"
|
||||||
#include "packager/file/file_closer.h"
|
#include "packager/file/file_closer.h"
|
||||||
|
#include "packager/flag_saver.h"
|
||||||
#include "packager/mpd/base/mpd_options.h"
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||||
#include "packager/mpd/test/xml_compare.h"
|
#include "packager/mpd/test/xml_compare.h"
|
||||||
|
@ -23,7 +25,7 @@ using ::testing::Not;
|
||||||
using ::testing::Values;
|
using ::testing::Values;
|
||||||
using ::testing::WithParamInterface;
|
using ::testing::WithParamInterface;
|
||||||
|
|
||||||
DECLARE_bool(use_legacy_vp9_codec_string);
|
ABSL_DECLARE_FLAG(bool, use_legacy_vp9_codec_string);
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -234,7 +236,8 @@ TEST_F(RepresentationTest, CheckVideoInfoVp9CodecInWebm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RepresentationTest, CheckVideoInfoLegacyVp9CodecInWebm) {
|
TEST_F(RepresentationTest, CheckVideoInfoLegacyVp9CodecInWebm) {
|
||||||
FLAGS_use_legacy_vp9_codec_string = true;
|
FlagSaver<bool> saver(&FLAGS_use_legacy_vp9_codec_string);
|
||||||
|
absl::SetFlag(&FLAGS_use_legacy_vp9_codec_string, true);
|
||||||
|
|
||||||
const char kTestMediaInfoCodecVp9[] =
|
const char kTestMediaInfoCodecVp9[] =
|
||||||
"video_info {\n"
|
"video_info {\n"
|
||||||
|
@ -435,7 +438,7 @@ std::string GetDefaultMediaInfo() {
|
||||||
"init_segment_url: 'init.mp4'\n"
|
"init_segment_url: 'init.mp4'\n"
|
||||||
"segment_template_url: '$Time$.mp4'\n";
|
"segment_template_url: '$Time$.mp4'\n";
|
||||||
|
|
||||||
return base::StringPrintf(kMediaInfo, kDefaultTimeScale);
|
return absl::StrFormat(kMediaInfo, kDefaultTimeScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -472,10 +475,10 @@ class SegmentTemplateTest : public RepresentationTest {
|
||||||
|
|
||||||
if (repeat == 0) {
|
if (repeat == 0) {
|
||||||
expected_s_elements_ +=
|
expected_s_elements_ +=
|
||||||
base::StringPrintf(kSElementTemplateWithoutR, start_time, duration);
|
absl::StrFormat(kSElementTemplateWithoutR, start_time, duration);
|
||||||
} else {
|
} else {
|
||||||
expected_s_elements_ +=
|
expected_s_elements_ +=
|
||||||
base::StringPrintf(kSElementTemplate, start_time, duration, repeat);
|
absl::StrFormat(kSElementTemplate, start_time, duration, repeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < repeat + 1; ++i) {
|
for (int i = 0; i < repeat + 1; ++i) {
|
||||||
|
@ -511,7 +514,7 @@ class SegmentTemplateTest : public RepresentationTest {
|
||||||
" </SegmentTimeline>\n"
|
" </SegmentTimeline>\n"
|
||||||
" </SegmentTemplate>\n"
|
" </SegmentTemplate>\n"
|
||||||
"</Representation>\n";
|
"</Representation>\n";
|
||||||
return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Max(),
|
return absl::StrFormat(kOutputTemplate, bandwidth_estimator_.Max(),
|
||||||
expected_s_elements_.c_str());
|
expected_s_elements_.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -768,7 +771,7 @@ class SegmentTimelineTestBase : public SegmentTemplateTest {
|
||||||
"init_segment_url: 'init.mp4'\n"
|
"init_segment_url: 'init.mp4'\n"
|
||||||
"segment_template_url: '$Number$.mp4'\n";
|
"segment_template_url: '$Number$.mp4'\n";
|
||||||
const std::string& number_template_media_info =
|
const std::string& number_template_media_info =
|
||||||
base::StringPrintf(kMediaInfo, kDefaultTimeScale);
|
absl::StrFormat(kMediaInfo, kDefaultTimeScale);
|
||||||
mpd_options_.mpd_type = MpdType::kDynamic;
|
mpd_options_.mpd_type = MpdType::kDynamic;
|
||||||
mpd_options_.mpd_params.target_segment_duration =
|
mpd_options_.mpd_params.target_segment_duration =
|
||||||
kTargetSegmentDurationInSeconds;
|
kTargetSegmentDurationInSeconds;
|
||||||
|
@ -794,9 +797,8 @@ class SegmentTimelineTestBase : public SegmentTemplateTest {
|
||||||
" </SegmentTemplate>\n"
|
" </SegmentTemplate>\n"
|
||||||
"</Representation>\n";
|
"</Representation>\n";
|
||||||
|
|
||||||
return base::StringPrintf(kOutputTemplate, bandwidth_estimator_.Max(),
|
return absl::StrFormat(kOutputTemplate, bandwidth_estimator_.Max(),
|
||||||
expected_start_number,
|
expected_start_number, expected_s_element.c_str());
|
||||||
expected_s_element.c_str());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -829,11 +831,11 @@ TEST_P(ApproximateSegmentTimelineTest, SegmentDurationAdjusted) {
|
||||||
|
|
||||||
std::string expected_s_elements;
|
std::string expected_s_elements;
|
||||||
if (allow_approximate_segment_timeline_) {
|
if (allow_approximate_segment_timeline_) {
|
||||||
expected_s_elements = base::StringPrintf(
|
expected_s_elements = absl::StrFormat(kSElementTemplateWithoutR, kStartTime,
|
||||||
kSElementTemplateWithoutR, kStartTime, kScaledTargetSegmentDuration);
|
kScaledTargetSegmentDuration);
|
||||||
} else {
|
} else {
|
||||||
expected_s_elements = base::StringPrintf(kSElementTemplateWithoutR,
|
expected_s_elements = absl::StrFormat(kSElementTemplateWithoutR, kStartTime,
|
||||||
kStartTime, kDurationSmaller);
|
kDurationSmaller);
|
||||||
}
|
}
|
||||||
EXPECT_THAT(representation_->GetXml(),
|
EXPECT_THAT(representation_->GetXml(),
|
||||||
XmlNodeEqual(ExpectedXml(expected_s_elements)));
|
XmlNodeEqual(ExpectedXml(expected_s_elements)));
|
||||||
|
@ -850,11 +852,11 @@ TEST_P(ApproximateSegmentTimelineTest,
|
||||||
|
|
||||||
std::string expected_s_elements;
|
std::string expected_s_elements;
|
||||||
if (allow_approximate_segment_timeline_) {
|
if (allow_approximate_segment_timeline_) {
|
||||||
expected_s_elements = base::StringPrintf(
|
expected_s_elements = absl::StrFormat(kSElementTemplateWithoutR, kStartTime,
|
||||||
kSElementTemplateWithoutR, kStartTime, kScaledTargetSegmentDuration);
|
kScaledTargetSegmentDuration);
|
||||||
} else {
|
} else {
|
||||||
expected_s_elements = base::StringPrintf(kSElementTemplateWithoutR,
|
expected_s_elements = absl::StrFormat(kSElementTemplateWithoutR, kStartTime,
|
||||||
kStartTime, kDurationSmaller);
|
kDurationSmaller);
|
||||||
}
|
}
|
||||||
EXPECT_THAT(representation_->GetXml(),
|
EXPECT_THAT(representation_->GetXml(),
|
||||||
XmlNodeEqual(ExpectedXml(expected_s_elements)));
|
XmlNodeEqual(ExpectedXml(expected_s_elements)));
|
||||||
|
@ -876,15 +878,15 @@ TEST_P(ApproximateSegmentTimelineTest, SegmentsWithSimilarDurations) {
|
||||||
if (allow_approximate_segment_timeline_) {
|
if (allow_approximate_segment_timeline_) {
|
||||||
int kNumSegments = 3;
|
int kNumSegments = 3;
|
||||||
expected_s_elements =
|
expected_s_elements =
|
||||||
base::StringPrintf(kSElementTemplate, kStartTime,
|
absl::StrFormat(kSElementTemplate, kStartTime,
|
||||||
kScaledTargetSegmentDuration, kNumSegments - 1);
|
kScaledTargetSegmentDuration, kNumSegments - 1);
|
||||||
} else {
|
} else {
|
||||||
expected_s_elements =
|
expected_s_elements =
|
||||||
base::StringPrintf(kSElementTemplateWithoutR, kStartTime,
|
absl::StrFormat(kSElementTemplateWithoutR, kStartTime,
|
||||||
kDurationSmaller) +
|
kDurationSmaller) +
|
||||||
base::StringPrintf(kSElementTemplateWithoutR,
|
absl::StrFormat(kSElementTemplateWithoutR,
|
||||||
kStartTime + kDurationSmaller, kDurationLarger) +
|
kStartTime + kDurationSmaller, kDurationLarger) +
|
||||||
base::StringPrintf(kSElementTemplateWithoutR,
|
absl::StrFormat(kSElementTemplateWithoutR,
|
||||||
kStartTime + kDurationSmaller + kDurationLarger,
|
kStartTime + kDurationSmaller + kDurationLarger,
|
||||||
kDurationSmaller);
|
kDurationSmaller);
|
||||||
}
|
}
|
||||||
|
@ -911,7 +913,7 @@ TEST_P(ApproximateSegmentTimelineTest, SegmentsWithSimilarDurations2) {
|
||||||
"<S t=\"20\" d=\"13\"/>";
|
"<S t=\"20\" d=\"13\"/>";
|
||||||
} else {
|
} else {
|
||||||
int kNumSegments = 3;
|
int kNumSegments = 3;
|
||||||
expected_s_elements = base::StringPrintf(kSElementTemplate, kStartTime,
|
expected_s_elements = absl::StrFormat(kSElementTemplate, kStartTime,
|
||||||
kDurationLarger, kNumSegments - 1);
|
kDurationLarger, kNumSegments - 1);
|
||||||
}
|
}
|
||||||
EXPECT_THAT(representation_->GetXml(),
|
EXPECT_THAT(representation_->GetXml(),
|
||||||
|
@ -930,12 +932,12 @@ TEST_P(ApproximateSegmentTimelineTest, FillSmallGap) {
|
||||||
std::string expected_s_elements;
|
std::string expected_s_elements;
|
||||||
if (allow_approximate_segment_timeline_) {
|
if (allow_approximate_segment_timeline_) {
|
||||||
int kNumSegments = 3;
|
int kNumSegments = 3;
|
||||||
expected_s_elements = base::StringPrintf(kSElementTemplate, kStartTime,
|
expected_s_elements = absl::StrFormat(kSElementTemplate, kStartTime,
|
||||||
kDuration, kNumSegments - 1);
|
kDuration, kNumSegments - 1);
|
||||||
} else {
|
} else {
|
||||||
expected_s_elements =
|
expected_s_elements =
|
||||||
base::StringPrintf(kSElementTemplateWithoutR, kStartTime, kDuration) +
|
absl::StrFormat(kSElementTemplateWithoutR, kStartTime, kDuration) +
|
||||||
base::StringPrintf(kSElementTemplate, kStartTime + kDuration + kGap,
|
absl::StrFormat(kSElementTemplate, kStartTime + kDuration + kGap,
|
||||||
kDuration, 1 /* repeat */);
|
kDuration, 1 /* repeat */);
|
||||||
}
|
}
|
||||||
EXPECT_THAT(representation_->GetXml(),
|
EXPECT_THAT(representation_->GetXml(),
|
||||||
|
@ -954,12 +956,12 @@ TEST_P(ApproximateSegmentTimelineTest, FillSmallOverlap) {
|
||||||
std::string expected_s_elements;
|
std::string expected_s_elements;
|
||||||
if (allow_approximate_segment_timeline_) {
|
if (allow_approximate_segment_timeline_) {
|
||||||
int kNumSegments = 3;
|
int kNumSegments = 3;
|
||||||
expected_s_elements = base::StringPrintf(kSElementTemplate, kStartTime,
|
expected_s_elements = absl::StrFormat(kSElementTemplate, kStartTime,
|
||||||
kDuration, kNumSegments - 1);
|
kDuration, kNumSegments - 1);
|
||||||
} else {
|
} else {
|
||||||
expected_s_elements =
|
expected_s_elements =
|
||||||
base::StringPrintf(kSElementTemplateWithoutR, kStartTime, kDuration) +
|
absl::StrFormat(kSElementTemplateWithoutR, kStartTime, kDuration) +
|
||||||
base::StringPrintf(kSElementTemplate, kStartTime + kDuration - kOverlap,
|
absl::StrFormat(kSElementTemplate, kStartTime + kDuration - kOverlap,
|
||||||
kDuration, 1 /* repeat */);
|
kDuration, 1 /* repeat */);
|
||||||
}
|
}
|
||||||
EXPECT_THAT(representation_->GetXml(),
|
EXPECT_THAT(representation_->GetXml(),
|
||||||
|
@ -1045,7 +1047,7 @@ TEST_P(TimeShiftBufferDepthTest, Normal) {
|
||||||
const int kExpectedRepeatsLeft = kTimeShiftBufferDepth;
|
const int kExpectedRepeatsLeft = kTimeShiftBufferDepth;
|
||||||
const int kExpectedStartNumber = kRepeat - kExpectedRepeatsLeft + 1;
|
const int kExpectedStartNumber = kRepeat - kExpectedRepeatsLeft + 1;
|
||||||
|
|
||||||
const std::string expected_s_element = base::StringPrintf(
|
const std::string expected_s_element = absl::StrFormat(
|
||||||
kSElementTemplate,
|
kSElementTemplate,
|
||||||
initial_start_time_ + kDuration * (kRepeat - kExpectedRepeatsLeft),
|
initial_start_time_ + kDuration * (kRepeat - kExpectedRepeatsLeft),
|
||||||
kDuration, kExpectedRepeatsLeft);
|
kDuration, kExpectedRepeatsLeft);
|
||||||
|
@ -1071,7 +1073,7 @@ TEST_P(TimeShiftBufferDepthTest, TimeShiftBufferDepthShorterThanSegmentLength) {
|
||||||
|
|
||||||
AddSegments(initial_start_time_, kDuration, kSize, kRepeat);
|
AddSegments(initial_start_time_, kDuration, kSize, kRepeat);
|
||||||
|
|
||||||
const std::string expected_s_element = base::StringPrintf(
|
const std::string expected_s_element = absl::StrFormat(
|
||||||
kSElementTemplate, initial_start_time_, kDuration, kRepeat);
|
kSElementTemplate, initial_start_time_, kDuration, kRepeat);
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
representation_->GetXml(),
|
representation_->GetXml(),
|
||||||
|
@ -1102,7 +1104,7 @@ TEST_P(TimeShiftBufferDepthTest, Generic) {
|
||||||
|
|
||||||
// Expect only the latest S element with 2 segments.
|
// Expect only the latest S element with 2 segments.
|
||||||
const std::string expected_s_element =
|
const std::string expected_s_element =
|
||||||
base::StringPrintf(kSElementTemplate, first_s_element_end_time,
|
absl::StrFormat(kSElementTemplate, first_s_element_end_time,
|
||||||
kTimeShiftBufferDepthDuration, kMoreSegmentsRepeat);
|
kTimeShiftBufferDepthDuration, kMoreSegmentsRepeat);
|
||||||
|
|
||||||
const int kExpectedRemovedSegments = kRepeat + 1;
|
const int kExpectedRemovedSegments = kRepeat + 1;
|
||||||
|
@ -1140,12 +1142,12 @@ TEST_P(TimeShiftBufferDepthTest, MoreThanOneS) {
|
||||||
(kOneSecondSegmentRepeat + 1 + kTwoSecondSegmentRepeat * 2) -
|
(kOneSecondSegmentRepeat + 1 + kTwoSecondSegmentRepeat * 2) -
|
||||||
kTimeShiftBufferDepth;
|
kTimeShiftBufferDepth;
|
||||||
|
|
||||||
std::string expected_s_element = base::StringPrintf(
|
std::string expected_s_element = absl::StrFormat(
|
||||||
kSElementTemplate,
|
kSElementTemplate,
|
||||||
initial_start_time_ + kOneSecondDuration * kExpectedRemovedSegments,
|
initial_start_time_ + kOneSecondDuration * kExpectedRemovedSegments,
|
||||||
kOneSecondDuration, kOneSecondSegmentRepeat - kExpectedRemovedSegments);
|
kOneSecondDuration, kOneSecondSegmentRepeat - kExpectedRemovedSegments);
|
||||||
expected_s_element +=
|
expected_s_element +=
|
||||||
base::StringPrintf(kSElementTemplate, first_s_element_end_time,
|
absl::StrFormat(kSElementTemplate, first_s_element_end_time,
|
||||||
kTwoSecondDuration, kTwoSecondSegmentRepeat);
|
kTwoSecondDuration, kTwoSecondSegmentRepeat);
|
||||||
|
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
|
@ -1182,13 +1184,13 @@ TEST_P(TimeShiftBufferDepthTest, UseLastSegmentInS) {
|
||||||
AddSegments(first_s_element_end_time, kTwoSecondDuration, kSize,
|
AddSegments(first_s_element_end_time, kTwoSecondDuration, kSize,
|
||||||
kTwoSecondSegmentRepeat);
|
kTwoSecondSegmentRepeat);
|
||||||
|
|
||||||
std::string expected_s_element = base::StringPrintf(
|
std::string expected_s_element = absl::StrFormat(
|
||||||
kSElementTemplateWithoutR,
|
kSElementTemplateWithoutR,
|
||||||
initial_start_time_ + kDuration1, // Expect one segment removed.
|
initial_start_time_ + kDuration1, // Expect one segment removed.
|
||||||
kDuration1);
|
kDuration1);
|
||||||
|
|
||||||
expected_s_element +=
|
expected_s_element +=
|
||||||
base::StringPrintf(kSElementTemplate, first_s_element_end_time,
|
absl::StrFormat(kSElementTemplate, first_s_element_end_time,
|
||||||
kTwoSecondDuration, kTwoSecondSegmentRepeat);
|
kTwoSecondDuration, kTwoSecondSegmentRepeat);
|
||||||
EXPECT_THAT(representation_->GetXml(),
|
EXPECT_THAT(representation_->GetXml(),
|
||||||
XmlNodeEqual(ExpectedXml(expected_s_element, 2)));
|
XmlNodeEqual(ExpectedXml(expected_s_element, 2)));
|
||||||
|
@ -1216,9 +1218,9 @@ TEST_P(TimeShiftBufferDepthTest, NormalGap) {
|
||||||
const int64_t gap_s_element_start_time = first_s_element_end_time + 1;
|
const int64_t gap_s_element_start_time = first_s_element_end_time + 1;
|
||||||
AddSegments(gap_s_element_start_time, kDuration, kSize, /* no repeat */ 0);
|
AddSegments(gap_s_element_start_time, kDuration, kSize, /* no repeat */ 0);
|
||||||
|
|
||||||
std::string expected_s_element = base::StringPrintf(
|
std::string expected_s_element = absl::StrFormat(
|
||||||
kSElementTemplate, initial_start_time_, kDuration, kRepeat);
|
kSElementTemplate, initial_start_time_, kDuration, kRepeat);
|
||||||
expected_s_element += base::StringPrintf(kSElementTemplateWithoutR,
|
expected_s_element += absl::StrFormat(kSElementTemplateWithoutR,
|
||||||
gap_s_element_start_time, kDuration);
|
gap_s_element_start_time, kDuration);
|
||||||
|
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
|
@ -1252,9 +1254,9 @@ TEST_P(TimeShiftBufferDepthTest, HugeGap) {
|
||||||
kSecondSElementRepeat);
|
kSecondSElementRepeat);
|
||||||
|
|
||||||
std::string expected_s_element =
|
std::string expected_s_element =
|
||||||
base::StringPrintf(kSElementTemplateWithoutR,
|
absl::StrFormat(kSElementTemplateWithoutR,
|
||||||
initial_start_time_ + kRepeat * kDuration, kDuration) +
|
initial_start_time_ + kRepeat * kDuration, kDuration) +
|
||||||
base::StringPrintf(kSElementTemplate, gap_s_element_start_time, kDuration,
|
absl::StrFormat(kSElementTemplate, gap_s_element_start_time, kDuration,
|
||||||
kSecondSElementRepeat);
|
kSecondSElementRepeat);
|
||||||
const int kExpectedRemovedSegments = kRepeat;
|
const int kExpectedRemovedSegments = kRepeat;
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
|
@ -1282,7 +1284,7 @@ TEST_P(TimeShiftBufferDepthTest, ManySegments) {
|
||||||
const int kExpectedStartNumber =
|
const int kExpectedStartNumber =
|
||||||
kDefaultStartNumber + kExpectedRemovedSegments;
|
kDefaultStartNumber + kExpectedRemovedSegments;
|
||||||
|
|
||||||
std::string expected_s_element = base::StringPrintf(
|
std::string expected_s_element = absl::StrFormat(
|
||||||
kSElementTemplate,
|
kSElementTemplate,
|
||||||
initial_start_time_ + kExpectedRemovedSegments * kDuration, kDuration,
|
initial_start_time_ + kExpectedRemovedSegments * kDuration, kDuration,
|
||||||
kExpectedSegmentsRepeat);
|
kExpectedSegmentsRepeat);
|
||||||
|
@ -1318,8 +1320,8 @@ class RepresentationDeleteSegmentsTest : public SegmentTimelineTestBase {
|
||||||
|
|
||||||
// Create 100 files with the template.
|
// Create 100 files with the template.
|
||||||
for (int i = 1; i <= 100; ++i) {
|
for (int i = 1; i <= 100; ++i) {
|
||||||
File::WriteStringToFile(
|
File::WriteStringToFile(absl::StrFormat(kStringPrintTemplate, i).c_str(),
|
||||||
base::StringPrintf(kStringPrintTemplate, i).c_str(), "dummy content");
|
"dummy content");
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaInfo media_info = ConvertToMediaInfo(GetDefaultMediaInfo());
|
MediaInfo media_info = ConvertToMediaInfo(GetDefaultMediaInfo());
|
||||||
|
@ -1348,8 +1350,7 @@ TEST_F(RepresentationDeleteSegmentsTest, NoSegmentsDeletedInitially) {
|
||||||
AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat);
|
AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < kMaxNumSegmentsAvailable; ++i) {
|
for (int i = 0; i < kMaxNumSegmentsAvailable; ++i) {
|
||||||
EXPECT_FALSE(
|
EXPECT_FALSE(SegmentDeleted(absl::StrFormat(kStringPrintTemplate, i + 1)));
|
||||||
SegmentDeleted(base::StringPrintf(kStringPrintTemplate, i + 1)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1357,8 +1358,8 @@ TEST_F(RepresentationDeleteSegmentsTest, OneSegmentDeleted) {
|
||||||
for (int i = 0; i <= kMaxNumSegmentsAvailable; ++i) {
|
for (int i = 0; i <= kMaxNumSegmentsAvailable; ++i) {
|
||||||
AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat);
|
AddSegments(kInitialStartTime + i * kDuration, kDuration, kSize, kNoRepeat);
|
||||||
}
|
}
|
||||||
EXPECT_FALSE(SegmentDeleted(base::StringPrintf(kStringPrintTemplate, 2)));
|
EXPECT_FALSE(SegmentDeleted(absl::StrFormat(kStringPrintTemplate, 2)));
|
||||||
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(kStringPrintTemplate, 1)));
|
EXPECT_TRUE(SegmentDeleted(absl::StrFormat(kStringPrintTemplate, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that segments are deleted as expected with many non-repeating
|
// Verify that segments are deleted as expected with many non-repeating
|
||||||
|
@ -1371,9 +1372,9 @@ TEST_F(RepresentationDeleteSegmentsTest, ManyNonRepeatingSegments) {
|
||||||
const int last_available_segment_index =
|
const int last_available_segment_index =
|
||||||
many_segments - kMaxNumSegmentsAvailable + 1;
|
many_segments - kMaxNumSegmentsAvailable + 1;
|
||||||
EXPECT_FALSE(SegmentDeleted(
|
EXPECT_FALSE(SegmentDeleted(
|
||||||
base::StringPrintf(kStringPrintTemplate, last_available_segment_index)));
|
absl::StrFormat(kStringPrintTemplate, last_available_segment_index)));
|
||||||
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(
|
EXPECT_TRUE(SegmentDeleted(
|
||||||
kStringPrintTemplate, last_available_segment_index - 1)));
|
absl::StrFormat(kStringPrintTemplate, last_available_segment_index - 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that segments are deleted as expected with many repeating segments.
|
// Verify that segments are deleted as expected with many repeating segments.
|
||||||
|
@ -1388,9 +1389,9 @@ TEST_F(RepresentationDeleteSegmentsTest, ManyRepeatingSegments) {
|
||||||
const int last_available_segment_index =
|
const int last_available_segment_index =
|
||||||
kNumSegments - kMaxNumSegmentsAvailable + 1;
|
kNumSegments - kMaxNumSegmentsAvailable + 1;
|
||||||
EXPECT_FALSE(SegmentDeleted(
|
EXPECT_FALSE(SegmentDeleted(
|
||||||
base::StringPrintf(kStringPrintTemplate, last_available_segment_index)));
|
absl::StrFormat(kStringPrintTemplate, last_available_segment_index)));
|
||||||
EXPECT_TRUE(SegmentDeleted(base::StringPrintf(
|
EXPECT_TRUE(SegmentDeleted(
|
||||||
kStringPrintTemplate, last_available_segment_index - 1)));
|
absl::StrFormat(kStringPrintTemplate, last_available_segment_index - 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
|
|
||||||
#include "packager/mpd/base/simple_mpd_notifier.h"
|
#include "packager/mpd/base/simple_mpd_notifier.h"
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
#include <glog/logging.h>
|
||||||
#include "packager/base/stl_util.h"
|
|
||||||
#include "packager/mpd/base/adaptation_set.h"
|
#include "packager/mpd/base/adaptation_set.h"
|
||||||
#include "packager/mpd/base/mpd_builder.h"
|
#include "packager/mpd/base/mpd_builder.h"
|
||||||
#include "packager/mpd/base/mpd_notifier_util.h"
|
#include "packager/mpd/base/mpd_notifier_util.h"
|
||||||
|
@ -44,7 +43,7 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
||||||
MediaInfo adjusted_media_info(media_info);
|
MediaInfo adjusted_media_info(media_info);
|
||||||
MpdBuilder::MakePathsRelativeToMpd(output_path_, &adjusted_media_info);
|
MpdBuilder::MakePathsRelativeToMpd(output_path_, &adjusted_media_info);
|
||||||
|
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock auto_lock(&lock_);
|
||||||
const double kPeriodStartTimeSeconds = 0.0;
|
const double kPeriodStartTimeSeconds = 0.0;
|
||||||
Period* period = mpd_builder_->GetOrCreatePeriod(kPeriodStartTimeSeconds);
|
Period* period = mpd_builder_->GetOrCreatePeriod(kPeriodStartTimeSeconds);
|
||||||
DCHECK(period);
|
DCHECK(period);
|
||||||
|
@ -72,7 +71,7 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimpleMpdNotifier::NotifyAvailabilityTimeOffset(uint32_t container_id) {
|
bool SimpleMpdNotifier::NotifyAvailabilityTimeOffset(uint32_t container_id) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto it = representation_map_.find(container_id);
|
auto it = representation_map_.find(container_id);
|
||||||
if (it == representation_map_.end()) {
|
if (it == representation_map_.end()) {
|
||||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||||
|
@ -84,7 +83,7 @@ bool SimpleMpdNotifier::NotifyAvailabilityTimeOffset(uint32_t container_id) {
|
||||||
|
|
||||||
bool SimpleMpdNotifier::NotifySampleDuration(uint32_t container_id,
|
bool SimpleMpdNotifier::NotifySampleDuration(uint32_t container_id,
|
||||||
int32_t sample_duration) {
|
int32_t sample_duration) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto it = representation_map_.find(container_id);
|
auto it = representation_map_.find(container_id);
|
||||||
if (it == representation_map_.end()) {
|
if (it == representation_map_.end()) {
|
||||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||||
|
@ -95,7 +94,7 @@ bool SimpleMpdNotifier::NotifySampleDuration(uint32_t container_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimpleMpdNotifier::NotifySegmentDuration(uint32_t container_id) {
|
bool SimpleMpdNotifier::NotifySegmentDuration(uint32_t container_id) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto it = representation_map_.find(container_id);
|
auto it = representation_map_.find(container_id);
|
||||||
if (it == representation_map_.end()) {
|
if (it == representation_map_.end()) {
|
||||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||||
|
@ -109,7 +108,7 @@ bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id,
|
||||||
int64_t start_time,
|
int64_t start_time,
|
||||||
int64_t duration,
|
int64_t duration,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto it = representation_map_.find(container_id);
|
auto it = representation_map_.find(container_id);
|
||||||
if (it == representation_map_.end()) {
|
if (it == representation_map_.end()) {
|
||||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||||
|
@ -122,7 +121,7 @@ bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id,
|
||||||
bool SimpleMpdNotifier::NotifyCompletedSegment(uint32_t container_id,
|
bool SimpleMpdNotifier::NotifyCompletedSegment(uint32_t container_id,
|
||||||
int64_t duration,
|
int64_t duration,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto it = representation_map_.find(container_id);
|
auto it = representation_map_.find(container_id);
|
||||||
if (it == representation_map_.end()) {
|
if (it == representation_map_.end()) {
|
||||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||||
|
@ -134,7 +133,7 @@ bool SimpleMpdNotifier::NotifyCompletedSegment(uint32_t container_id,
|
||||||
|
|
||||||
bool SimpleMpdNotifier::NotifyCueEvent(uint32_t container_id,
|
bool SimpleMpdNotifier::NotifyCueEvent(uint32_t container_id,
|
||||||
int64_t timestamp) {
|
int64_t timestamp) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto it = representation_map_.find(container_id);
|
auto it = representation_map_.find(container_id);
|
||||||
if (it == representation_map_.end()) {
|
if (it == representation_map_.end()) {
|
||||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||||
|
@ -181,7 +180,7 @@ bool SimpleMpdNotifier::NotifyEncryptionUpdate(
|
||||||
const std::string& drm_uuid,
|
const std::string& drm_uuid,
|
||||||
const std::vector<uint8_t>& new_key_id,
|
const std::vector<uint8_t>& new_key_id,
|
||||||
const std::vector<uint8_t>& new_pssh) {
|
const std::vector<uint8_t>& new_pssh) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto it = representation_map_.find(container_id);
|
auto it = representation_map_.find(container_id);
|
||||||
if (it == representation_map_.end()) {
|
if (it == representation_map_.end()) {
|
||||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||||
|
@ -202,7 +201,7 @@ bool SimpleMpdNotifier::NotifyEncryptionUpdate(
|
||||||
|
|
||||||
bool SimpleMpdNotifier::NotifyMediaInfoUpdate(uint32_t container_id,
|
bool SimpleMpdNotifier::NotifyMediaInfoUpdate(uint32_t container_id,
|
||||||
const MediaInfo& media_info) {
|
const MediaInfo& media_info) {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
auto it = representation_map_.find(container_id);
|
auto it = representation_map_.find(container_id);
|
||||||
if (it == representation_map_.end()) {
|
if (it == representation_map_.end()) {
|
||||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||||
|
@ -217,7 +216,7 @@ bool SimpleMpdNotifier::NotifyMediaInfoUpdate(uint32_t container_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimpleMpdNotifier::Flush() {
|
bool SimpleMpdNotifier::Flush() {
|
||||||
base::AutoLock auto_lock(lock_);
|
absl::MutexLock lock(&lock_);
|
||||||
return WriteMpdToFile(output_path_, mpd_builder_.get());
|
return WriteMpdToFile(output_path_, mpd_builder_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/synchronization/lock.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "packager/mpd/base/mpd_notifier.h"
|
#include "packager/mpd/base/mpd_notifier.h"
|
||||||
#include "packager/mpd/base/mpd_notifier_util.h"
|
#include "packager/mpd/base/mpd_notifier_util.h"
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ class SimpleMpdNotifier : public MpdNotifier {
|
||||||
std::string output_path_;
|
std::string output_path_;
|
||||||
std::unique_ptr<MpdBuilder> mpd_builder_;
|
std::unique_ptr<MpdBuilder> mpd_builder_;
|
||||||
bool content_protection_in_adaptation_set_ = true;
|
bool content_protection_in_adaptation_set_ = true;
|
||||||
base::Lock lock_;
|
absl::Mutex lock_;
|
||||||
|
|
||||||
uint32_t next_adaptation_set_id_ = 0;
|
uint32_t next_adaptation_set_id_ = 0;
|
||||||
// Maps Representation ID to Representation.
|
// Maps Representation ID to Representation.
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <google/protobuf/util/message_differencer.h>
|
#include <google/protobuf/util/message_differencer.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include "packager/base/files/file_path.h"
|
#include "packager/file/file_test_util.h"
|
||||||
#include "packager/base/files/file_util.h"
|
|
||||||
#include "packager/mpd/base/mock_mpd_builder.h"
|
#include "packager/mpd/base/mock_mpd_builder.h"
|
||||||
#include "packager/mpd/base/mpd_builder.h"
|
#include "packager/mpd/base/mpd_builder.h"
|
||||||
#include "packager/mpd/base/mpd_options.h"
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
|
@ -45,8 +45,7 @@ class SimpleMpdNotifierTest : public ::testing::Test {
|
||||||
default_mock_adaptation_set_(new MockAdaptationSet()) {}
|
default_mock_adaptation_set_(new MockAdaptationSet()) {}
|
||||||
|
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_));
|
empty_mpd_option_.mpd_params.mpd_output = temp_file_.path();
|
||||||
empty_mpd_option_.mpd_params.mpd_output = temp_file_path_.AsUTF8Unsafe();
|
|
||||||
|
|
||||||
// Three valid media info. The actual data does not matter.
|
// Three valid media info. The actual data does not matter.
|
||||||
const char kValidMediaInfo[] =
|
const char kValidMediaInfo[] =
|
||||||
|
@ -68,9 +67,7 @@ class SimpleMpdNotifierTest : public ::testing::Test {
|
||||||
valid_media_info3_.mutable_video_info()->set_width(480);
|
valid_media_info3_.mutable_video_info()->set_width(480);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override {}
|
||||||
base::DeleteFile(temp_file_path_, false /* non recursive, just 1 file */);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetMpdBuilder(SimpleMpdNotifier* notifier,
|
void SetMpdBuilder(SimpleMpdNotifier* notifier,
|
||||||
std::unique_ptr<MpdBuilder> mpd_builder) {
|
std::unique_ptr<MpdBuilder> mpd_builder) {
|
||||||
|
@ -93,7 +90,7 @@ class SimpleMpdNotifierTest : public ::testing::Test {
|
||||||
MediaInfo valid_media_info3_;
|
MediaInfo valid_media_info3_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
base::FilePath temp_file_path_;
|
TempFile temp_file_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Verify NotifyNewContainer() works as expected for VOD.
|
// Verify NotifyNewContainer() works as expected for VOD.
|
||||||
|
@ -329,7 +326,7 @@ TEST_F(SimpleMpdNotifierTest,
|
||||||
0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e,
|
0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e,
|
||||||
0x67, 0x65, 0x6c, 0x73, 0x65};
|
0x67, 0x65, 0x6c, 0x73, 0x65};
|
||||||
const std::vector<uint8_t> kBogusNewPsshVector(
|
const std::vector<uint8_t> kBogusNewPsshVector(
|
||||||
kBogusNewPssh, kBogusNewPssh + arraysize(kBogusNewPssh));
|
kBogusNewPssh, kBogusNewPssh + std::size(kBogusNewPssh));
|
||||||
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
|
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
|
||||||
|
|
||||||
EXPECT_CALL(*default_mock_adaptation_set_,
|
EXPECT_CALL(*default_mock_adaptation_set_,
|
||||||
|
@ -372,7 +369,7 @@ TEST_F(SimpleMpdNotifierTest,
|
||||||
0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e,
|
0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e,
|
||||||
0x67, 0x65, 0x6c, 0x73, 0x65};
|
0x67, 0x65, 0x6c, 0x73, 0x65};
|
||||||
const std::vector<uint8_t> kBogusNewPsshVector(
|
const std::vector<uint8_t> kBogusNewPsshVector(
|
||||||
kBogusNewPssh, kBogusNewPssh + arraysize(kBogusNewPssh));
|
kBogusNewPssh, kBogusNewPssh + std::size(kBogusNewPssh));
|
||||||
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
|
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
|
||||||
|
|
||||||
EXPECT_CALL(*mock_representation,
|
EXPECT_CALL(*mock_representation,
|
||||||
|
|
|
@ -6,29 +6,33 @@
|
||||||
|
|
||||||
#include "packager/mpd/base/xml/xml_node.h"
|
#include "packager/mpd/base/xml/xml_node.h"
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <absl/base/internal/endian.h>
|
||||||
|
#include <absl/flags/flag.h>
|
||||||
|
#include <absl/strings/numbers.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
#include "absl/strings/escaping.h"
|
||||||
#include "packager/base/macros.h"
|
#include "absl/strings/str_format.h"
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "packager/macros.h"
|
||||||
#include "packager/base/sys_byteorder.h"
|
|
||||||
#include "packager/media/base/rcheck.h"
|
#include "packager/media/base/rcheck.h"
|
||||||
#include "packager/mpd/base/media_info.pb.h"
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
#include "packager/mpd/base/mpd_utils.h"
|
#include "packager/mpd/base/mpd_utils.h"
|
||||||
#include "packager/mpd/base/segment_info.h"
|
#include "packager/mpd/base/segment_info.h"
|
||||||
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
||||||
|
|
||||||
DEFINE_bool(segment_template_constant_duration,
|
ABSL_FLAG(bool,
|
||||||
|
segment_template_constant_duration,
|
||||||
false,
|
false,
|
||||||
"Generates SegmentTemplate@duration if all segments except the "
|
"Generates SegmentTemplate@duration if all segments except the "
|
||||||
"last one has the same duration if this flag is set to true.");
|
"last one has the same duration if this flag is set to true.");
|
||||||
|
|
||||||
DEFINE_bool(dash_add_last_segment_number_when_needed,
|
ABSL_FLAG(bool,
|
||||||
|
dash_add_last_segment_number_when_needed,
|
||||||
false,
|
false,
|
||||||
"Adds a Supplemental Descriptor with @schemeIdUri "
|
"Adds a Supplemental Descriptor with @schemeIdUri "
|
||||||
"set to http://dashif.org/guidelines/last-segment-number with "
|
"set to http://dashif.org/guidelines/last-segment-number with "
|
||||||
|
@ -45,15 +49,14 @@ const char kEC3Codec[] = "ec-3";
|
||||||
const char kAC4Codec[] = "ac-4";
|
const char kAC4Codec[] = "ac-4";
|
||||||
|
|
||||||
std::string RangeToString(const Range& range) {
|
std::string RangeToString(const Range& range) {
|
||||||
return base::Uint64ToString(range.begin()) + "-" +
|
return absl::StrFormat("%u-%u", range.begin(), range.end());
|
||||||
base::Uint64ToString(range.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if segments are continuous and all segments except the last one are of
|
// Check if segments are continuous and all segments except the last one are of
|
||||||
// the same duration.
|
// the same duration.
|
||||||
bool IsTimelineConstantDuration(const std::list<SegmentInfo>& segment_infos,
|
bool IsTimelineConstantDuration(const std::list<SegmentInfo>& segment_infos,
|
||||||
uint32_t start_number) {
|
uint32_t start_number) {
|
||||||
if (!FLAGS_segment_template_constant_duration)
|
if (!absl::GetFlag(FLAGS_segment_template_constant_duration))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DCHECK(!segment_infos.empty());
|
DCHECK(!segment_infos.empty());
|
||||||
|
@ -145,7 +148,7 @@ bool XmlNode::AddChild(XmlNode child) {
|
||||||
|
|
||||||
// Reaching here means the ownership of |child| transfered to |node|.
|
// Reaching here means the ownership of |child| transfered to |node|.
|
||||||
// Release the pointer so that it doesn't get destructed in this scope.
|
// Release the pointer so that it doesn't get destructed in this scope.
|
||||||
ignore_result(child.impl_->node.release());
|
UNUSED(child.impl_->node.release());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,14 +195,15 @@ bool XmlNode::SetIntegerAttribute(const std::string& attribute_name,
|
||||||
uint64_t number) {
|
uint64_t number) {
|
||||||
DCHECK(impl_->node);
|
DCHECK(impl_->node);
|
||||||
return xmlSetProp(impl_->node.get(), BAD_CAST attribute_name.c_str(),
|
return xmlSetProp(impl_->node.get(), BAD_CAST attribute_name.c_str(),
|
||||||
BAD_CAST(base::Uint64ToString(number).c_str())) != nullptr;
|
BAD_CAST(absl::StrFormat("%" PRIu64, number).c_str())) !=
|
||||||
|
nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XmlNode::SetFloatingPointAttribute(const std::string& attribute_name,
|
bool XmlNode::SetFloatingPointAttribute(const std::string& attribute_name,
|
||||||
double number) {
|
double number) {
|
||||||
DCHECK(impl_->node);
|
DCHECK(impl_->node);
|
||||||
return xmlSetProp(impl_->node.get(), BAD_CAST attribute_name.c_str(),
|
return xmlSetProp(impl_->node.get(), BAD_CAST attribute_name.c_str(),
|
||||||
BAD_CAST(base::DoubleToString(number).c_str())) != nullptr;
|
BAD_CAST(FloatToXmlString(number).c_str())) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XmlNode::SetId(uint32_t id) {
|
bool XmlNode::SetId(uint32_t id) {
|
||||||
|
@ -345,9 +349,9 @@ bool RepresentationXmlNode::AddVideoInfo(const VideoInfo& video_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video_info.has_pixel_width() && video_info.has_pixel_height()) {
|
if (video_info.has_pixel_width() && video_info.has_pixel_height()) {
|
||||||
RCHECK(SetStringAttribute(
|
RCHECK(SetStringAttribute("sar",
|
||||||
"sar", base::IntToString(video_info.pixel_width()) + ":" +
|
absl::StrFormat("%d:%d", video_info.pixel_width(),
|
||||||
base::IntToString(video_info.pixel_height())));
|
video_info.pixel_height())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_width)
|
if (set_width)
|
||||||
|
@ -355,14 +359,14 @@ bool RepresentationXmlNode::AddVideoInfo(const VideoInfo& video_info,
|
||||||
if (set_height)
|
if (set_height)
|
||||||
RCHECK(SetIntegerAttribute("height", video_info.height()));
|
RCHECK(SetIntegerAttribute("height", video_info.height()));
|
||||||
if (set_frame_rate) {
|
if (set_frame_rate) {
|
||||||
RCHECK(SetStringAttribute(
|
RCHECK(SetStringAttribute("frameRate",
|
||||||
"frameRate", base::IntToString(video_info.time_scale()) + "/" +
|
absl::StrFormat("%d/%d", video_info.time_scale(),
|
||||||
base::IntToString(video_info.frame_duration())));
|
video_info.frame_duration())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video_info.has_playback_rate()) {
|
if (video_info.has_playback_rate()) {
|
||||||
RCHECK(SetStringAttribute("maxPlayoutRate",
|
RCHECK(SetStringAttribute(
|
||||||
base::IntToString(video_info.playback_rate())));
|
"maxPlayoutRate", absl::StrFormat("%d", video_info.playback_rate())));
|
||||||
// Since the trick play stream contains only key frames, there is no coding
|
// Since the trick play stream contains only key frames, there is no coding
|
||||||
// dependency on the main stream. Simply set the codingDependency to false.
|
// dependency on the main stream. Simply set the codingDependency to false.
|
||||||
// TODO(hmchen): propagate this attribute up to the AdaptationSet, since
|
// TODO(hmchen): propagate this attribute up to the AdaptationSet, since
|
||||||
|
@ -416,7 +420,7 @@ bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info,
|
||||||
media_info.reference_time_scale()));
|
media_info.reference_time_scale()));
|
||||||
|
|
||||||
if (use_segment_list && !use_single_segment_url_with_media) {
|
if (use_segment_list && !use_single_segment_url_with_media) {
|
||||||
const int64_t duration_seconds = static_cast<int64_t>(
|
const auto duration_seconds = static_cast<int64_t>(
|
||||||
floor(target_segment_duration * media_info.reference_time_scale()));
|
floor(target_segment_duration * media_info.reference_time_scale()));
|
||||||
RCHECK(child.SetIntegerAttribute("duration", duration_seconds));
|
RCHECK(child.SetIntegerAttribute("duration", duration_seconds));
|
||||||
}
|
}
|
||||||
|
@ -505,7 +509,7 @@ bool RepresentationXmlNode::AddLiveOnlyInfo(
|
||||||
if (IsTimelineConstantDuration(segment_infos, start_number)) {
|
if (IsTimelineConstantDuration(segment_infos, start_number)) {
|
||||||
RCHECK(segment_template.SetIntegerAttribute(
|
RCHECK(segment_template.SetIntegerAttribute(
|
||||||
"duration", segment_infos.front().duration));
|
"duration", segment_infos.front().duration));
|
||||||
if (FLAGS_dash_add_last_segment_number_when_needed) {
|
if (absl::GetFlag(FLAGS_dash_add_last_segment_number_when_needed)) {
|
||||||
uint32_t last_segment_number = start_number - 1;
|
uint32_t last_segment_number = start_number - 1;
|
||||||
for (const auto& segment_info_element : segment_infos)
|
for (const auto& segment_info_element : segment_infos)
|
||||||
last_segment_number += segment_info_element.repeat + 1;
|
last_segment_number += segment_info_element.repeat + 1;
|
||||||
|
@ -537,19 +541,18 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
|
||||||
const uint32_t ec3_channel_mpeg_value = codec_data.channel_mpeg_value();
|
const uint32_t ec3_channel_mpeg_value = codec_data.channel_mpeg_value();
|
||||||
const uint32_t NO_MAPPING = 0xFFFFFFFF;
|
const uint32_t NO_MAPPING = 0xFFFFFFFF;
|
||||||
if (ec3_channel_mpeg_value == NO_MAPPING) {
|
if (ec3_channel_mpeg_value == NO_MAPPING) {
|
||||||
// Convert EC3 channel map into string of hexadecimal digits. Spec: DASH-IF
|
// Convert EC3 channel map into string of hexadecimal digits. Spec:
|
||||||
// Interoperability Points v3.0 9.2.1.2.
|
// DASH-IF Interoperability Points v3.0 9.2.1.2.
|
||||||
const uint16_t ec3_channel_map =
|
|
||||||
base::HostToNet16(codec_data.channel_mask());
|
|
||||||
audio_channel_config_value =
|
audio_channel_config_value =
|
||||||
base::HexEncode(&ec3_channel_map, sizeof(ec3_channel_map));
|
absl::StrFormat("%04X", codec_data.channel_mask());
|
||||||
audio_channel_config_scheme =
|
audio_channel_config_scheme =
|
||||||
"tag:dolby.com,2014:dash:audio_channel_configuration:2011";
|
"tag:dolby.com,2014:dash:audio_channel_configuration:2011";
|
||||||
} else {
|
} else {
|
||||||
// Calculate EC3 channel configuration descriptor value with MPEG scheme.
|
// Calculate EC3 channel configuration descriptor value with MPEG scheme.
|
||||||
// Spec: ETSI TS 102 366 V1.4.1 Digital Audio Compression
|
// Spec: ETSI TS 102 366 V1.4.1 Digital Audio Compression
|
||||||
// (AC-3, Enhanced AC-3) I.1.2.
|
// (AC-3, Enhanced AC-3) I.1.2.
|
||||||
audio_channel_config_value = base::UintToString(ec3_channel_mpeg_value);
|
audio_channel_config_value =
|
||||||
|
absl::StrFormat("%u", ec3_channel_mpeg_value);
|
||||||
audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration";
|
audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration";
|
||||||
}
|
}
|
||||||
bool ret = AddDescriptor("AudioChannelConfiguration",
|
bool ret = AddDescriptor("AudioChannelConfiguration",
|
||||||
|
@ -560,7 +563,7 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
|
||||||
// D.2.2.
|
// D.2.2.
|
||||||
if (codec_data.ec3_joc_complexity() != 0) {
|
if (codec_data.ec3_joc_complexity() != 0) {
|
||||||
std::string ec3_joc_complexity =
|
std::string ec3_joc_complexity =
|
||||||
base::UintToString(codec_data.ec3_joc_complexity());
|
absl::StrFormat("%u", codec_data.ec3_joc_complexity());
|
||||||
ret &= AddDescriptor("SupplementalProperty",
|
ret &= AddDescriptor("SupplementalProperty",
|
||||||
"tag:dolby.com,2018:dash:EC3_ExtensionType:2018",
|
"tag:dolby.com,2018:dash:EC3_ExtensionType:2018",
|
||||||
"JOC");
|
"JOC");
|
||||||
|
@ -582,10 +585,10 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
|
||||||
// Calculate AC-4 channel mask. Spec: ETSI TS 103 190-2 V1.2.1 Digital
|
// Calculate AC-4 channel mask. Spec: ETSI TS 103 190-2 V1.2.1 Digital
|
||||||
// Audio Compression (AC-4) Standard; Part 2: Immersive and personalized
|
// Audio Compression (AC-4) Standard; Part 2: Immersive and personalized
|
||||||
// audio G.3.1.
|
// audio G.3.1.
|
||||||
const uint32_t ac4_channel_mask =
|
//
|
||||||
base::HostToNet32(codec_data.channel_mask() << 8);
|
// this needs to print only 3 bytes of the 32-bit value
|
||||||
audio_channel_config_value =
|
audio_channel_config_value =
|
||||||
base::HexEncode(&ac4_channel_mask, sizeof(ac4_channel_mask) - 1);
|
absl::StrFormat("%06X", codec_data.channel_mask());
|
||||||
// Note that the channel config schemes for EC-3 and AC-4 are different.
|
// Note that the channel config schemes for EC-3 and AC-4 are different.
|
||||||
// See https://github.com/Dash-Industry-Forum/DASH-IF-IOP/issues/268.
|
// See https://github.com/Dash-Industry-Forum/DASH-IF-IOP/issues/268.
|
||||||
audio_channel_config_scheme =
|
audio_channel_config_scheme =
|
||||||
|
@ -594,7 +597,8 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
|
||||||
// Calculate AC-4 channel configuration descriptor value with MPEG scheme.
|
// Calculate AC-4 channel configuration descriptor value with MPEG scheme.
|
||||||
// Spec: ETSI TS 103 190-2 V1.2.1 Digital Audio Compression (AC-4) Standard;
|
// Spec: ETSI TS 103 190-2 V1.2.1 Digital Audio Compression (AC-4) Standard;
|
||||||
// Part 2: Immersive and personalized audio G.3.2.
|
// Part 2: Immersive and personalized audio G.3.2.
|
||||||
audio_channel_config_value = base::UintToString(ac4_channel_mpeg_value);
|
audio_channel_config_value =
|
||||||
|
absl::StrFormat("%u", ac4_channel_mpeg_value);
|
||||||
audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration";
|
audio_channel_config_scheme = "urn:mpeg:mpegB:cicp:ChannelConfiguration";
|
||||||
}
|
}
|
||||||
bool ret = AddDescriptor("AudioChannelConfiguration",
|
bool ret = AddDescriptor("AudioChannelConfiguration",
|
||||||
|
@ -607,7 +611,8 @@ bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) {
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
audio_channel_config_value = base::UintToString(audio_info.num_channels());
|
audio_channel_config_value =
|
||||||
|
absl::StrFormat("%u", audio_info.num_channels());
|
||||||
audio_channel_config_scheme =
|
audio_channel_config_scheme =
|
||||||
"urn:mpeg:dash:23003:3:audio_channel_configuration:2011";
|
"urn:mpeg:dash:23003:3:audio_channel_configuration:2011";
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/compiler_specific.h"
|
#include "packager/macros.h"
|
||||||
#include "packager/base/macros.h"
|
|
||||||
#include "packager/mpd/base/content_protection_element.h"
|
#include "packager/mpd/base/content_protection_element.h"
|
||||||
#include "packager/mpd/base/media_info.pb.h"
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
|
|
||||||
|
@ -54,32 +53,33 @@ class XmlNode {
|
||||||
/// Add a child element to this element.
|
/// Add a child element to this element.
|
||||||
/// @param child is an XmlNode to add as a child for this element.
|
/// @param child is an XmlNode to add as a child for this element.
|
||||||
/// @return true on success, false otherwise.
|
/// @return true on success, false otherwise.
|
||||||
bool AddChild(XmlNode child) WARN_UNUSED_RESULT;
|
[[nodiscard]] bool AddChild(XmlNode child);
|
||||||
|
|
||||||
/// Adds Elements to this node using the Element struct.
|
/// Adds Elements to this node using the Element struct.
|
||||||
bool AddElements(const std::vector<Element>& elements) WARN_UNUSED_RESULT;
|
[[nodiscard]] bool AddElements(const std::vector<Element>& elements);
|
||||||
|
|
||||||
/// Set a string attribute.
|
/// Set a string attribute.
|
||||||
/// @param attribute_name The name (lhs) of the attribute.
|
/// @param attribute_name The name (lhs) of the attribute.
|
||||||
/// @param attribute The value (rhs) of the attribute.
|
/// @param attribute The value (rhs) of the attribute.
|
||||||
bool SetStringAttribute(const std::string& attribute_name,
|
[[nodiscard]] bool SetStringAttribute(const std::string& attribute_name,
|
||||||
const std::string& attribute) WARN_UNUSED_RESULT;
|
const std::string& attribute);
|
||||||
|
|
||||||
/// Sets an integer attribute.
|
/// Sets an integer attribute.
|
||||||
/// @param attribute_name The name (lhs) of the attribute.
|
/// @param attribute_name The name (lhs) of the attribute.
|
||||||
/// @param number The value (rhs) of the attribute.
|
/// @param number The value (rhs) of the attribute.
|
||||||
bool SetIntegerAttribute(const std::string& attribute_name,
|
[[nodiscard]] bool SetIntegerAttribute(const std::string& attribute_name,
|
||||||
uint64_t number) WARN_UNUSED_RESULT;
|
uint64_t number);
|
||||||
|
|
||||||
/// Set a floating point number attribute.
|
/// Set a floating point number attribute.
|
||||||
/// @param attribute_name is the name of the attribute to set.
|
/// @param attribute_name is the name of the attribute to set.
|
||||||
/// @param number is the value (rhs) of the attribute.
|
/// @param number is the value (rhs) of the attribute.
|
||||||
bool SetFloatingPointAttribute(const std::string& attribute_name,
|
[[nodiscard]] bool SetFloatingPointAttribute(
|
||||||
double number) WARN_UNUSED_RESULT;
|
const std::string& attribute_name,
|
||||||
|
double number);
|
||||||
|
|
||||||
/// Sets 'id=@a id' attribute.
|
/// Sets 'id=@a id' attribute.
|
||||||
/// @param id is the ID for this element.
|
/// @param id is the ID for this element.
|
||||||
bool SetId(uint32_t id) WARN_UNUSED_RESULT;
|
[[nodiscard]] bool SetId(uint32_t id);
|
||||||
|
|
||||||
/// Similar to SetContent, but appends to the end of existing content.
|
/// Similar to SetContent, but appends to the end of existing content.
|
||||||
void AddContent(const std::string& content);
|
void AddContent(const std::string& content);
|
||||||
|
@ -125,19 +125,18 @@ class XmlNode {
|
||||||
class RepresentationBaseXmlNode : public XmlNode {
|
class RepresentationBaseXmlNode : public XmlNode {
|
||||||
public:
|
public:
|
||||||
~RepresentationBaseXmlNode() override;
|
~RepresentationBaseXmlNode() override;
|
||||||
bool AddContentProtectionElements(
|
[[nodiscard]] bool AddContentProtectionElements(
|
||||||
const std::list<ContentProtectionElement>& content_protection_elements)
|
const std::list<ContentProtectionElement>& content_protection_elements);
|
||||||
WARN_UNUSED_RESULT;
|
|
||||||
|
|
||||||
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
||||||
/// @param value is the content of value attribute.
|
/// @param value is the content of value attribute.
|
||||||
bool AddSupplementalProperty(const std::string& scheme_id_uri,
|
[[nodiscard]] bool AddSupplementalProperty(const std::string& scheme_id_uri,
|
||||||
const std::string& value) WARN_UNUSED_RESULT;
|
const std::string& value);
|
||||||
|
|
||||||
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
||||||
/// @param value is the content of value attribute.
|
/// @param value is the content of value attribute.
|
||||||
bool AddEssentialProperty(const std::string& scheme_id_uri,
|
[[nodiscard]] bool AddEssentialProperty(const std::string& scheme_id_uri,
|
||||||
const std::string& value) WARN_UNUSED_RESULT;
|
const std::string& value);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit RepresentationBaseXmlNode(const std::string& name);
|
explicit RepresentationBaseXmlNode(const std::string& name);
|
||||||
|
@ -146,14 +145,13 @@ class RepresentationBaseXmlNode : public XmlNode {
|
||||||
/// @param descriptor_name is the name of the descriptor.
|
/// @param descriptor_name is the name of the descriptor.
|
||||||
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
||||||
/// @param value is the content of value attribute.
|
/// @param value is the content of value attribute.
|
||||||
bool AddDescriptor(const std::string& descriptor_name,
|
[[nodiscard]] bool AddDescriptor(const std::string& descriptor_name,
|
||||||
const std::string& scheme_id_uri,
|
const std::string& scheme_id_uri,
|
||||||
const std::string& value) WARN_UNUSED_RESULT;
|
const std::string& value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool AddContentProtectionElement(
|
[[nodiscard]] bool AddContentProtectionElement(
|
||||||
const ContentProtectionElement& content_protection_element)
|
const ContentProtectionElement& content_protection_element);
|
||||||
WARN_UNUSED_RESULT;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(RepresentationBaseXmlNode);
|
DISALLOW_COPY_AND_ASSIGN(RepresentationBaseXmlNode);
|
||||||
};
|
};
|
||||||
|
@ -166,13 +164,13 @@ class AdaptationSetXmlNode : public RepresentationBaseXmlNode {
|
||||||
|
|
||||||
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
||||||
/// @param value is the content of value attribute.
|
/// @param value is the content of value attribute.
|
||||||
bool AddAccessibilityElement(const std::string& scheme_id_uri,
|
[[nodiscard]] bool AddAccessibilityElement(const std::string& scheme_id_uri,
|
||||||
const std::string& value) WARN_UNUSED_RESULT;
|
const std::string& value);
|
||||||
|
|
||||||
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
/// @param scheme_id_uri is content of the schemeIdUri attribute.
|
||||||
/// @param value is the content of value attribute.
|
/// @param value is the content of value attribute.
|
||||||
bool AddRoleElement(const std::string& scheme_id_uri,
|
[[nodiscard]] bool AddRoleElement(const std::string& scheme_id_uri,
|
||||||
const std::string& value) WARN_UNUSED_RESULT;
|
const std::string& value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(AdaptationSetXmlNode);
|
DISALLOW_COPY_AND_ASSIGN(AdaptationSetXmlNode);
|
||||||
|
@ -191,16 +189,16 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode {
|
||||||
/// @param set_frame_rate is a flag for setting the frameRate attribute.
|
/// @param set_frame_rate is a flag for setting the frameRate attribute.
|
||||||
/// @return true if successfully set attributes and children elements (if
|
/// @return true if successfully set attributes and children elements (if
|
||||||
/// applicable), false otherwise.
|
/// applicable), false otherwise.
|
||||||
bool AddVideoInfo(const MediaInfo::VideoInfo& video_info,
|
[[nodiscard]] bool AddVideoInfo(const MediaInfo::VideoInfo& video_info,
|
||||||
bool set_width,
|
bool set_width,
|
||||||
bool set_height,
|
bool set_height,
|
||||||
bool set_frame_rate) WARN_UNUSED_RESULT;
|
bool set_frame_rate);
|
||||||
|
|
||||||
/// Adds audio metadata to the MPD.
|
/// Adds audio metadata to the MPD.
|
||||||
/// @param audio_info constains the AudioInfos for a Representation.
|
/// @param audio_info constains the AudioInfos for a Representation.
|
||||||
/// @return true if successfully set attributes and children elements (if
|
/// @return true if successfully set attributes and children elements (if
|
||||||
/// applicable), false otherwise.
|
/// applicable), false otherwise.
|
||||||
bool AddAudioInfo(const MediaInfo::AudioInfo& audio_info) WARN_UNUSED_RESULT;
|
[[nodiscard]] bool AddAudioInfo(const MediaInfo::AudioInfo& audio_info);
|
||||||
|
|
||||||
/// Adds fields that are specific to VOD. This ignores @a media_info fields
|
/// Adds fields that are specific to VOD. This ignores @a media_info fields
|
||||||
/// for Live.
|
/// for Live.
|
||||||
|
@ -211,26 +209,27 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode {
|
||||||
// duration of media segments. This is only used when use_segment_list
|
// duration of media segments. This is only used when use_segment_list
|
||||||
// is true.
|
// is true.
|
||||||
/// @return true on success, false otherwise.
|
/// @return true on success, false otherwise.
|
||||||
bool AddVODOnlyInfo(const MediaInfo& media_info,
|
[[nodiscard]] bool AddVODOnlyInfo(const MediaInfo& media_info,
|
||||||
bool use_segment_list,
|
bool use_segment_list,
|
||||||
double target_segment_duration) WARN_UNUSED_RESULT;
|
double target_segment_duration);
|
||||||
|
|
||||||
/// @param segment_infos is a set of SegmentInfos. This method assumes that
|
/// @param segment_infos is a set of SegmentInfos. This method assumes that
|
||||||
/// SegmentInfos are sorted by its start time.
|
/// SegmentInfos are sorted by its start time.
|
||||||
bool AddLiveOnlyInfo(const MediaInfo& media_info,
|
[[nodiscard]] bool AddLiveOnlyInfo(
|
||||||
|
const MediaInfo& media_info,
|
||||||
const std::list<SegmentInfo>& segment_infos,
|
const std::list<SegmentInfo>& segment_infos,
|
||||||
uint32_t start_number,
|
uint32_t start_number,
|
||||||
bool low_latency_dash_mode) WARN_UNUSED_RESULT;
|
bool low_latency_dash_mode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Add AudioChannelConfiguration element. Note that it is a required element
|
// Add AudioChannelConfiguration element. Note that it is a required element
|
||||||
// for audio Representations.
|
// for audio Representations.
|
||||||
bool AddAudioChannelInfo(const MediaInfo::AudioInfo& audio_info)
|
[[nodiscard]] bool AddAudioChannelInfo(
|
||||||
WARN_UNUSED_RESULT;
|
const MediaInfo::AudioInfo& audio_info);
|
||||||
|
|
||||||
// Add audioSamplingRate attribute to this element, if present.
|
// Add audioSamplingRate attribute to this element, if present.
|
||||||
bool AddAudioSamplingRateInfo(const MediaInfo::AudioInfo& audio_info)
|
[[nodiscard]] bool AddAudioSamplingRateInfo(
|
||||||
WARN_UNUSED_RESULT;
|
const MediaInfo::AudioInfo& audio_info);
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(RepresentationXmlNode);
|
DISALLOW_COPY_AND_ASSIGN(RepresentationXmlNode);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,23 +4,23 @@
|
||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://developers.google.com/open-source/licenses/bsd
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <absl/flags/declare.h>
|
||||||
|
#include <absl/flags/flag.h>
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
#include <glog/logging.h>
|
||||||
#include "packager/base/strings/string_util.h"
|
#include "packager/flag_saver.h"
|
||||||
#include "packager/mpd/base/segment_info.h"
|
#include "packager/mpd/base/segment_info.h"
|
||||||
#include "packager/mpd/base/xml/xml_node.h"
|
#include "packager/mpd/base/xml/xml_node.h"
|
||||||
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||||
#include "packager/mpd/test/xml_compare.h"
|
#include "packager/mpd/test/xml_compare.h"
|
||||||
|
|
||||||
DECLARE_bool(segment_template_constant_duration);
|
ABSL_DECLARE_FLAG(bool, segment_template_constant_duration);
|
||||||
DECLARE_bool(dash_add_last_segment_number_when_needed);
|
ABSL_DECLARE_FLAG(bool, dash_add_last_segment_number_when_needed);
|
||||||
|
|
||||||
|
|
||||||
using ::testing::ElementsAre;
|
using ::testing::ElementsAre;
|
||||||
|
|
||||||
|
@ -352,15 +352,20 @@ TEST(XmlNodeTest, AddAC4AudioInfoMPEGSchemeIMS) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class LiveSegmentTimelineTest : public ::testing::Test {
|
class LiveSegmentTimelineTest : public ::testing::Test {
|
||||||
|
public:
|
||||||
|
LiveSegmentTimelineTest()
|
||||||
|
: saver(&FLAGS_segment_template_constant_duration) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
FLAGS_segment_template_constant_duration = true;
|
absl::SetFlag(&FLAGS_segment_template_constant_duration, true);
|
||||||
media_info_.set_segment_template_url("$Number$.m4s");
|
media_info_.set_segment_template_url("$Number$.m4s");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override { FLAGS_segment_template_constant_duration = false; }
|
|
||||||
|
|
||||||
MediaInfo media_info_;
|
MediaInfo media_info_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FlagSaver<bool> saver;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(LiveSegmentTimelineTest, OneSegmentInfo) {
|
TEST_F(LiveSegmentTimelineTest, OneSegmentInfo) {
|
||||||
|
@ -536,7 +541,9 @@ TEST_F(LiveSegmentTimelineTest, LastSegmentNumberSupplementalProperty) {
|
||||||
{kStartTime, kDuration, kRepeat},
|
{kStartTime, kDuration, kRepeat},
|
||||||
};
|
};
|
||||||
RepresentationXmlNode representation;
|
RepresentationXmlNode representation;
|
||||||
FLAGS_dash_add_last_segment_number_when_needed = true;
|
FlagSaver<bool> segment_number_saver(
|
||||||
|
&FLAGS_dash_add_last_segment_number_when_needed);
|
||||||
|
absl::SetFlag(&FLAGS_dash_add_last_segment_number_when_needed, true);
|
||||||
|
|
||||||
ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos,
|
ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos,
|
||||||
kStartNumber, kIsLowLatency));
|
kStartNumber, kIsLowLatency));
|
||||||
|
@ -549,7 +556,6 @@ TEST_F(LiveSegmentTimelineTest, LastSegmentNumberSupplementalProperty) {
|
||||||
" <SegmentTemplate media=\"$Number$.m4s\" "
|
" <SegmentTemplate media=\"$Number$.m4s\" "
|
||||||
" startNumber=\"1\" duration=\"100\"/>"
|
" startNumber=\"1\" duration=\"100\"/>"
|
||||||
"</Representation>"));
|
"</Representation>"));
|
||||||
FLAGS_dash_add_last_segment_number_when_needed = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creating a separate Test Suite for RepresentationXmlNode::AddVODOnlyInfo
|
// Creating a separate Test Suite for RepresentationXmlNode::AddVODOnlyInfo
|
||||||
|
@ -751,7 +757,7 @@ TEST_F(LowLatencySegmentTest, LowLatencySegmentTemplate) {
|
||||||
representation,
|
representation,
|
||||||
XmlNodeEqual("<Representation>"
|
XmlNodeEqual("<Representation>"
|
||||||
" <SegmentTemplate timescale=\"90000\" duration=\"450000\" "
|
" <SegmentTemplate timescale=\"90000\" duration=\"450000\" "
|
||||||
" availabilityTimeOffset=\"4.9750987314\" "
|
" availabilityTimeOffset=\"4.9751\" "
|
||||||
" availabilityTimeComplete=\"false\" "
|
" availabilityTimeComplete=\"false\" "
|
||||||
" initialization=\"init.m4s\" "
|
" initialization=\"init.m4s\" "
|
||||||
" media=\"$Number$.m4s\" "
|
" media=\"$Number$.m4s\" "
|
||||||
|
|
|
@ -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',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
|
@ -6,47 +6,26 @@
|
||||||
|
|
||||||
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
#include <google/protobuf/text_format.h>
|
#include <google/protobuf/text_format.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include "packager/base/files/file_util.h"
|
#include "packager/media/test/test_data_util.h"
|
||||||
#include "packager/base/path_service.h"
|
|
||||||
#include "packager/mpd/base/media_info.pb.h"
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
#include "packager/mpd/base/mpd_builder.h"
|
|
||||||
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
||||||
#include "packager/mpd/test/xml_compare.h"
|
#include "packager/mpd/test/xml_compare.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
base::FilePath GetTestDataFilePath(const std::string& file_name) {
|
std::filesystem::path GetTestDataFilePath(const std::string& name) {
|
||||||
base::FilePath file_path;
|
auto data_dir = std::filesystem::path(TEST_DATA_DIR);
|
||||||
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
|
return data_dir / name;
|
||||||
|
|
||||||
file_path = file_path.Append(FILE_PATH_LITERAL("packager"))
|
|
||||||
.Append(FILE_PATH_LITERAL("mpd"))
|
|
||||||
.Append(FILE_PATH_LITERAL("test"))
|
|
||||||
.Append(FILE_PATH_LITERAL("data"))
|
|
||||||
.AppendASCII(file_name);
|
|
||||||
return file_path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base::FilePath GetSchemaPath() {
|
std::filesystem::path GetSchemaPath() {
|
||||||
base::FilePath file_path;
|
auto schema_dir = std::filesystem::path(TEST_SCHEMA_DIR);
|
||||||
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
|
return schema_dir / "DASH-MPD.xsd";
|
||||||
|
|
||||||
file_path = file_path.Append(FILE_PATH_LITERAL("packager"))
|
|
||||||
.Append(FILE_PATH_LITERAL("mpd"))
|
|
||||||
.Append(FILE_PATH_LITERAL("test"))
|
|
||||||
.Append(FILE_PATH_LITERAL("schema"))
|
|
||||||
.Append(FILE_PATH_LITERAL("DASH-MPD.xsd"));
|
|
||||||
return file_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GetPathContent(const base::FilePath& file_path) {
|
|
||||||
std::string content;
|
|
||||||
bool file_read_to_string = base::ReadFileToString(file_path, &content);
|
|
||||||
DCHECK(file_read_to_string) << file_path.value();
|
|
||||||
return content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
|
MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
|
||||||
|
@ -57,8 +36,8 @@ MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaInfo GetTestMediaInfo(const std::string& media_info_file_name) {
|
MediaInfo GetTestMediaInfo(const std::string& media_info_file_name) {
|
||||||
return ConvertToMediaInfo(
|
std::filesystem::path test_path = GetTestDataFilePath(media_info_file_name);
|
||||||
GetPathContent(GetTestDataFilePath(media_info_file_name)));
|
return ConvertToMediaInfo(GetPathContent(test_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValidateMpdSchema(const std::string& mpd) {
|
bool ValidateMpdSchema(const std::string& mpd) {
|
||||||
|
@ -69,18 +48,15 @@ bool ValidateMpdSchema(const std::string& mpd) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
base::FilePath schema_path = GetSchemaPath();
|
std::filesystem::path schema_path = GetSchemaPath();
|
||||||
std::string schema_str = GetPathContent(schema_path);
|
std::string schema_str = GetPathContent(schema_path);
|
||||||
|
|
||||||
// First, I need to load the schema as a xmlDoc so that I can pass the path of
|
// First, I need to load the schema as a xmlDoc so that I can pass the path of
|
||||||
// the DASH-MPD.xsd. Then it can resolve the relative path included from the
|
// the DASH-MPD.xsd. Then it can resolve the relative path included from the
|
||||||
// XSD when creating xmlSchemaParserCtxt.
|
// XSD when creating xmlSchemaParserCtxt.
|
||||||
xml::scoped_xml_ptr<xmlDoc> schema_as_doc(
|
xml::scoped_xml_ptr<xmlDoc> schema_as_doc(
|
||||||
xmlReadMemory(schema_str.data(),
|
xmlReadMemory(schema_str.data(), schema_str.size(),
|
||||||
schema_str.size(),
|
schema_path.string().c_str(), NULL, 0));
|
||||||
schema_path.AsUTF8Unsafe().c_str(),
|
|
||||||
NULL,
|
|
||||||
0));
|
|
||||||
DCHECK(schema_as_doc);
|
DCHECK(schema_as_doc);
|
||||||
xml::scoped_xml_ptr<xmlSchemaParserCtxt>
|
xml::scoped_xml_ptr<xmlSchemaParserCtxt>
|
||||||
schema_parser_ctxt(xmlSchemaNewDocParserCtxt(schema_as_doc.get()));
|
schema_parser_ctxt(xmlSchemaNewDocParserCtxt(schema_as_doc.get()));
|
||||||
|
@ -102,9 +78,11 @@ bool ValidateMpdSchema(const std::string& mpd) {
|
||||||
void ExpectMpdToEqualExpectedOutputFile(
|
void ExpectMpdToEqualExpectedOutputFile(
|
||||||
const std::string& mpd_string,
|
const std::string& mpd_string,
|
||||||
const std::string& expected_output_file) {
|
const std::string& expected_output_file) {
|
||||||
std::string expected_mpd;
|
std::filesystem::path expected_output_file_path =
|
||||||
ASSERT_TRUE(base::ReadFileToString(GetTestDataFilePath(expected_output_file),
|
GetTestDataFilePath(expected_output_file);
|
||||||
&expected_mpd))
|
std::string expected_mpd = GetPathContent(expected_output_file_path);
|
||||||
|
|
||||||
|
ASSERT_TRUE(!expected_mpd.empty())
|
||||||
<< "Failed to read: " << expected_output_file;
|
<< "Failed to read: " << expected_output_file;
|
||||||
|
|
||||||
// Adding extra << here to get a formatted output.
|
// Adding extra << here to get a formatted output.
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
#ifndef MPD_TEST_MPD_BUILDER_TEST_HELPER_H_
|
#ifndef MPD_TEST_MPD_BUILDER_TEST_HELPER_H_
|
||||||
#define MPD_TEST_MPD_BUILDER_TEST_HELPER_H_
|
#define MPD_TEST_MPD_BUILDER_TEST_HELPER_H_
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "packager/base/files/file_path.h"
|
|
||||||
#include "packager/mpd/base/media_info.pb.h"
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -39,13 +39,10 @@ const char kFileNameExpectedMpdOutputAudio1AndVideo1[] =
|
||||||
|
|
||||||
// Returns the path to test data with |file_name|. Use constants above to get
|
// Returns the path to test data with |file_name|. Use constants above to get
|
||||||
// path to the test files.
|
// path to the test files.
|
||||||
base::FilePath GetTestDataFilePath(const std::string& file_name);
|
std::filesystem::path GetTestDataFilePath(const std::string& file_name);
|
||||||
|
|
||||||
// Get path to DASH MPD schema.
|
// Get path to DASH MPD schema.
|
||||||
base::FilePath GetSchemaPath();
|
std::filesystem::path GetSchemaPath();
|
||||||
|
|
||||||
// Get the content of |file_path|. Returns empty string on error.
|
|
||||||
std::string GetPathContent(const base::FilePath& file_path);
|
|
||||||
|
|
||||||
// Convert |media_info_string| to MediaInfo.
|
// Convert |media_info_string| to MediaInfo.
|
||||||
MediaInfo ConvertToMediaInfo(const std::string& media_info_string);
|
MediaInfo ConvertToMediaInfo(const std::string& media_info_string);
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
#include "packager/mpd/test/xml_compare.h"
|
#include "packager/mpd/test/xml_compare.h"
|
||||||
|
|
||||||
|
#include <absl/strings/strip.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
|
||||||
#include "packager/base/strings/string_util.h"
|
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -80,10 +78,17 @@ bool CompareContents(xmlNodePtr node1, xmlNodePtr node2) {
|
||||||
reinterpret_cast<const char*>(node1_content_ptr.get());
|
reinterpret_cast<const char*>(node1_content_ptr.get());
|
||||||
std::string node2_content =
|
std::string node2_content =
|
||||||
reinterpret_cast<const char*>(node2_content_ptr.get());
|
reinterpret_cast<const char*>(node2_content_ptr.get());
|
||||||
base::ReplaceChars(node1_content, "\n", "", &node1_content);
|
|
||||||
base::TrimString(node1_content, " ", &node1_content);
|
node1_content.erase(
|
||||||
base::ReplaceChars(node2_content, "\n", "", &node2_content);
|
std::remove(node1_content.begin(), node1_content.end(), '\n'),
|
||||||
base::TrimString(node2_content, " ", &node2_content);
|
node1_content.end());
|
||||||
|
node2_content.erase(
|
||||||
|
std::remove(node2_content.begin(), node2_content.end(), '\n'),
|
||||||
|
node2_content.end());
|
||||||
|
|
||||||
|
node1_content = absl::StripAsciiWhitespace(node1_content);
|
||||||
|
node2_content = absl::StripAsciiWhitespace(node2_content);
|
||||||
|
|
||||||
DVLOG(2) << "Comparing contents of "
|
DVLOG(2) << "Comparing contents of "
|
||||||
<< reinterpret_cast<const char*>(node1->name) << "\n"
|
<< reinterpret_cast<const char*>(node1->name) << "\n"
|
||||||
<< "First node's content:\n" << node1_content << "\n"
|
<< "First node's content:\n" << node1_content << "\n"
|
||||||
|
@ -157,7 +162,7 @@ bool XmlEqual(const std::string& xml1, const std::string& xml2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XmlEqual(const std::string& xml1,
|
bool XmlEqual(const std::string& xml1,
|
||||||
const base::Optional<xml::XmlNode>& xml2) {
|
const std::optional<xml::XmlNode>& xml2) {
|
||||||
return xml2 && XmlEqual(xml1, *xml2);
|
return xml2 && XmlEqual(xml1, *xml2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +178,7 @@ bool XmlEqual(const std::string& xml1, const xml::XmlNode& xml2) {
|
||||||
return CompareNodes(xml1_root_element, xml2.GetRawPtr());
|
return CompareNodes(xml1_root_element, xml2.GetRawPtr());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string XmlNodeToString(const base::Optional<xml::XmlNode>& xml_node) {
|
std::string XmlNodeToString(const std::optional<xml::XmlNode>& xml_node) {
|
||||||
return xml_node ? XmlNodeToString(*xml_node) : "$ERROR$";
|
return xml_node ? XmlNodeToString(*xml_node) : "$ERROR$";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "packager/base/optional.h"
|
#include <optional>
|
||||||
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
||||||
#include "packager/mpd/base/xml/xml_node.h"
|
#include "packager/mpd/base/xml/xml_node.h"
|
||||||
|
|
||||||
|
@ -22,12 +22,11 @@ namespace shaka {
|
||||||
/// @return true if @a xml1 and @a xml2 are equivalent, false otherwise.
|
/// @return true if @a xml1 and @a xml2 are equivalent, false otherwise.
|
||||||
bool XmlEqual(const std::string& xml1, const std::string& xml2);
|
bool XmlEqual(const std::string& xml1, const std::string& xml2);
|
||||||
bool XmlEqual(const std::string& xml1, const xml::XmlNode& xml2);
|
bool XmlEqual(const std::string& xml1, const xml::XmlNode& xml2);
|
||||||
bool XmlEqual(const std::string& xml1,
|
bool XmlEqual(const std::string& xml1, const std::optional<xml::XmlNode>& xml2);
|
||||||
const base::Optional<xml::XmlNode>& xml2);
|
|
||||||
|
|
||||||
/// Get string representation of the xml node.
|
/// Get string representation of the xml node.
|
||||||
std::string XmlNodeToString(const xml::XmlNode& xml_node);
|
std::string XmlNodeToString(const xml::XmlNode& xml_node);
|
||||||
std::string XmlNodeToString(const base::Optional<xml::XmlNode>& xml_node);
|
std::string XmlNodeToString(const std::optional<xml::XmlNode>& xml_node);
|
||||||
|
|
||||||
/// Match an xmlNodePtr with an xml in string representation.
|
/// Match an xmlNodePtr with an xml in string representation.
|
||||||
MATCHER_P(XmlNodeEqual,
|
MATCHER_P(XmlNodeEqual,
|
||||||
|
|
|
@ -6,19 +6,19 @@
|
||||||
|
|
||||||
#include "packager/mpd/util/mpd_writer.h"
|
#include "packager/mpd/util/mpd_writer.h"
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <absl/flags/flag.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
#include <google/protobuf/text_format.h>
|
#include <google/protobuf/text_format.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "packager/base/files/file_path.h"
|
|
||||||
#include "packager/base/files/file_util.h"
|
|
||||||
#include "packager/file/file.h"
|
#include "packager/file/file.h"
|
||||||
#include "packager/mpd/base/mpd_builder.h"
|
#include "packager/mpd/base/mpd_builder.h"
|
||||||
#include "packager/mpd/base/mpd_notifier.h"
|
#include "packager/mpd/base/mpd_notifier.h"
|
||||||
#include "packager/mpd/base/mpd_utils.h"
|
#include "packager/mpd/base/mpd_utils.h"
|
||||||
#include "packager/mpd/base/simple_mpd_notifier.h"
|
#include "packager/mpd/base/simple_mpd_notifier.h"
|
||||||
|
|
||||||
DEFINE_bool(generate_dash_if_iop_compliant_mpd,
|
ABSL_FLAG(bool,
|
||||||
|
generate_dash_if_iop_compliant_mpd,
|
||||||
true,
|
true,
|
||||||
"Try to generate DASH-IF IOP compliant MPD. This is best effort "
|
"Try to generate DASH-IF IOP compliant MPD. This is best effort "
|
||||||
"and does not guarantee compliance.");
|
"and does not guarantee compliance.");
|
||||||
|
@ -71,7 +71,7 @@ bool MpdWriter::WriteMpdToFile(const char* file_name) {
|
||||||
mpd_options.mpd_params.base_urls = base_urls_;
|
mpd_options.mpd_params.base_urls = base_urls_;
|
||||||
mpd_options.mpd_params.mpd_output = file_name;
|
mpd_options.mpd_params.mpd_output = file_name;
|
||||||
mpd_options.mpd_params.generate_dash_if_iop_compliant_mpd =
|
mpd_options.mpd_params.generate_dash_if_iop_compliant_mpd =
|
||||||
FLAGS_generate_dash_if_iop_compliant_mpd;
|
absl::GetFlag(FLAGS_generate_dash_if_iop_compliant_mpd);
|
||||||
std::unique_ptr<MpdNotifier> notifier =
|
std::unique_ptr<MpdNotifier> notifier =
|
||||||
notifier_factory_->Create(mpd_options);
|
notifier_factory_->Create(mpd_options);
|
||||||
if (!notifier->Init()) {
|
if (!notifier->Init()) {
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/macros.h"
|
#include "packager/macros.h"
|
||||||
#include "packager/mpd/base/mpd_notifier.h"
|
#include "packager/mpd/base/mpd_notifier.h"
|
||||||
#include "packager/mpd/base/mpd_options.h"
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include "packager/base/files/file_util.h"
|
#include "packager/file/file_test_util.h"
|
||||||
#include "packager/base/path_service.h"
|
|
||||||
#include "packager/mpd/base/mock_mpd_notifier.h"
|
#include "packager/mpd/base/mock_mpd_notifier.h"
|
||||||
#include "packager/mpd/base/mpd_options.h"
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||||
|
@ -43,7 +43,7 @@ class TestMpdNotifierFactory : public MpdNotifierFactory {
|
||||||
.Times(2)
|
.Times(2)
|
||||||
.WillRepeatedly(Return(true));
|
.WillRepeatedly(Return(true));
|
||||||
EXPECT_CALL(*mock_notifier, Flush()).WillOnce(Return(true));
|
EXPECT_CALL(*mock_notifier, Flush()).WillOnce(Return(true));
|
||||||
return std::move(mock_notifier);
|
return mock_notifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetExpectedBaseUrls(const std::vector<std::string>& base_urls) {
|
void SetExpectedBaseUrls(const std::vector<std::string>& base_urls) {
|
||||||
|
@ -77,25 +77,24 @@ TEST_F(MpdWriterTest, WriteMpdToFile) {
|
||||||
const char kBaseUrl1[] = "http://cdn1.mydomain.com/";
|
const char kBaseUrl1[] = "http://cdn1.mydomain.com/";
|
||||||
const char kBaseUrl2[] = "http://cdn2.mydomain.com/";
|
const char kBaseUrl2[] = "http://cdn2.mydomain.com/";
|
||||||
std::vector<std::string> base_urls_;
|
std::vector<std::string> base_urls_;
|
||||||
base_urls_.push_back(kBaseUrl1);
|
base_urls_.emplace_back(kBaseUrl1);
|
||||||
base_urls_.push_back(kBaseUrl2);
|
base_urls_.emplace_back(kBaseUrl2);
|
||||||
|
|
||||||
notifier_factory_->SetExpectedBaseUrls(base_urls_);
|
notifier_factory_->SetExpectedBaseUrls(base_urls_);
|
||||||
|
|
||||||
base::FilePath media_info_file1 =
|
std::filesystem::path media_info_file1 =
|
||||||
GetTestDataFilePath(kFileNameVideoMediaInfo1);
|
GetTestDataFilePath(kFileNameVideoMediaInfo1);
|
||||||
base::FilePath media_info_file2 =
|
std::filesystem::path media_info_file2 =
|
||||||
GetTestDataFilePath(kFileNameVideoMediaInfo2);
|
GetTestDataFilePath(kFileNameVideoMediaInfo2);
|
||||||
|
|
||||||
SetMpdNotifierFactoryForTest();
|
SetMpdNotifierFactoryForTest();
|
||||||
EXPECT_TRUE(mpd_writer_.AddFile(media_info_file1.AsUTF8Unsafe()));
|
EXPECT_TRUE(mpd_writer_.AddFile(media_info_file1.string()));
|
||||||
EXPECT_TRUE(mpd_writer_.AddFile(media_info_file2.AsUTF8Unsafe()));
|
EXPECT_TRUE(mpd_writer_.AddFile(media_info_file2.string()));
|
||||||
mpd_writer_.AddBaseUrl(kBaseUrl1);
|
mpd_writer_.AddBaseUrl(kBaseUrl1);
|
||||||
mpd_writer_.AddBaseUrl(kBaseUrl2);
|
mpd_writer_.AddBaseUrl(kBaseUrl2);
|
||||||
|
|
||||||
base::FilePath mpd_file_path;
|
auto temp = new TempFile();
|
||||||
ASSERT_TRUE(base::CreateTemporaryFile(&mpd_file_path));
|
EXPECT_TRUE(mpd_writer_.WriteMpdToFile(temp->path().c_str()));
|
||||||
EXPECT_TRUE(mpd_writer_.WriteMpdToFile(mpd_file_path.AsUTF8Unsafe().c_str()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -4,6 +4,16 @@
|
||||||
# license that can be found in the LICENSE file or at
|
# license that can be found in the LICENSE file or at
|
||||||
# https://developers.google.com/open-source/licenses/bsd
|
# https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
add_library(utils_clock STATIC
|
||||||
|
clock.h
|
||||||
|
clock.cc)
|
||||||
|
|
||||||
|
add_library(utils_test_clock STATIC
|
||||||
|
test_clock.cc
|
||||||
|
test_clock.h)
|
||||||
|
|
||||||
|
target_link_libraries(utils_test_clock utils_clock absl::strings)
|
||||||
|
|
||||||
add_library(hex_parser STATIC
|
add_library(hex_parser STATIC
|
||||||
hex_parser.cc)
|
hex_parser.cc)
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
Loading…
Reference in New Issue