From e1371d8bc58221f7a687c352c181c4e40938cf90 Mon Sep 17 00:00:00 2001 From: sr90 Date: Tue, 7 Apr 2020 23:03:35 -0700 Subject: [PATCH 01/10] Omit SegmentTimeline completely, with allow_approximate_segment_timeline option --- packager/mpd/base/representation.cc | 4 ++++ packager/mpd/base/xml/xml_node.cc | 26 +++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packager/mpd/base/representation.cc b/packager/mpd/base/representation.cc index b1b46c8c2e0..2254dbe0132 100644 --- a/packager/mpd/base/representation.cc +++ b/packager/mpd/base/representation.cc @@ -332,6 +332,10 @@ void Representation::AddSegmentInfo(int64_t start_time, int64_t duration) { const uint64_t kNoRepeat = 0; const int64_t adjusted_duration = AdjustDuration(duration); + if (segment_infos_.empty()) { + start_number_ = start_time / duration; + } + if (!segment_infos_.empty()) { // Contiguous segment. const SegmentInfo& previous = segment_infos_.back(); diff --git a/packager/mpd/base/xml/xml_node.cc b/packager/mpd/base/xml/xml_node.cc index a231b3683fc..58ef90a6474 100644 --- a/packager/mpd/base/xml/xml_node.cc +++ b/packager/mpd/base/xml/xml_node.cc @@ -44,6 +44,14 @@ std::string RangeToString(const Range& range) { base::Uint64ToString(range.end()); } +bool ApproximiatelyEqual(int64_t time1, int64_t time2) { + const double kErrorThresholdSeconds = 0.05; + + const uint32_t error_threshold = + static_cast(kErrorThresholdSeconds * time1); + return std::abs(time1 - time2) <= error_threshold; +} + // Check if segments are continuous and all segments except the last one are of // the same duration. bool IsTimelineConstantDuration(const std::list& segment_infos, @@ -56,14 +64,15 @@ bool IsTimelineConstantDuration(const std::list& segment_infos, return false; const SegmentInfo& first_segment = segment_infos.front(); - if (first_segment.start_time != first_segment.duration * (start_number - 1)) + if (!ApproximiatelyEqual(first_segment.start_time, + first_segment.duration * start_number)) return false; if (segment_infos.size() == 1) return true; const SegmentInfo& last_segment = segment_infos.back(); - if (last_segment.repeat != 0) + if (!(last_segment.repeat == 0 || first_segment.repeat == 0)) return false; const int64_t expected_last_segment_start_time = @@ -179,9 +188,8 @@ void XmlNode::SetStringAttribute(const char* attribute_name, void XmlNode::SetIntegerAttribute(const char* attribute_name, uint64_t number) { DCHECK(node_); DCHECK(attribute_name); - xmlSetProp(node_.get(), - BAD_CAST attribute_name, - BAD_CAST (base::Uint64ToString(number).c_str())); + xmlSetProp(node_.get(), BAD_CAST attribute_name, + BAD_CAST(base::Uint64ToString(number).c_str())); } void XmlNode::SetFloatingPointAttribute(const char* attribute_name, @@ -437,12 +445,12 @@ bool RepresentationXmlNode::AddLiveOnlyInfo( segment_infos.front().duration); if (FLAGS_dash_add_last_segment_number_when_needed) { 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; - + AddSupplementalProperty( - "http://dashif.org/guidelines/last-segment-number", - std::to_string(last_segment_number)); + "http://dashif.org/guidelines/last-segment-number", + std::to_string(last_segment_number)); } } else { XmlNode segment_timeline("SegmentTimeline"); From e07db24f6e811f0c8aee57b35b104f0e78cb4247 Mon Sep 17 00:00:00 2001 From: sr90 Date: Sun, 12 Apr 2020 15:19:19 -0700 Subject: [PATCH 02/10] Changes as per comments - 1 --- packager/mpd/base/representation.cc | 32 +++++++- packager/mpd/base/representation.h | 12 +-- packager/mpd/base/representation_unittest.cc | 8 +- packager/mpd/base/xml/xml_node.cc | 42 ++++++---- packager/mpd/base/xml/xml_node.h | 3 +- packager/mpd/base/xml/xml_node_unittest.cc | 83 ++++++++++---------- 6 files changed, 110 insertions(+), 70 deletions(-) diff --git a/packager/mpd/base/representation.cc b/packager/mpd/base/representation.cc index 2254dbe0132..832b5fd85b1 100644 --- a/packager/mpd/base/representation.cc +++ b/packager/mpd/base/representation.cc @@ -269,8 +269,9 @@ xml::scoped_xml_ptr Representation::GetXml() { } if (HasLiveOnlyFields(media_info_) && - !representation.AddLiveOnlyInfo(media_info_, segment_infos_, - start_number_)) { + !representation.AddLiveOnlyInfo( + media_info_, segment_infos_, start_number_, + mpd_options_.mpd_params.target_segment_duration)) { LOG(ERROR) << "Failed to add Live info."; return xml::scoped_xml_ptr(); } @@ -333,7 +334,19 @@ void Representation::AddSegmentInfo(int64_t start_time, int64_t duration) { const int64_t adjusted_duration = AdjustDuration(duration); if (segment_infos_.empty()) { - start_number_ = start_time / duration; + const int64_t scaled_target_duration = + mpd_options_.mpd_params.target_segment_duration * + media_info_.reference_time_scale(); + + if (mpd_options_.mpd_params.target_segment_duration > 0 && + mpd_options_.mpd_params.allow_approximate_segment_timeline) { + if (adjusted_duration == scaled_target_duration) { + start_number_ = start_time / scaled_target_duration + 1; + } else { + start_number_ = start_time / scaled_target_duration; + } + stream_just_started_ = true; + } } if (!segment_infos_.empty()) { @@ -352,6 +365,19 @@ void Representation::AddSegmentInfo(int64_t start_time, int64_t duration) { if (ApproximiatelyEqual(segment_end_time_for_same_duration, actual_segment_end_time)) { ++segment_infos_.back().repeat; + if (mpd_options_.mpd_params.allow_approximate_segment_timeline && + mpd_options_.mpd_params.target_segment_duration > 0 && + stream_just_started_ && segment_infos_.size() == 2) { + const SegmentInfo& first_segment = segment_infos_.front(); + const SegmentInfo& last_segment = segment_infos_.back(); + if (first_segment.repeat == 0 && + first_segment.duration < last_segment.duration && + last_segment.repeat > 0) { + segment_infos_.pop_front(); + start_number_ = last_segment.start_time / last_segment.duration + 1; + } + stream_just_started_ = false; + } } else { segment_infos_.push_back( {previous_segment_end_time, diff --git a/packager/mpd/base/representation.h b/packager/mpd/base/representation.h index 813d43edc5f..91c24c30ae6 100644 --- a/packager/mpd/base/representation.h +++ b/packager/mpd/base/representation.h @@ -9,16 +9,16 @@ #ifndef PACKAGER_MPD_BASE_REPRESENTATION_H_ #define PACKAGER_MPD_BASE_REPRESENTATION_H_ -#include "packager/mpd/base/bandwidth_estimator.h" -#include "packager/mpd/base/media_info.pb.h" -#include "packager/mpd/base/segment_info.h" -#include "packager/mpd/base/xml/scoped_xml_ptr.h" - #include #include #include +#include "packager/mpd/base/bandwidth_estimator.h" +#include "packager/mpd/base/media_info.pb.h" +#include "packager/mpd/base/segment_info.h" +#include "packager/mpd/base/xml/scoped_xml_ptr.h" + namespace shaka { struct ContentProtectionElement; @@ -231,6 +231,8 @@ class Representation { // Starts from 1. uint32_t start_number_ = 1; + bool stream_just_started_ = false; + // If this is not null, then Representation is responsible for calling the // right methods at right timings. std::unique_ptr state_change_listener_; diff --git a/packager/mpd/base/representation_unittest.cc b/packager/mpd/base/representation_unittest.cc index c9a9f97684b..4f26bc6199a 100644 --- a/packager/mpd/base/representation_unittest.cc +++ b/packager/mpd/base/representation_unittest.cc @@ -799,12 +799,16 @@ TEST_P(ApproximateSegmentTimelineTest, if (allow_approximate_segment_timeline_) { expected_s_elements = base::StringPrintf( kSElementTemplateWithoutR, kStartTime, kScaledTargetSegmentDuration); + EXPECT_THAT(representation_->GetXml().get(), + XmlNodeEqual(SegmentTimelineTestBase::ExpectedXml( + expected_s_elements, 1235))); } else { expected_s_elements = base::StringPrintf(kSElementTemplateWithoutR, kStartTime, kDurationSmaller); + EXPECT_THAT(representation_->GetXml().get(), + XmlNodeEqual(SegmentTimelineTestBase::ExpectedXml( + expected_s_elements, 1))); } - EXPECT_THAT(representation_->GetXml().get(), - XmlNodeEqual(ExpectedXml(expected_s_elements))); } TEST_P(ApproximateSegmentTimelineTest, SegmentsWithSimilarDurations) { diff --git a/packager/mpd/base/xml/xml_node.cc b/packager/mpd/base/xml/xml_node.cc index 58ef90a6474..c14d4f68d3c 100644 --- a/packager/mpd/base/xml/xml_node.cc +++ b/packager/mpd/base/xml/xml_node.cc @@ -44,18 +44,11 @@ std::string RangeToString(const Range& range) { base::Uint64ToString(range.end()); } -bool ApproximiatelyEqual(int64_t time1, int64_t time2) { - const double kErrorThresholdSeconds = 0.05; - - const uint32_t error_threshold = - static_cast(kErrorThresholdSeconds * time1); - return std::abs(time1 - time2) <= error_threshold; -} - // Check if segments are continuous and all segments except the last one are of // the same duration. bool IsTimelineConstantDuration(const std::list& segment_infos, - uint32_t start_number) { + uint32_t start_number, + const double target_duration) { if (!FLAGS_segment_template_constant_duration) return false; @@ -64,15 +57,22 @@ bool IsTimelineConstantDuration(const std::list& segment_infos, return false; const SegmentInfo& first_segment = segment_infos.front(); - if (!ApproximiatelyEqual(first_segment.start_time, - first_segment.duration * start_number)) - return false; + + if (first_segment.duration < target_duration) { + if (static_cast(first_segment.start_time / target_duration) != + start_number) + return false; + } else { + if (static_cast(first_segment.start_time / + first_segment.duration) != start_number - 1) + return false; + } if (segment_infos.size() == 1) return true; const SegmentInfo& last_segment = segment_infos.back(); - if (!(last_segment.repeat == 0 || first_segment.repeat == 0)) + if (last_segment.repeat != 0) return false; const int64_t expected_last_segment_start_time = @@ -414,7 +414,8 @@ bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info) { bool RepresentationXmlNode::AddLiveOnlyInfo( const MediaInfo& media_info, const std::list& segment_infos, - uint32_t start_number) { + uint32_t start_number, + const double target_duration) { XmlNode segment_template("SegmentTemplate"); if (media_info.has_reference_time_scale()) { segment_template.SetIntegerAttribute("timescale", @@ -440,9 +441,16 @@ bool RepresentationXmlNode::AddLiveOnlyInfo( if (!segment_infos.empty()) { // Don't use SegmentTimeline if all segments except the last one are of // the same duration. - if (IsTimelineConstantDuration(segment_infos, start_number)) { - segment_template.SetIntegerAttribute("duration", - segment_infos.front().duration); + if (IsTimelineConstantDuration( + segment_infos, start_number, + target_duration * media_info.reference_time_scale())) { + if (target_duration > 0) { + segment_template.SetIntegerAttribute( + "duration", target_duration * media_info.reference_time_scale()); + } else { + segment_template.SetIntegerAttribute("duration", + segment_infos.front().duration); + } if (FLAGS_dash_add_last_segment_number_when_needed) { uint32_t last_segment_number = start_number - 1; for (const auto& segment_info_element : segment_infos) diff --git a/packager/mpd/base/xml/xml_node.h b/packager/mpd/base/xml/xml_node.h index 976730d4d3f..858a56fb2e4 100644 --- a/packager/mpd/base/xml/xml_node.h +++ b/packager/mpd/base/xml/xml_node.h @@ -186,7 +186,8 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode { /// SegmentInfos are sorted by its start time. bool AddLiveOnlyInfo(const MediaInfo& media_info, const std::list& segment_infos, - uint32_t start_number); + uint32_t start_number, + const double target_duration); private: // Add AudioChannelConfiguration element. Note that it is a required element diff --git a/packager/mpd/base/xml/xml_node_unittest.cc b/packager/mpd/base/xml/xml_node_unittest.cc index 1840765c703..45c20ee952e 100644 --- a/packager/mpd/base/xml/xml_node_unittest.cc +++ b/packager/mpd/base/xml/xml_node_unittest.cc @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd +#include "packager/mpd/base/xml/xml_node.h" + #include #include #include @@ -14,7 +16,6 @@ #include "packager/base/logging.h" #include "packager/base/strings/string_util.h" #include "packager/mpd/base/segment_info.h" -#include "packager/mpd/base/xml/xml_node.h" #include "packager/mpd/test/xml_compare.h" DECLARE_bool(segment_template_constant_duration); @@ -57,7 +58,6 @@ TEST(XmlNodeTest, MetaTestXmlElementsEqual) { " \n" ""; - // This is same as kXml1 but the attributes are reordered. Note that the // children are not reordered. static const char kXml1AttributeReorder[] = @@ -134,9 +134,8 @@ TEST(XmlNodeTest, MetaTestXmlElementsEqual) { // But if it is run on for the first XML, it will return "content1", but // for second XML will return "c". TEST(XmlNodeTest, MetaTestXmlEqualDifferentContent) { - ASSERT_FALSE(XmlEqual( - "content1content2", - "content1content2")); + ASSERT_FALSE(XmlEqual("content1content2", + "content1content2")); } TEST(XmlNodeTest, ExtractReferencedNamespaces) { @@ -303,8 +302,8 @@ TEST_F(LiveSegmentTimelineTest, OneSegmentInfo) { {kStartTime, kDuration, kRepeat}, }; RepresentationXmlNode representation; - ASSERT_TRUE( - representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); + ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, + kStartNumber, 0)); EXPECT_THAT( representation.GetRawPtr(), @@ -324,8 +323,8 @@ TEST_F(LiveSegmentTimelineTest, OneSegmentInfoNonZeroStartTime) { {kNonZeroStartTime, kDuration, kRepeat}, }; RepresentationXmlNode representation; - ASSERT_TRUE( - representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); + ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, + kStartNumber, 0)); EXPECT_THAT(representation.GetRawPtr(), XmlNodeEqual( @@ -348,8 +347,8 @@ TEST_F(LiveSegmentTimelineTest, OneSegmentInfoMatchingStartTimeAndNumber) { {kNonZeroStartTime, kDuration, kRepeat}, }; RepresentationXmlNode representation; - ASSERT_TRUE( - representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); + ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, + kStartNumber, 0)); EXPECT_THAT( representation.GetRawPtr(), @@ -375,8 +374,8 @@ TEST_F(LiveSegmentTimelineTest, AllSegmentsSameDurationExpectLastOne) { {kStartTime2, kDuration2, kRepeat2}, }; RepresentationXmlNode representation; - ASSERT_TRUE( - representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); + ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, + kStartNumber, 0)); EXPECT_THAT( representation.GetRawPtr(), @@ -402,8 +401,8 @@ TEST_F(LiveSegmentTimelineTest, SecondSegmentInfoNonZeroRepeat) { {kStartTime2, kDuration2, kRepeat2}, }; RepresentationXmlNode representation; - ASSERT_TRUE( - representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); + ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, + kStartNumber, 0)); EXPECT_THAT(representation.GetRawPtr(), XmlNodeEqual( @@ -434,8 +433,8 @@ TEST_F(LiveSegmentTimelineTest, TwoSegmentInfoWithGap) { {kStartTime2, kDuration2, kRepeat2}, }; RepresentationXmlNode representation; - ASSERT_TRUE( - representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); + ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, + kStartNumber, 0)); EXPECT_THAT(representation.GetRawPtr(), XmlNodeEqual( @@ -449,31 +448,31 @@ TEST_F(LiveSegmentTimelineTest, TwoSegmentInfoWithGap) { "")); } -TEST_F(LiveSegmentTimelineTest, LastSegmentNumberSupplementalProperty) { - const uint32_t kStartNumber = 1; - const uint64_t kStartTime = 0; - const uint64_t kDuration = 100; - const uint64_t kRepeat = 9; - - std::list segment_infos = { - {kStartTime, kDuration, kRepeat}, - }; - RepresentationXmlNode representation; - FLAGS_dash_add_last_segment_number_when_needed = true; - - ASSERT_TRUE( - representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); - - EXPECT_THAT( - representation.GetRawPtr(), - XmlNodeEqual("" - "" - " " - "")); - FLAGS_dash_add_last_segment_number_when_needed = false; -} +TEST_F(LiveSegmentTimelineTest, LastSegmentNumberSupplementalProperty) { + const uint32_t kStartNumber = 1; + const uint64_t kStartTime = 0; + const uint64_t kDuration = 100; + const uint64_t kRepeat = 9; + + std::list segment_infos = { + {kStartTime, kDuration, kRepeat}, + }; + RepresentationXmlNode representation; + FLAGS_dash_add_last_segment_number_when_needed = true; + + ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, + kStartNumber, 0)); + + EXPECT_THAT( + representation.GetRawPtr(), + XmlNodeEqual("" + "" + " " + "")); + FLAGS_dash_add_last_segment_number_when_needed = false; +} } // namespace xml } // namespace shaka From d76d77854563a1cfbaf97c8e77a3de0c0c47d74b Mon Sep 17 00:00:00 2001 From: sr90 Date: Tue, 21 Apr 2020 01:11:55 -0700 Subject: [PATCH 03/10] Changes as per comments - 2 --- packager/mpd/base/representation.cc | 6 +----- packager/mpd/base/xml/xml_node.cc | 11 ++--------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/packager/mpd/base/representation.cc b/packager/mpd/base/representation.cc index 832b5fd85b1..c9018ef7bfc 100644 --- a/packager/mpd/base/representation.cc +++ b/packager/mpd/base/representation.cc @@ -340,11 +340,7 @@ void Representation::AddSegmentInfo(int64_t start_time, int64_t duration) { if (mpd_options_.mpd_params.target_segment_duration > 0 && mpd_options_.mpd_params.allow_approximate_segment_timeline) { - if (adjusted_duration == scaled_target_duration) { - start_number_ = start_time / scaled_target_duration + 1; - } else { - start_number_ = start_time / scaled_target_duration; - } + start_number_ = start_time / scaled_target_duration + 1; stream_just_started_ = true; } } diff --git a/packager/mpd/base/xml/xml_node.cc b/packager/mpd/base/xml/xml_node.cc index c14d4f68d3c..9f3dc198160 100644 --- a/packager/mpd/base/xml/xml_node.cc +++ b/packager/mpd/base/xml/xml_node.cc @@ -58,15 +58,8 @@ bool IsTimelineConstantDuration(const std::list& segment_infos, const SegmentInfo& first_segment = segment_infos.front(); - if (first_segment.duration < target_duration) { - if (static_cast(first_segment.start_time / target_duration) != - start_number) - return false; - } else { - if (static_cast(first_segment.start_time / - first_segment.duration) != start_number - 1) - return false; - } + if (first_segment.start_time / first_segment.duration != (start_number - 1)) + return false; if (segment_infos.size() == 1) return true; From 4c6b29e67b678b69e7afa55602b9de91be6cb65c Mon Sep 17 00:00:00 2001 From: sr90 Date: Mon, 27 Apr 2020 19:57:46 -0700 Subject: [PATCH 04/10] Changes as per comments - 3 --- packager/mpd/base/representation.cc | 3 +-- packager/mpd/base/xml/xml_node.cc | 20 ++++------------ packager/mpd/base/xml/xml_node.h | 3 +-- packager/mpd/base/xml/xml_node_unittest.cc | 28 +++++++++++----------- 4 files changed, 21 insertions(+), 33 deletions(-) diff --git a/packager/mpd/base/representation.cc b/packager/mpd/base/representation.cc index c9018ef7bfc..501f5f42c4c 100644 --- a/packager/mpd/base/representation.cc +++ b/packager/mpd/base/representation.cc @@ -270,8 +270,7 @@ xml::scoped_xml_ptr Representation::GetXml() { if (HasLiveOnlyFields(media_info_) && !representation.AddLiveOnlyInfo( - media_info_, segment_infos_, start_number_, - mpd_options_.mpd_params.target_segment_duration)) { + media_info_, segment_infos_, start_number_)) { LOG(ERROR) << "Failed to add Live info."; return xml::scoped_xml_ptr(); } diff --git a/packager/mpd/base/xml/xml_node.cc b/packager/mpd/base/xml/xml_node.cc index 9f3dc198160..7bb314eec74 100644 --- a/packager/mpd/base/xml/xml_node.cc +++ b/packager/mpd/base/xml/xml_node.cc @@ -47,8 +47,7 @@ std::string RangeToString(const Range& range) { // Check if segments are continuous and all segments except the last one are of // the same duration. bool IsTimelineConstantDuration(const std::list& segment_infos, - uint32_t start_number, - const double target_duration) { + uint32_t start_number) { if (!FLAGS_segment_template_constant_duration) return false; @@ -57,7 +56,6 @@ bool IsTimelineConstantDuration(const std::list& segment_infos, return false; const SegmentInfo& first_segment = segment_infos.front(); - if (first_segment.start_time / first_segment.duration != (start_number - 1)) return false; @@ -407,8 +405,7 @@ bool RepresentationXmlNode::AddVODOnlyInfo(const MediaInfo& media_info) { bool RepresentationXmlNode::AddLiveOnlyInfo( const MediaInfo& media_info, const std::list& segment_infos, - uint32_t start_number, - const double target_duration) { + uint32_t start_number) { XmlNode segment_template("SegmentTemplate"); if (media_info.has_reference_time_scale()) { segment_template.SetIntegerAttribute("timescale", @@ -434,16 +431,9 @@ bool RepresentationXmlNode::AddLiveOnlyInfo( if (!segment_infos.empty()) { // Don't use SegmentTimeline if all segments except the last one are of // the same duration. - if (IsTimelineConstantDuration( - segment_infos, start_number, - target_duration * media_info.reference_time_scale())) { - if (target_duration > 0) { - segment_template.SetIntegerAttribute( - "duration", target_duration * media_info.reference_time_scale()); - } else { - segment_template.SetIntegerAttribute("duration", - segment_infos.front().duration); - } + if (IsTimelineConstantDuration(segment_infos, start_number)) { + segment_template.SetIntegerAttribute("duration", + segment_infos.front().duration); if (FLAGS_dash_add_last_segment_number_when_needed) { uint32_t last_segment_number = start_number - 1; for (const auto& segment_info_element : segment_infos) diff --git a/packager/mpd/base/xml/xml_node.h b/packager/mpd/base/xml/xml_node.h index 858a56fb2e4..976730d4d3f 100644 --- a/packager/mpd/base/xml/xml_node.h +++ b/packager/mpd/base/xml/xml_node.h @@ -186,8 +186,7 @@ class RepresentationXmlNode : public RepresentationBaseXmlNode { /// SegmentInfos are sorted by its start time. bool AddLiveOnlyInfo(const MediaInfo& media_info, const std::list& segment_infos, - uint32_t start_number, - const double target_duration); + uint32_t start_number); private: // Add AudioChannelConfiguration element. Note that it is a required element diff --git a/packager/mpd/base/xml/xml_node_unittest.cc b/packager/mpd/base/xml/xml_node_unittest.cc index 45c20ee952e..870db0cbbab 100644 --- a/packager/mpd/base/xml/xml_node_unittest.cc +++ b/packager/mpd/base/xml/xml_node_unittest.cc @@ -302,8 +302,8 @@ TEST_F(LiveSegmentTimelineTest, OneSegmentInfo) { {kStartTime, kDuration, kRepeat}, }; RepresentationXmlNode representation; - ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, - kStartNumber, 0)); + ASSERT_TRUE( + representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); EXPECT_THAT( representation.GetRawPtr(), @@ -323,8 +323,8 @@ TEST_F(LiveSegmentTimelineTest, OneSegmentInfoNonZeroStartTime) { {kNonZeroStartTime, kDuration, kRepeat}, }; RepresentationXmlNode representation; - ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, - kStartNumber, 0)); + ASSERT_TRUE( + representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); EXPECT_THAT(representation.GetRawPtr(), XmlNodeEqual( @@ -347,8 +347,8 @@ TEST_F(LiveSegmentTimelineTest, OneSegmentInfoMatchingStartTimeAndNumber) { {kNonZeroStartTime, kDuration, kRepeat}, }; RepresentationXmlNode representation; - ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, - kStartNumber, 0)); + ASSERT_TRUE( + representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); EXPECT_THAT( representation.GetRawPtr(), @@ -374,8 +374,8 @@ TEST_F(LiveSegmentTimelineTest, AllSegmentsSameDurationExpectLastOne) { {kStartTime2, kDuration2, kRepeat2}, }; RepresentationXmlNode representation; - ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, - kStartNumber, 0)); + ASSERT_TRUE( + representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); EXPECT_THAT( representation.GetRawPtr(), @@ -401,8 +401,8 @@ TEST_F(LiveSegmentTimelineTest, SecondSegmentInfoNonZeroRepeat) { {kStartTime2, kDuration2, kRepeat2}, }; RepresentationXmlNode representation; - ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, - kStartNumber, 0)); + ASSERT_TRUE( + representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); EXPECT_THAT(representation.GetRawPtr(), XmlNodeEqual( @@ -433,8 +433,8 @@ TEST_F(LiveSegmentTimelineTest, TwoSegmentInfoWithGap) { {kStartTime2, kDuration2, kRepeat2}, }; RepresentationXmlNode representation; - ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, - kStartNumber, 0)); + ASSERT_TRUE( + representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); EXPECT_THAT(representation.GetRawPtr(), XmlNodeEqual( @@ -460,8 +460,8 @@ TEST_F(LiveSegmentTimelineTest, LastSegmentNumberSupplementalProperty) { RepresentationXmlNode representation; FLAGS_dash_add_last_segment_number_when_needed = true; - ASSERT_TRUE(representation.AddLiveOnlyInfo(media_info_, segment_infos, - kStartNumber, 0)); + ASSERT_TRUE( + representation.AddLiveOnlyInfo(media_info_, segment_infos, kStartNumber)); EXPECT_THAT( representation.GetRawPtr(), From 02c8730825fd4a9e81aaad8fba0bf818589af821 Mon Sep 17 00:00:00 2001 From: sr90 Date: Mon, 11 May 2020 17:13:28 -0700 Subject: [PATCH 05/10] Get start number from muxer --- packager/file/callback_file.cc | 8 + packager/file/callback_file.h | 1 + packager/file/file.h | 10 + packager/file/local_file.cc | 8 + packager/file/local_file.h | 1 + packager/file/memory_file.cc | 7 + packager/file/memory_file.h | 2 + packager/file/threaded_io_file.cc | 8 + packager/file/threaded_io_file.h | 2 + packager/file/udp_file.cc | 21 +- packager/file/udp_file.h | 1 + packager/media/base/media_handler.h | 1 + .../media/base/media_handler_test_base.cc | 1 + packager/media/chunking/chunking_handler.cc | 13 +- packager/media/chunking/chunking_handler.h | 2 +- packager/media/chunking/text_chunker.cc | 4 + packager/media/chunking/text_chunker.h | 1 + .../media/event/combined_muxer_listener.cc | 6 +- .../media/event/combined_muxer_listener.h | 3 +- .../media/event/hls_notify_muxer_listener.cc | 4 +- .../media/event/hls_notify_muxer_listener.h | 3 +- .../hls_notify_muxer_listener_unittest.cc | 24 +- packager/media/event/mock_muxer_listener.h | 8 +- .../media/event/mpd_notify_muxer_listener.cc | 32 +- .../media/event/mpd_notify_muxer_listener.h | 3 +- .../mpd_notify_muxer_listener_unittest.cc | 69 +- .../multi_codec_muxer_listener_unittest.cc | 10 +- packager/media/event/muxer_listener.h | 4 +- .../vod_media_info_dump_muxer_listener.cc | 10 +- .../vod_media_info_dump_muxer_listener.h | 3 +- ...media_info_dump_muxer_listener_unittest.cc | 22 +- packager/media/formats/mp2t/ts_muxer.cc | 3 +- packager/media/formats/mp2t/ts_segmenter.cc | 20 +- packager/media/formats/mp2t/ts_segmenter.h | 8 +- .../formats/mp2t/ts_segmenter_unittest.cc | 31 +- packager/media/formats/mp2t/ts_writer.cc | 30 +- packager/media/formats/mp2t/ts_writer.h | 6 + packager/media/formats/mp4/mp4_muxer.cc | 8 +- .../formats/mp4/multi_segment_segmenter.cc | 17 +- .../formats/mp4/multi_segment_segmenter.h | 6 +- packager/media/formats/mp4/segmenter.cc | 11 +- packager/media/formats/mp4/segmenter.h | 2 +- .../formats/mp4/single_segment_segmenter.cc | 23 +- .../formats/mp4/single_segment_segmenter.h | 2 +- .../packed_audio/packed_audio_writer.cc | 6 +- .../packed_audio_writer_unittest.cc | 24 +- .../webm/encrypted_segmenter_unittest.cc | 687 +++++++++++++----- .../formats/webm/multi_segment_segmenter.cc | 15 +- .../formats/webm/multi_segment_segmenter.h | 4 +- .../webm/multi_segment_segmenter_unittest.cc | 101 ++- packager/media/formats/webm/segmenter.cc | 15 +- packager/media/formats/webm/segmenter.h | 3 +- .../formats/webm/single_segment_segmenter.cc | 9 +- .../formats/webm/single_segment_segmenter.h | 7 +- .../webm/single_segment_segmenter_unittest.cc | 530 +++++++++++--- packager/media/formats/webm/webm_muxer.cc | 6 +- .../webvtt/webvtt_text_output_handler.cc | 7 +- .../webvtt_text_output_handler_unittest.cc | 15 +- packager/mpd/base/adaptation_set_unittest.cc | 35 +- packager/mpd/base/mock_mpd_builder.h | 7 +- packager/mpd/base/mock_mpd_notifier.h | 8 +- packager/mpd/base/mpd_builder_unittest.cc | 22 +- packager/mpd/base/mpd_notifier.h | 9 +- packager/mpd/base/representation.cc | 19 +- packager/mpd/base/representation.h | 8 +- packager/mpd/base/representation_unittest.cc | 8 +- packager/mpd/base/simple_mpd_notifier.cc | 5 +- packager/mpd/base/simple_mpd_notifier.h | 3 +- .../mpd/base/simple_mpd_notifier_unittest.cc | 7 +- 69 files changed, 1411 insertions(+), 608 deletions(-) diff --git a/packager/file/callback_file.cc b/packager/file/callback_file.cc index 55dae0a98e3..7f715681d65 100644 --- a/packager/file/callback_file.cc +++ b/packager/file/callback_file.cc @@ -65,4 +65,12 @@ bool CallbackFile::Open() { return ParseCallbackFileName(file_name(), &callback_params_, &name_); } +bool CallbackFile::Rename(const std::string& new_file_name) { + if (rename(file_name().c_str(), new_file_name.c_str()) != 0) { + return false; + } + set_file_name(new_file_name); + return true; +} + } // namespace shaka diff --git a/packager/file/callback_file.h b/packager/file/callback_file.h index 60e31a498fd..618cbb0bf82 100644 --- a/packager/file/callback_file.h +++ b/packager/file/callback_file.h @@ -28,6 +28,7 @@ class CallbackFile : public File { bool Flush() override; bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; + bool Rename(const std::string& new_file_name) override; /// @} protected: diff --git a/packager/file/file.h b/packager/file/file.h index 295fddb75c8..33e8115c05c 100644 --- a/packager/file/file.h +++ b/packager/file/file.h @@ -93,6 +93,16 @@ class File { /// off. const std::string& file_name() const { return file_name_; } + /// Set the file name. + void set_file_name(const std::string& newFileName) { + file_name_ = newFileName; + } + + /// Rename the file + /// @param new file name. + /// @return true on success, false otherwise. + virtual bool Rename(const std::string& new_file_name) = 0; + // ************************************************************ // * Static Methods: File-on-the-filesystem status // ************************************************************ diff --git a/packager/file/local_file.cc b/packager/file/local_file.cc index 4120df1812b..c6430a0ba32 100644 --- a/packager/file/local_file.cc +++ b/packager/file/local_file.cc @@ -200,4 +200,12 @@ bool LocalFile::Delete(const char* file_name) { return base::DeleteFile(base::FilePath::FromUTF8Unsafe(file_name), false); } +bool LocalFile::Rename(const std::string& new_file_name) { + if (rename(file_name().c_str(), new_file_name.c_str()) != 0) { + return false; + } + set_file_name(new_file_name); + return true; +} + } // namespace shaka diff --git a/packager/file/local_file.h b/packager/file/local_file.h index ecc3c0b62c3..c9e5a8b4a08 100644 --- a/packager/file/local_file.h +++ b/packager/file/local_file.h @@ -33,6 +33,7 @@ class LocalFile : public File { bool Flush() override; bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; + bool Rename(const std::string& new_file_name) override; /// @} /// Delete a local file. diff --git a/packager/file/memory_file.cc b/packager/file/memory_file.cc index 415a738723f..221124137cc 100644 --- a/packager/file/memory_file.cc +++ b/packager/file/memory_file.cc @@ -191,4 +191,11 @@ void MemoryFile::Delete(const std::string& file_name) { FileSystem::Instance()->Delete(file_name); } +bool MemoryFile::Rename(const std::string& new_file_name) { + if (rename(file_name().c_str(), new_file_name.c_str()) != 0) { + return false; + } + set_file_name(new_file_name); + return true; +} } // namespace shaka diff --git a/packager/file/memory_file.h b/packager/file/memory_file.h index 02f80199da5..a16857609de 100644 --- a/packager/file/memory_file.h +++ b/packager/file/memory_file.h @@ -31,6 +31,8 @@ class MemoryFile : public File { bool Flush() override; bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; + bool Rename(const std::string& new_file_name) override; + // { LOG(ERROR) << "NIKKI: In memory file"; return false;} /// @} /// Deletes all memory file data created. This assumes that there are no diff --git a/packager/file/threaded_io_file.cc b/packager/file/threaded_io_file.cc index ff8ca7313ac..83066d55373 100644 --- a/packager/file/threaded_io_file.cc +++ b/packager/file/threaded_io_file.cc @@ -183,6 +183,14 @@ void ThreadedIoFile::RunInInputMode() { } } +bool ThreadedIoFile::Rename(const std::string& new_file_name) { + if (rename(file_name().c_str(), new_file_name.c_str()) != 0) { + return false; + } + set_file_name(new_file_name); + return true; +} + void ThreadedIoFile::RunInOutputMode() { DCHECK(internal_file_); DCHECK_EQ(kOutputMode, mode_); diff --git a/packager/file/threaded_io_file.h b/packager/file/threaded_io_file.h index dacfe8c43d1..c1fb289fb3d 100644 --- a/packager/file/threaded_io_file.h +++ b/packager/file/threaded_io_file.h @@ -9,6 +9,7 @@ #include #include + #include "packager/base/synchronization/waitable_event.h" #include "packager/file/file.h" #include "packager/file/file_closer.h" @@ -35,6 +36,7 @@ class ThreadedIoFile : public File { bool Flush() override; bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; + bool Rename(const std::string& new_file_name) override; /// @} protected: diff --git a/packager/file/udp_file.cc b/packager/file/udp_file.cc index d7fb03505c0..2b54b18a018 100644 --- a/packager/file/udp_file.cc +++ b/packager/file/udp_file.cc @@ -27,7 +27,7 @@ // IP_MULTICAST_ALL has been supported since kernel version 2.6.31 but we may be // building on a machine that is older than that. #ifndef IP_MULTICAST_ALL -#define IP_MULTICAST_ALL 49 +#define IP_MULTICAST_ALL 49 #endif #endif // defined(OS_WIN) @@ -205,24 +205,20 @@ bool UdpFile::Open() { struct ip_mreq_source source_multicast_group; source_multicast_group.imr_multiaddr = local_in_addr; - if (inet_pton(AF_INET, - options->interface_address().c_str(), + if (inet_pton(AF_INET, options->interface_address().c_str(), &source_multicast_group.imr_interface) != 1) { LOG(ERROR) << "Malformed IPv4 interface address " << options->interface_address(); return false; } - if (inet_pton(AF_INET, - options->source_address().c_str(), + if (inet_pton(AF_INET, options->source_address().c_str(), &source_multicast_group.imr_sourceaddr) != 1) { LOG(ERROR) << "Malformed IPv4 source specific multicast address " << options->source_address(); return false; } - if (setsockopt(new_socket.get(), - IPPROTO_IP, - IP_ADD_SOURCE_MEMBERSHIP, + if (setsockopt(new_socket.get(), IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, reinterpret_cast(&source_multicast_group), sizeof(source_multicast_group)) < 0) { LOG(ERROR) << "Failed to join multicast group, error = " @@ -249,7 +245,7 @@ bool UdpFile::Open() { << GetSocketErrorCode(); return false; } - } + } #if defined(__linux__) // Disable IP_MULTICAST_ALL to avoid interference caused when two sockets @@ -294,4 +290,11 @@ bool UdpFile::Open() { return true; } +bool UdpFile::Rename(const std::string& new_file_name) { + if (rename(file_name().c_str(), new_file_name.c_str()) != 0) { + return false; + } + set_file_name(new_file_name); + return true; +} } // namespace shaka diff --git a/packager/file/udp_file.h b/packager/file/udp_file.h index 039563abdbc..55cbb36aa6f 100644 --- a/packager/file/udp_file.h +++ b/packager/file/udp_file.h @@ -38,6 +38,7 @@ class UdpFile : public File { bool Flush() override; bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; + bool Rename(const std::string& new_file_name) override; /// @} protected: diff --git a/packager/media/base/media_handler.h b/packager/media/base/media_handler.h index 18f4bc6482a..7dfd63f10e7 100644 --- a/packager/media/base/media_handler.h +++ b/packager/media/base/media_handler.h @@ -57,6 +57,7 @@ struct SegmentInfo { bool is_encrypted = false; int64_t start_timestamp = -1; int64_t duration = 0; + int64_t segment_index = 0; // This is only available if key rotation is enabled. Note that we may have // a |key_rotation_encryption_config| even if the segment is not encrypted, // which is the case for clear lead. diff --git a/packager/media/base/media_handler_test_base.cc b/packager/media/base/media_handler_test_base.cc index 87e42aa4003..0146843a1e9 100644 --- a/packager/media/base/media_handler_test_base.cc +++ b/packager/media/base/media_handler_test_base.cc @@ -249,6 +249,7 @@ std::unique_ptr MediaHandlerTestBase::GetSegmentInfo( info->start_timestamp = start_timestamp; info->duration = duration; info->is_subsegment = is_subsegment; + info->segment_index = start_timestamp / duration + 1; return info; } diff --git a/packager/media/chunking/chunking_handler.cc b/packager/media/chunking/chunking_handler.cc index a9686071d21..18ab3ffd2af 100644 --- a/packager/media/chunking/chunking_handler.cc +++ b/packager/media/chunking/chunking_handler.cc @@ -26,6 +26,10 @@ bool IsNewSegmentIndex(int64_t new_index, int64_t current_index) { new_index != current_index - 1; } +bool isGreaterSegmentIndex(int64_t new_index, int64_t current_index) { + return new_index > current_index; +} + } // namespace ChunkingHandler::ChunkingHandler(const ChunkingParams& chunking_params) @@ -60,6 +64,7 @@ Status ChunkingHandler::Process(std::unique_ptr stream_data) { } Status ChunkingHandler::OnFlushRequest(size_t input_stream_index) { + set_segment_index_++; RETURN_IF_ERROR(EndSegmentIfStarted()); return FlushDownstream(kStreamIndex); } @@ -74,6 +79,7 @@ Status ChunkingHandler::OnStreamInfo(std::shared_ptr info) { } Status ChunkingHandler::OnCueEvent(std::shared_ptr event) { + set_segment_index_++; RETURN_IF_ERROR(EndSegmentIfStarted()); const double event_time_in_seconds = event->time_in_seconds; RETURN_IF_ERROR(DispatchCueEvent(kStreamIndex, std::move(event))); @@ -89,7 +95,6 @@ Status ChunkingHandler::OnCueEvent(std::shared_ptr event) { Status ChunkingHandler::OnMediaSample( std::shared_ptr sample) { DCHECK_NE(time_scale_, 0u) << "kStreamInfo should arrive before kMediaSample"; - const int64_t timestamp = sample->pts(); bool started_new_segment = false; @@ -101,6 +106,11 @@ Status ChunkingHandler::OnMediaSample( : (timestamp - cue_offset_) / segment_duration_; if (!segment_start_time_ || IsNewSegmentIndex(segment_index, current_segment_index_)) { + if (!isGreaterSegmentIndex(segment_index, current_segment_index_)) { + set_segment_index_ = current_segment_index_ + 1; + } else { + set_segment_index_ = segment_index; + } current_segment_index_ = segment_index; // Reset subsegment index. current_subsegment_index_ = 0; @@ -151,6 +161,7 @@ Status ChunkingHandler::EndSegmentIfStarted() const { auto segment_info = std::make_shared(); segment_info->start_timestamp = segment_start_time_.value(); segment_info->duration = max_segment_time_ - segment_start_time_.value(); + segment_info->segment_index = set_segment_index_; return DispatchSegmentInfo(kStreamIndex, std::move(segment_info)); } diff --git a/packager/media/chunking/chunking_handler.h b/packager/media/chunking/chunking_handler.h index 8159c8758de..e7dd8333b68 100644 --- a/packager/media/chunking/chunking_handler.h +++ b/packager/media/chunking/chunking_handler.h @@ -72,7 +72,7 @@ class ChunkingHandler : public MediaHandler { // Segment and subsegment duration in stream's time scale. int64_t segment_duration_ = 0; int64_t subsegment_duration_ = 0; - + int64_t set_segment_index_ = 0; // Current segment index, useful to determine where to do chunking. int64_t current_segment_index_ = -1; // Current subsegment index, useful to determine where to do chunking. diff --git a/packager/media/chunking/text_chunker.cc b/packager/media/chunking/text_chunker.cc index ea43f69ec17..c3a549571a8 100644 --- a/packager/media/chunking/text_chunker.cc +++ b/packager/media/chunking/text_chunker.cc @@ -35,6 +35,7 @@ Status TextChunker::OnFlushRequest(size_t input_stream_index) { // Keep outputting segments until all the samples leave the system. Calling // |DispatchSegment| will remove samples over time. while (samples_in_current_segment_.size()) { + segment_index_++; RETURN_IF_ERROR(DispatchSegment(segment_duration_)); } @@ -107,6 +108,9 @@ Status TextChunker::DispatchSegment(int64_t duration) { std::shared_ptr info = std::make_shared(); info->start_timestamp = segment_start_; info->duration = duration; + segment_index_ = + std::max(((segment_start_ / segment_duration_) + 1), segment_index_); + info->segment_index = segment_index_; RETURN_IF_ERROR(DispatchSegmentInfo(kStreamIndex, std::move(info))); // Move onto the next segment. diff --git a/packager/media/chunking/text_chunker.h b/packager/media/chunking/text_chunker.h index 8e9e09accb4..b252d201bff 100644 --- a/packager/media/chunking/text_chunker.h +++ b/packager/media/chunking/text_chunker.h @@ -52,6 +52,7 @@ class TextChunker : public MediaHandler { int64_t segment_start_ = -1; // Set when the first sample comes in. int64_t segment_duration_ = -1; // Set in OnStreamInfo. + int64_t segment_index_ = 0; // All samples that make up the current segment. We must store the samples // until the segment ends because a cue event may end the segment sooner // than we expected. diff --git a/packager/media/event/combined_muxer_listener.cc b/packager/media/event/combined_muxer_listener.cc index 6c28e267056..ab6aaa31763 100644 --- a/packager/media/event/combined_muxer_listener.cc +++ b/packager/media/event/combined_muxer_listener.cc @@ -59,9 +59,11 @@ void CombinedMuxerListener::OnMediaEnd(const MediaRanges& media_ranges, void CombinedMuxerListener::OnNewSegment(const std::string& file_name, int64_t start_time, int64_t duration, - uint64_t segment_file_size) { + uint64_t segment_file_size, + uint64_t segment_index) { for (auto& listener : muxer_listeners_) { - listener->OnNewSegment(file_name, start_time, duration, segment_file_size); + listener->OnNewSegment(file_name, start_time, duration, segment_file_size, + segment_index); } } diff --git a/packager/media/event/combined_muxer_listener.h b/packager/media/event/combined_muxer_listener.h index 2528974ae7d..368871f5aba 100644 --- a/packager/media/event/combined_muxer_listener.h +++ b/packager/media/event/combined_muxer_listener.h @@ -42,7 +42,8 @@ class CombinedMuxerListener : public MuxerListener { void OnNewSegment(const std::string& file_name, int64_t start_time, int64_t duration, - uint64_t segment_file_size) override; + uint64_t segment_file_size, + uint64_t index_segment) override; void OnKeyFrame(int64_t timestamp, uint64_t start_byte_offset, uint64_t size); void OnCueEvent(int64_t timestamp, const std::string& cue_data) override; /// @} diff --git a/packager/media/event/hls_notify_muxer_listener.cc b/packager/media/event/hls_notify_muxer_listener.cc index fdd326c4b6a..4553269e992 100644 --- a/packager/media/event/hls_notify_muxer_listener.cc +++ b/packager/media/event/hls_notify_muxer_listener.cc @@ -7,6 +7,7 @@ #include "packager/media/event/hls_notify_muxer_listener.h" #include + #include "packager/base/logging.h" #include "packager/hls/base/hls_notifier.h" #include "packager/media/base/muxer_options.h" @@ -228,7 +229,8 @@ void HlsNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges, void HlsNotifyMuxerListener::OnNewSegment(const std::string& file_name, int64_t start_time, int64_t duration, - uint64_t segment_file_size) { + uint64_t segment_file_size, + uint64_t segment_index) { if (!media_info_->has_segment_template()) { EventInfo event_info; event_info.type = EventInfoType::kSegment; diff --git a/packager/media/event/hls_notify_muxer_listener.h b/packager/media/event/hls_notify_muxer_listener.h index ed0cb8a4183..8bd26c32427 100644 --- a/packager/media/event/hls_notify_muxer_listener.h +++ b/packager/media/event/hls_notify_muxer_listener.h @@ -67,7 +67,8 @@ class HlsNotifyMuxerListener : public MuxerListener { void OnNewSegment(const std::string& file_name, int64_t start_time, int64_t duration, - uint64_t segment_file_size) override; + uint64_t segment_file_size, + uint64_t segment_index) override; void OnKeyFrame(int64_t timestamp, uint64_t start_byte_offset, uint64_t size); void OnCueEvent(int64_t timestamp, const std::string& cue_data) override; /// @} diff --git a/packager/media/event/hls_notify_muxer_listener_unittest.cc b/packager/media/event/hls_notify_muxer_listener_unittest.cc index d0923a8f443..c73c9fa380c 100644 --- a/packager/media/event/hls_notify_muxer_listener_unittest.cc +++ b/packager/media/event/hls_notify_muxer_listener_unittest.cc @@ -4,13 +4,14 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd +#include "packager/media/event/hls_notify_muxer_listener.h" + #include #include #include "packager/hls/base/hls_notifier.h" #include "packager/media/base/muxer_options.h" #include "packager/media/base/protection_system_specific_info.h" -#include "packager/media/event/hls_notify_muxer_listener.h" #include "packager/media/event/muxer_listener_test_helper.h" namespace shaka { @@ -66,12 +67,15 @@ class MockHlsNotifier : public hls::HlsNotifier { // Doesn't really matter what the values are as long as it is a system ID (16 // bytes). const uint8_t kAnySystemId[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, }; const uint8_t kAnyData[] = { - 0xFF, 0x78, 0xAA, 0x6B, + 0xFF, + 0x78, + 0xAA, + 0x6B, }; const uint64_t kSegmentStartOffset = 10000; @@ -345,7 +349,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnNewSegmentAndCueEvent) { kSegmentDuration, _, kSegmentSize)); listener_.OnCueEvent(kCueStartTime, "dummy cue data"); listener_.OnNewSegment("new_segment_name10.ts", kSegmentStartTime, - kSegmentDuration, kSegmentSize); + kSegmentDuration, kSegmentSize, 10); } // Verify that the notifier is called for every segment in OnMediaEnd if @@ -363,7 +367,7 @@ TEST_F(HlsNotifyMuxerListenerTest, NoSegmentTemplateOnMediaEnd) { listener_.OnCueEvent(kCueStartTime, "dummy cue data"); listener_.OnNewSegment("filename.mp4", kSegmentStartTime, kSegmentDuration, - kSegmentSize); + kSegmentSize, 0); EXPECT_CALL(mock_notifier_, NotifyCueEvent(_, kCueStartTime)); EXPECT_CALL( @@ -393,7 +397,7 @@ TEST_F(HlsNotifyMuxerListenerTest, NoSegmentTemplateOnMediaEndTwice) { listener_.OnMediaStart(muxer_options1, *video_stream_info, 90000, MuxerListener::kContainerMpeg2ts); listener_.OnNewSegment("filename1.mp4", kSegmentStartTime, kSegmentDuration, - kSegmentSize); + kSegmentSize, 1); listener_.OnCueEvent(kCueStartTime, "dummy cue data"); EXPECT_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _)) @@ -410,7 +414,7 @@ TEST_F(HlsNotifyMuxerListenerTest, NoSegmentTemplateOnMediaEndTwice) { listener_.OnMediaStart(muxer_options2, *video_stream_info, 90000, MuxerListener::kContainerMpeg2ts); listener_.OnNewSegment("filename2.mp4", kSegmentStartTime + kSegmentDuration, - kSegmentDuration, kSegmentSize); + kSegmentDuration, kSegmentSize, 2); EXPECT_CALL(mock_notifier_, NotifyNewSegment(_, StrEq("filename2.mp4"), kSegmentStartTime + kSegmentDuration, _, _, _)); @@ -436,7 +440,7 @@ TEST_F(HlsNotifyMuxerListenerTest, MuxerListener::kContainerMpeg2ts); listener_.OnNewSegment("filename.mp4", kSegmentStartTime, kSegmentDuration, - kSegmentSize); + kSegmentSize, 0); EXPECT_CALL( mock_notifier_, NotifyNewSegment(_, StrEq("filename.mp4"), kSegmentStartTime, @@ -498,7 +502,7 @@ TEST_P(HlsNotifyMuxerListenerKeyFrameTest, NoSegmentTemplate) { listener_.OnKeyFrame(kKeyFrameTimestamp, kKeyFrameStartByteOffset, kKeyFrameSize); listener_.OnNewSegment("filename.mp4", kSegmentStartTime, kSegmentDuration, - kSegmentSize); + kSegmentSize, 0); EXPECT_CALL(mock_notifier_, NotifyKeyFrame(_, kKeyFrameTimestamp, diff --git a/packager/media/event/mock_muxer_listener.h b/packager/media/event/mock_muxer_listener.h index 818fb4bcbac..c74075fe7fe 100644 --- a/packager/media/event/mock_muxer_listener.h +++ b/packager/media/event/mock_muxer_listener.h @@ -53,14 +53,14 @@ class MockMuxerListener : public MuxerListener { // Windows 32 bit cannot mock MediaRanges because it has Optionals that use // memory alignment of 8 bytes. The compiler fails if it is mocked. - void OnMediaEnd(const MediaRanges& range, - float duration_seconds) override; + void OnMediaEnd(const MediaRanges& range, float duration_seconds) override; - MOCK_METHOD4(OnNewSegment, + MOCK_METHOD5(OnNewSegment, void(const std::string& segment_name, int64_t start_time, int64_t duration, - uint64_t segment_file_size)); + uint64_t segment_file_size, + uint64_t segment_index)); MOCK_METHOD3(OnKeyFrame, void(int64_t timestamp, diff --git a/packager/media/event/mpd_notify_muxer_listener.cc b/packager/media/event/mpd_notify_muxer_listener.cc index 749b7843cba..1b0b8137d64 100644 --- a/packager/media/event/mpd_notify_muxer_listener.cc +++ b/packager/media/event/mpd_notify_muxer_listener.cc @@ -57,17 +57,13 @@ void MpdNotifyMuxerListener::OnEncryptionInfoReady( void MpdNotifyMuxerListener::OnEncryptionStart() {} -void MpdNotifyMuxerListener::OnMediaStart( - const MuxerOptions& muxer_options, - const StreamInfo& stream_info, - uint32_t time_scale, - ContainerType container_type) { +void MpdNotifyMuxerListener::OnMediaStart(const MuxerOptions& muxer_options, + const StreamInfo& stream_info, + uint32_t time_scale, + ContainerType container_type) { std::unique_ptr media_info(new MediaInfo()); - if (!internal::GenerateMediaInfo(muxer_options, - stream_info, - time_scale, - container_type, - media_info.get())) { + if (!internal::GenerateMediaInfo(muxer_options, stream_info, time_scale, + container_type, media_info.get())) { LOG(ERROR) << "Failed to generate MediaInfo from input."; return; } @@ -79,7 +75,8 @@ void MpdNotifyMuxerListener::OnMediaStart( if (is_encrypted_) { internal::SetContentProtectionFields(protection_scheme_, default_key_id_, key_system_info_, media_info.get()); - media_info->mutable_protected_content()->set_include_mspr_pro(mpd_notifier_->include_mspr_pro()); + media_info->mutable_protected_content()->set_include_mspr_pro( + mpd_notifier_->include_mspr_pro()); } // The content may be splitted into multiple files, but their MediaInfo @@ -102,8 +99,7 @@ void MpdNotifyMuxerListener::OnMediaStart( // Record the sample duration in the media info for VOD so that OnMediaEnd, all // the information is in the media info. -void MpdNotifyMuxerListener::OnSampleDurationReady( - uint32_t sample_duration) { +void MpdNotifyMuxerListener::OnSampleDurationReady(uint32_t sample_duration) { if (mpd_notifier_->dash_profile() == DashProfile::kLive) { mpd_notifier_->NotifySampleDuration(notification_id_.value(), sample_duration); @@ -157,7 +153,10 @@ void MpdNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges, mpd_notifier_->NotifyNewSegment( notification_id_.value(), event_info.segment_info.start_time, event_info.segment_info.duration, - event_info.segment_info.segment_file_size); + event_info.segment_info.segment_file_size, + (event_info.segment_info.start_time / + event_info.segment_info.duration) + + 1); break; case EventInfoType::kKeyFrame: // NO-OP for DASH. @@ -175,10 +174,11 @@ void MpdNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges, void MpdNotifyMuxerListener::OnNewSegment(const std::string& file_name, int64_t start_time, int64_t duration, - uint64_t segment_file_size) { + uint64_t segment_file_size, + uint64_t segment_index) { if (mpd_notifier_->dash_profile() == DashProfile::kLive) { mpd_notifier_->NotifyNewSegment(notification_id_.value(), start_time, - duration, segment_file_size); + duration, segment_file_size, segment_index); if (mpd_notifier_->mpd_type() == MpdType::kDynamic) mpd_notifier_->Flush(); } else { diff --git a/packager/media/event/mpd_notify_muxer_listener.h b/packager/media/event/mpd_notify_muxer_listener.h index ffa84c1f865..112d55e3572 100644 --- a/packager/media/event/mpd_notify_muxer_listener.h +++ b/packager/media/event/mpd_notify_muxer_listener.h @@ -50,7 +50,8 @@ class MpdNotifyMuxerListener : public MuxerListener { void OnNewSegment(const std::string& file_name, int64_t start_time, int64_t duration, - uint64_t segment_file_size) override; + uint64_t segment_file_size, + uint64_t segment_index) override; void OnKeyFrame(int64_t timestamp, uint64_t start_byte_offset, uint64_t size); void OnCueEvent(int64_t timestamp, const std::string& cue_data) override; /// @} diff --git a/packager/media/event/mpd_notify_muxer_listener_unittest.cc b/packager/media/event/mpd_notify_muxer_listener_unittest.cc index 219fac18266..69a70e25349 100644 --- a/packager/media/event/mpd_notify_muxer_listener_unittest.cc +++ b/packager/media/event/mpd_notify_muxer_listener_unittest.cc @@ -10,6 +10,7 @@ #include #include #include + #include #include @@ -52,8 +53,8 @@ void SetDefaultLiveMuxerOptions(media::MuxerOptions* muxer_options) { } const uint8_t kBogusIv[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x83, 0xC3, 0x66, 0xEE, 0xAB, 0xB2, 0xF1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x83, 0xC3, 0x66, 0xEE, 0xAB, 0xB2, 0xF1, }; } // namespace @@ -62,15 +63,13 @@ namespace media { class MpdNotifyMuxerListenerTest : public ::testing::TestWithParam { public: - void SetupForVod() { MpdOptions mpd_options; mpd_options.dash_profile = DashProfile::kOnDemand; // On-demand profile should be static. mpd_options.mpd_type = MpdType::kStatic; notifier_.reset(new MockMpdNotifier(mpd_options)); - listener_.reset( - new MpdNotifyMuxerListener(notifier_.get())); + listener_.reset(new MpdNotifyMuxerListener(notifier_.get())); } void SetupForLive() { @@ -165,7 +164,9 @@ TEST_F(MpdNotifyMuxerListenerTest, VodEncryptedContent) { " protection_scheme: 'cenc'\n" " content_protection_entry {\n" " uuid: '00010203-0405-0607-0809-0a0b0c0d0e0f'\n" - " pssh: '" + std::string(kExpectedDefaultPsshBox) + "'\n" + " pssh: '" + + std::string(kExpectedDefaultPsshBox) + + "'\n" " }\n" " default_key_id: 'defaultkeyid'\n" " include_mspr_pro: 1\n" @@ -256,24 +257,24 @@ TEST_F(MpdNotifyMuxerListenerTest, VodOnNewSegment) { const uint64_t kSegmentFileSize2 = 83743u; EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0); - EXPECT_CALL(*notifier_, NotifyNewSegment(_, _, _, _)).Times(0); + EXPECT_CALL(*notifier_, NotifyNewSegment(_, _, _, _, _)).Times(0); listener_->OnMediaStart(muxer_options, *video_stream_info, kDefaultReferenceTimeScale, MuxerListener::kContainerMp4); - listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1); + listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1, 1); listener_->OnCueEvent(kStartTime2, "dummy cue data"); - listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2); + listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2, 1); ::testing::Mock::VerifyAndClearExpectations(notifier_.get()); InSequence s; EXPECT_CALL(*notifier_, NotifyNewContainer( ExpectMediaInfoEq(kExpectedDefaultMediaInfo), _)) .WillOnce(Return(true)); - EXPECT_CALL(*notifier_, - NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1)); + EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime1, kDuration1, + kSegmentFileSize1, 1)); EXPECT_CALL(*notifier_, NotifyCueEvent(_, kStartTime2)); - EXPECT_CALL(*notifier_, - NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2)); + EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime2, kDuration2, + kSegmentFileSize2, 1)); EXPECT_CALL(*notifier_, Flush()); FireOnMediaEndWithParams(GetDefaultOnMediaEndParams()); } @@ -307,11 +308,11 @@ TEST_F(MpdNotifyMuxerListenerTest, VodMultipleFiles) { // Expectation for first file before OnMediaEnd. EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0); - EXPECT_CALL(*notifier_, NotifyNewSegment(_, _, _, _)).Times(0); + EXPECT_CALL(*notifier_, NotifyNewSegment(_, _, _, _, _)).Times(0); listener_->OnMediaStart(muxer_options1, *video_stream_info, kDefaultReferenceTimeScale, MuxerListener::kContainerMp4); - listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1); + listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1, 1); listener_->OnCueEvent(kStartTime2, "dummy cue data"); ::testing::Mock::VerifyAndClearExpectations(notifier_.get()); @@ -320,8 +321,8 @@ TEST_F(MpdNotifyMuxerListenerTest, VodMultipleFiles) { EXPECT_CALL(*notifier_, NotifyNewContainer(EqualsProto(expected_media_info1), _)) .WillOnce(Return(true)); - EXPECT_CALL(*notifier_, - NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1)); + EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime1, kDuration1, + kSegmentFileSize1, 1)); EXPECT_CALL(*notifier_, NotifyCueEvent(_, kStartTime2)); EXPECT_CALL(*notifier_, Flush()); FireOnMediaEndWithParams(GetDefaultOnMediaEndParams()); @@ -330,13 +331,13 @@ TEST_F(MpdNotifyMuxerListenerTest, VodMultipleFiles) { listener_->OnMediaStart(muxer_options2, *video_stream_info, kDefaultReferenceTimeScale, MuxerListener::kContainerMp4); - listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2); + listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2, 1); // Expectation for second file OnMediaEnd. EXPECT_CALL(*notifier_, NotifyMediaInfoUpdate(_, EqualsProto(expected_media_info2))); - EXPECT_CALL(*notifier_, - NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2)); + EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime2, kDuration2, + kSegmentFileSize2, 1)); EXPECT_CALL(*notifier_, Flush()); FireOnMediaEndWithParams(GetDefaultOnMediaEndParams()); } @@ -369,7 +370,9 @@ TEST_P(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) { " default_key_id: \"defaultkeyid\"\n" " content_protection_entry {\n" " uuid: '00010203-0405-0607-0809-0a0b0c0d0e0f'\n" - " pssh: \"" + std::string(kExpectedDefaultPsshBox) + "\"\n" + " pssh: \"" + + std::string(kExpectedDefaultPsshBox) + + "\"\n" " }\n" " protection_scheme: 'cbcs'\n" " include_mspr_pro: 1\n" @@ -389,14 +392,14 @@ TEST_P(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) { EXPECT_CALL(*notifier_, NotifyNewContainer(ExpectMediaInfoEq(kExpectedMediaInfo), _)) .WillOnce(Return(true)); - EXPECT_CALL(*notifier_, - NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1)); + EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime1, kDuration1, + kSegmentFileSize1, 0)); // Flush should only be called once in OnMediaEnd. if (GetParam() == MpdType::kDynamic) EXPECT_CALL(*notifier_, Flush()); EXPECT_CALL(*notifier_, NotifyCueEvent(_, kStartTime2)); - EXPECT_CALL(*notifier_, - NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2)); + EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime2, kDuration2, + kSegmentFileSize2, 0)); if (GetParam() == MpdType::kDynamic) EXPECT_CALL(*notifier_, Flush()); @@ -407,9 +410,9 @@ TEST_P(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) { listener_->OnMediaStart(muxer_options, *video_stream_info, kDefaultReferenceTimeScale, MuxerListener::kContainerMp4); - listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1); + listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1, 0); listener_->OnCueEvent(kStartTime2, "dummy cue data"); - listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2); + listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2, 0); ::testing::Mock::VerifyAndClearExpectations(notifier_.get()); EXPECT_CALL(*notifier_, Flush()) @@ -462,13 +465,13 @@ TEST_P(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) { NotifyNewContainer(ExpectMediaInfoEq(kExpectedMediaInfo), _)) .WillOnce(Return(true)); EXPECT_CALL(*notifier_, NotifyEncryptionUpdate(_, _, _, _)).Times(1); - EXPECT_CALL(*notifier_, - NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1)); + EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime1, kDuration1, + kSegmentFileSize1, 0)); // Flush should only be called once in OnMediaEnd. if (GetParam() == MpdType::kDynamic) EXPECT_CALL(*notifier_, Flush()); - EXPECT_CALL(*notifier_, - NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2)); + EXPECT_CALL(*notifier_, NotifyNewSegment(_, kStartTime2, kDuration2, + kSegmentFileSize2, 0)); if (GetParam() == MpdType::kDynamic) EXPECT_CALL(*notifier_, Flush()); @@ -482,8 +485,8 @@ TEST_P(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) { listener_->OnEncryptionInfoReady(kNonInitialEncryptionInfo, FOURCC_cbc1, std::vector(), iv, GetDefaultKeySystemInfo()); - listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1); - listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2); + listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1, 0); + listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2, 0); ::testing::Mock::VerifyAndClearExpectations(notifier_.get()); EXPECT_CALL(*notifier_, Flush()) diff --git a/packager/media/event/multi_codec_muxer_listener_unittest.cc b/packager/media/event/multi_codec_muxer_listener_unittest.cc index acce6036b9b..86f4a420939 100644 --- a/packager/media/event/multi_codec_muxer_listener_unittest.cc +++ b/packager/media/event/multi_codec_muxer_listener_unittest.cc @@ -79,10 +79,10 @@ TEST_F(MultiCodecMuxerListenerTest, OnNewSegmentAfterOnMediaStartSingleCodec) { EXPECT_CALL(*listener_for_first_codec_, OnNewSegment(StrEq("new_segment_name10.ts"), kSegmentStartTime, - kSegmentDuration, kSegmentSize)); + kSegmentDuration, kSegmentSize, 10)); multi_codec_listener_.OnNewSegment("new_segment_name10.ts", kSegmentStartTime, - kSegmentDuration, kSegmentSize); + kSegmentDuration, kSegmentSize, 10); } TEST_F(MultiCodecMuxerListenerTest, OnMediaStartTwoCodecs) { @@ -114,13 +114,13 @@ TEST_F(MultiCodecMuxerListenerTest, OnNewSegmentAfterOnMediaStartTwoCodecs) { EXPECT_CALL(*listener_for_first_codec_, OnNewSegment(StrEq("new_segment_name10.ts"), kSegmentStartTime, - kSegmentDuration, kSegmentSize)); + kSegmentDuration, kSegmentSize, 10)); EXPECT_CALL(*listener_for_second_codec_, OnNewSegment(StrEq("new_segment_name10.ts"), kSegmentStartTime, - kSegmentDuration, kSegmentSize)); + kSegmentDuration, kSegmentSize, 10)); multi_codec_listener_.OnNewSegment("new_segment_name10.ts", kSegmentStartTime, - kSegmentDuration, kSegmentSize); + kSegmentDuration, kSegmentSize, 10); } } // namespace media diff --git a/packager/media/event/muxer_listener.h b/packager/media/event/muxer_listener.h index 65946c124ed..72d83337288 100644 --- a/packager/media/event/muxer_listener.h +++ b/packager/media/event/muxer_listener.h @@ -124,10 +124,12 @@ class MuxerListener { /// @param duration is the duration of the segment, relative to the timescale /// specified by MediaInfo passed to OnMediaStart(). /// @param segment_file_size is the segment size in bytes. + /// @param segment_index is the segment index. virtual void OnNewSegment(const std::string& segment_name, int64_t start_time, int64_t duration, - uint64_t segment_file_size) = 0; + uint64_t segment_file_size, + uint64_t segment_index) = 0; /// Called when there is a new key frame. For Video only. Note that it should /// be called before OnNewSegment is called on the containing segment. diff --git a/packager/media/event/vod_media_info_dump_muxer_listener.cc b/packager/media/event/vod_media_info_dump_muxer_listener.cc index e1f59c4948d..b23b8a304db 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener.cc @@ -49,11 +49,8 @@ void VodMediaInfoDumpMuxerListener::OnMediaStart( ContainerType container_type) { DCHECK(muxer_options.segment_template.empty()); media_info_.reset(new MediaInfo()); - if (!internal::GenerateMediaInfo(muxer_options, - stream_info, - time_scale, - container_type, - media_info_.get())) { + if (!internal::GenerateMediaInfo(muxer_options, stream_info, time_scale, + container_type, media_info_.get())) { LOG(ERROR) << "Failed to generate MediaInfo from input."; return; } @@ -90,7 +87,8 @@ void VodMediaInfoDumpMuxerListener::OnMediaEnd(const MediaRanges& media_ranges, void VodMediaInfoDumpMuxerListener::OnNewSegment(const std::string& file_name, int64_t start_time, int64_t duration, - uint64_t segment_file_size) { + uint64_t segment_file_size, + uint64_t segment_index) { const double segment_duration_seconds = static_cast(duration) / media_info_->reference_time_scale(); diff --git a/packager/media/event/vod_media_info_dump_muxer_listener.h b/packager/media/event/vod_media_info_dump_muxer_listener.h index 19044d472bc..08cdc7d2998 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.h +++ b/packager/media/event/vod_media_info_dump_muxer_listener.h @@ -49,7 +49,8 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener { void OnNewSegment(const std::string& file_name, int64_t start_time, int64_t duration, - uint64_t segment_file_size) override; + uint64_t segment_file_size, + uint64_t segment_index) override; void OnKeyFrame(int64_t timestamp, uint64_t start_byte_offset, uint64_t size); void OnCueEvent(int64_t timestamp, const std::string& cue_data) override; /// @} diff --git a/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc b/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc index 9e74b191132..28ca608046e 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd +#include "packager/media/event/vod_media_info_dump_muxer_listener.h" + #include #include #include @@ -18,7 +20,6 @@ #include "packager/media/base/muxer_options.h" #include "packager/media/base/video_stream_info.h" #include "packager/media/event/muxer_listener_test_helper.h" -#include "packager/media/event/vod_media_info_dump_muxer_listener.h" #include "packager/mpd/base/media_info.pb.h" namespace { @@ -29,8 +30,8 @@ const uint8_t kBogusDefaultKeyId[] = {0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x5f, 0x69, 0x64, 0x5f}; const uint8_t kBogusIv[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x83, 0xC3, 0x66, 0xEE, 0xAB, 0xB2, 0xF1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x83, 0xC3, 0x66, 0xEE, 0xAB, 0xB2, 0xF1, }; const bool kInitialEncryptionInfo = true; @@ -70,17 +71,14 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test { ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_)); DLOG(INFO) << "Created temp file: " << temp_file_path_.value(); - listener_.reset(new VodMediaInfoDumpMuxerListener(temp_file_path_ - .AsUTF8Unsafe())); + listener_.reset( + new VodMediaInfoDumpMuxerListener(temp_file_path_.AsUTF8Unsafe())); } - void TearDown() override { - base::DeleteFile(temp_file_path_, false); - } + void TearDown() override { base::DeleteFile(temp_file_path_, false); } - void FireOnMediaStartWithDefaultMuxerOptions( - const StreamInfo& stream_info, - bool enable_encryption) { + void FireOnMediaStartWithDefaultMuxerOptions(const StreamInfo& stream_info, + bool enable_encryption) { MuxerOptions muxer_options; SetDefaultMuxerOptions(&muxer_options); const uint32_t kReferenceTimeScale = 1000; @@ -100,7 +98,7 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test { void FireOnNewSegmentWithParams(const OnNewSegmentParameters& params) { listener_->OnNewSegment(params.file_name, params.start_time, - params.duration, params.segment_file_size); + params.duration, params.segment_file_size, 0); } void FireOnMediaEndWithParams(const OnMediaEndParameters& params) { diff --git a/packager/media/formats/mp2t/ts_muxer.cc b/packager/media/formats/mp2t/ts_muxer.cc index bd005afa047..cfa5d5262a0 100644 --- a/packager/media/formats/mp2t/ts_muxer.cc +++ b/packager/media/formats/mp2t/ts_muxer.cc @@ -43,7 +43,8 @@ Status TsMuxer::FinalizeSegment(size_t stream_id, return segment_info.is_subsegment ? Status::OK : segmenter_->FinalizeSegment(segment_info.start_timestamp, - segment_info.duration); + segment_info.duration, + segment_info.segment_index); } void TsMuxer::FireOnMediaStartEvent() { diff --git a/packager/media/formats/mp2t/ts_segmenter.cc b/packager/media/formats/mp2t/ts_segmenter.cc index aaea5ced179..2feca59ac9f 100644 --- a/packager/media/formats/mp2t/ts_segmenter.cc +++ b/packager/media/formats/mp2t/ts_segmenter.cc @@ -131,6 +131,7 @@ Status TsSegmenter::OpenNewSegmentIfClosed(int64_t next_pts) { const std::string segment_name = GetSegmentName(muxer_options_.segment_template, next_pts, segment_number_++, muxer_options_.bandwidth); + next_pts_ = next_pts; if (!ts_writer_->NewSegment(segment_name)) return Status(error::MUXER_FAILURE, "Failed to initilize TsPacketWriter."); current_segment_path_ = segment_name; @@ -168,11 +169,12 @@ Status TsSegmenter::WritePesPacketsToFile() { return Status::OK; } +// OPTION : Set segment_number_ data member here. Status TsSegmenter::FinalizeSegment(uint64_t start_timestamp, - uint64_t duration) { + uint64_t duration, + uint64_t segment_index) { if (!pes_packet_generator_->Flush()) { - return Status(error::MUXER_FAILURE, - "Failed to flush PesPacketGenerator."); + return Status(error::MUXER_FAILURE, "Failed to flush PesPacketGenerator."); } Status status = WritePesPacketsToFile(); if (!status.ok()) @@ -181,6 +183,15 @@ Status TsSegmenter::FinalizeSegment(uint64_t start_timestamp, // This method may be called from Finalize() so ts_writer_file_opened_ could // be false. if (ts_writer_file_opened_) { + // RENAME FILE: + std::string segment_name_segment_index = + GetSegmentName(muxer_options_.segment_template, next_pts_, + segment_index - 1, muxer_options_.bandwidth); + + if (!ts_writer_->RenameFile(segment_name_segment_index)) { + LOG(ERROR) << "TS Error renaming the file"; + } + if (!ts_writer_->FinalizeSegment()) { return Status(error::MUXER_FAILURE, "Failed to finalize TsWriter."); } @@ -190,7 +201,8 @@ Status TsSegmenter::FinalizeSegment(uint64_t start_timestamp, listener_->OnNewSegment(current_segment_path_, start_timestamp * timescale_scale_ + transport_stream_timestamp_offset_, - duration * timescale_scale_, file_size); + duration * timescale_scale_, file_size, + segment_index); } ts_writer_file_opened_ = false; } diff --git a/packager/media/formats/mp2t/ts_segmenter.h b/packager/media/formats/mp2t/ts_segmenter.h index b140040eb71..b5f7c801f81 100644 --- a/packager/media/formats/mp2t/ts_segmenter.h +++ b/packager/media/formats/mp2t/ts_segmenter.h @@ -8,6 +8,7 @@ #define PACKAGER_MEDIA_FORMATS_MP2T_TS_SEGMENTER_H_ #include + #include "packager/file/file.h" #include "packager/media/base/muxer_options.h" #include "packager/media/formats/mp2t/pes_packet_generator.h" @@ -55,10 +56,13 @@ class TsSegmenter { /// stream's time scale. /// @param duration is the segment's duration in the input stream's time /// scale. + /// @param segment_index is the segment index. // TODO(kqyang): Remove the usage of segment start timestamp and duration in // xx_segmenter, which could cause confusions on which is the source of truth // as the segment start timestamp and duration could be tracked locally. - Status FinalizeSegment(uint64_t start_timestamp, uint64_t duration); + Status FinalizeSegment(uint64_t start_timestamp, + uint64_t duration, + uint64_t segment_index); /// Only for testing. void InjectTsWriterForTesting(std::unique_ptr writer); @@ -70,6 +74,8 @@ class TsSegmenter { /// Only for testing. void SetTsWriterFileOpenedForTesting(bool value); + int next_pts_ = -1; + private: Status OpenNewSegmentIfClosed(int64_t next_pts); diff --git a/packager/media/formats/mp2t/ts_segmenter_unittest.cc b/packager/media/formats/mp2t/ts_segmenter_unittest.cc index aa0fd31a9f1..d6bfaf0e9fb 100644 --- a/packager/media/formats/mp2t/ts_segmenter_unittest.cc +++ b/packager/media/formats/mp2t/ts_segmenter_unittest.cc @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd +#include "packager/media/formats/mp2t/ts_segmenter.h" + #include #include @@ -12,18 +14,17 @@ #include "packager/media/event/mock_muxer_listener.h" #include "packager/media/formats/mp2t/pes_packet.h" #include "packager/media/formats/mp2t/program_map_table_writer.h" -#include "packager/media/formats/mp2t/ts_segmenter.h" #include "packager/status_test_util.h" namespace shaka { namespace media { namespace mp2t { +using ::testing::_; using ::testing::InSequence; using ::testing::Return; using ::testing::Sequence; using ::testing::StrEq; -using ::testing::_; namespace { @@ -50,7 +51,9 @@ const uint8_t kNaluLengthSize = 1; const bool kIsEncrypted = false; const uint8_t kAnyData[] = { - 0x01, 0x0F, 0x3C, + 0x01, + 0x0F, + 0x3C, }; class MockPesPacketGenerator : public PesPacketGenerator { @@ -84,6 +87,7 @@ class MockTsWriter : public TsWriter { MOCK_METHOD1(NewSegment, bool(const std::string& file_name)); MOCK_METHOD0(SignalEncrypted, void()); MOCK_METHOD0(FinalizeSegment, bool()); + MOCK_METHOD1(RenameFile, bool(const std::string& file_name)); // Similar to the hack above but takes a std::unique_ptr. MOCK_METHOD1(AddPesPacketMock, bool(PesPacket* pes_packet)); @@ -159,8 +163,7 @@ TEST_F(TsSegmenterTest, AddSample) { .InSequence(ready_pes_sequence) .WillOnce(Return(0u)); - EXPECT_CALL(*mock_ts_writer_, AddPesPacketMock(_)) - .WillOnce(Return(true)); + EXPECT_CALL(*mock_ts_writer_, AddPesPacketMock(_)).WillOnce(Return(true)); // The pointer is released inside the segmenter. EXPECT_CALL(*mock_pes_packet_generator_, GetNextPesPacketMock()) @@ -190,7 +193,6 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { MockMuxerListener mock_listener; TsSegmenter segmenter(options, &mock_listener); - const uint32_t kFirstPts = 1000; EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_)) @@ -209,7 +211,7 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { // duration. EXPECT_CALL(mock_listener, OnNewSegment("file1.ts", kFirstPts * kTimeScale / kInputTimescale, - kTimeScale * 11, _)); + kTimeScale * 11, _, _)); Sequence writer_sequence; EXPECT_CALL(*mock_ts_writer_, NewSegment(StrEq("file1.ts"))) @@ -219,7 +221,6 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { EXPECT_CALL(*mock_pes_packet_generator_, PushSample(_)) .Times(2) .WillRepeatedly(Return(true)); - Sequence ready_pes_sequence; // First AddSample(). EXPECT_CALL(*mock_pes_packet_generator_, NumberOfReadyPesPackets()) @@ -240,12 +241,12 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { .InSequence(ready_pes_sequence) .WillOnce(Return(0u)); - EXPECT_CALL(*mock_pes_packet_generator_, Flush()) - .WillOnce(Return(true)); + EXPECT_CALL(*mock_pes_packet_generator_, Flush()).WillOnce(Return(true)); EXPECT_CALL(*mock_ts_writer_, FinalizeSegment()) .InSequence(writer_sequence) .WillOnce(Return(true)); + EXPECT_CALL(*mock_ts_writer_, NewSegment(StrEq("file2.ts"))) .InSequence(writer_sequence) .WillOnce(Return(true)); @@ -268,7 +269,7 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { EXPECT_OK(segmenter.Initialize(*stream_info)); segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_)); EXPECT_OK(segmenter.AddSample(*sample1)); - EXPECT_OK(segmenter.FinalizeSegment(kFirstPts, sample1->duration())); + EXPECT_OK(segmenter.FinalizeSegment(kFirstPts, sample1->duration(), 1)); EXPECT_OK(segmenter.AddSample(*sample2)); } @@ -326,7 +327,7 @@ TEST_F(TsSegmenterTest, FinalizeSegment) { EXPECT_OK(segmenter.Initialize(*stream_info)); segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_)); segmenter.SetTsWriterFileOpenedForTesting(true); - EXPECT_OK(segmenter.FinalizeSegment(0, 100 /* arbitrary duration */)); + EXPECT_OK(segmenter.FinalizeSegment(0, 100 /* arbitrary duration */, 1)); } TEST_F(TsSegmenterTest, EncryptedSample) { @@ -351,7 +352,9 @@ TEST_F(TsSegmenterTest, EncryptedSample) { ON_CALL(*mock_pes_packet_generator_, Flush()).WillByDefault(Return(true)); const uint8_t kAnyData[] = { - 0x01, 0x0F, 0x3C, + 0x01, + 0x0F, + 0x3C, }; std::shared_ptr sample1 = MediaSample::CopyFrom(kAnyData, arraysize(kAnyData), kIsKeyFrame); @@ -406,7 +409,7 @@ TEST_F(TsSegmenterTest, EncryptedSample) { segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_)); EXPECT_OK(segmenter.AddSample(*sample1)); - EXPECT_OK(segmenter.FinalizeSegment(1, sample1->duration())); + EXPECT_OK(segmenter.FinalizeSegment(1, sample1->duration(), 1)); // Signal encrypted if sample is encrypted. EXPECT_CALL(*mock_ts_writer_raw, SignalEncrypted()); sample2->set_is_encrypted(true); diff --git a/packager/media/formats/mp2t/ts_writer.cc b/packager/media/formats/mp2t/ts_writer.cc index d8dc540178f..3dc35a1c4e1 100644 --- a/packager/media/formats/mp2t/ts_writer.cc +++ b/packager/media/formats/mp2t/ts_writer.cc @@ -30,18 +30,23 @@ const uint8_t kProgramAssociationTableId = 0x00; const uint8_t kPat[] = { 0x00, // pointer field kProgramAssociationTableId, - 0xB0, // The last 2 '00' assumes that this PAT is not very long. - 0x0D, // Length of the rest of this array. - 0x00, 0x00, // Transport stream ID is 0. - 0xC1, // version number 0, current next indicator 1. - 0x00, // section number - 0x00, // last section number + 0xB0, // The last 2 '00' assumes that this PAT is not very long. + 0x0D, // Length of the rest of this array. + 0x00, + 0x00, // Transport stream ID is 0. + 0xC1, // version number 0, current next indicator 1. + 0x00, // section number + 0x00, // last section number // program number -> PMT PID mapping. - 0x00, 0x01, // program number is 1. - 0xE0, // first 3 bits is reserved. + 0x00, + 0x01, // program number is 1. + 0xE0, // first 3 bits is reserved. ProgramMapTableWriter::kPmtPid, // CRC32. - 0xF9, 0x62, 0xF5, 0x8B, + 0xF9, + 0x62, + 0xF5, + 0x8B, }; const bool kHasPcr = true; @@ -51,8 +56,7 @@ const bool kPayloadUnitStartIndicator = true; // without adaptation field or the payload. const int kTsPacketHeaderSize = 4; const int kTsPacketSize = 188; -const int kTsPacketMaximumPayloadSize = - kTsPacketSize - kTsPacketHeaderSize; +const int kTsPacketMaximumPayloadSize = kTsPacketSize - kTsPacketHeaderSize; const size_t kMaxPesPacketLengthValue = 0xFFFF; @@ -222,6 +226,10 @@ base::Optional TsWriter::GetFilePosition() { : base::nullopt; } +bool TsWriter::RenameFile(const std::string& new_segment_name) { + return file()->Rename(new_segment_name); +} + } // namespace mp2t } // namespace media } // namespace shaka diff --git a/packager/media/formats/mp2t/ts_writer.h b/packager/media/formats/mp2t/ts_writer.h index 43e4c0d09ab..244eb7e1d25 100644 --- a/packager/media/formats/mp2t/ts_writer.h +++ b/packager/media/formats/mp2t/ts_writer.h @@ -54,6 +54,12 @@ class TsWriter { /// @return current file position on success, nullopt otherwise. base::Optional GetFilePosition(); + File* file() { return current_file_.get(); } + + /// Rename the file to newSegmentName. + /// @param new_segment_name + virtual bool RenameFile(const std::string& new_segment_name); + private: TsWriter(const TsWriter&) = delete; TsWriter& operator=(const TsWriter&) = delete; diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index 336f13a0399..41c5eb7d196 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -34,9 +34,7 @@ namespace { // Sets the range start and end value from offset and size. // |start| and |end| are for byte-range-spec specified in RFC2616. -void SetStartAndEndFromOffsetAndSize(size_t offset, - size_t size, - Range* range) { +void SetStartAndEndFromOffsetAndSize(size_t offset, size_t size, Range* range) { DCHECK(range); range->start = static_cast(offset); // Note that ranges are inclusive. So we need - 1. @@ -194,7 +192,7 @@ Status MP4Muxer::FinalizeSegment(size_t stream_id, DCHECK(segmenter_); VLOG(3) << "Finalizing " << (segment_info.is_subsegment ? "sub" : "") << "segment " << segment_info.start_timestamp << " duration " - << segment_info.duration; + << segment_info.duration << " index " << segment_info.segment_index; return segmenter_->FinalizeSegment(stream_id, segment_info); } @@ -456,7 +454,7 @@ bool MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info, AudioSampleEntry audio; audio.format = CodecToFourCC(audio_info->codec(), H26xStreamFormat::kUnSpecified); - switch(audio_info->codec()){ + switch (audio_info->codec()) { case kCodecAAC: { audio.esds.es_descriptor.set_esid(track_id); DecoderConfigDescriptor* decoder_config = diff --git a/packager/media/formats/mp4/multi_segment_segmenter.cc b/packager/media/formats/mp4/multi_segment_segmenter.cc index cf12e4c1705..cae3f74187d 100644 --- a/packager/media/formats/mp4/multi_segment_segmenter.cc +++ b/packager/media/formats/mp4/multi_segment_segmenter.cc @@ -28,8 +28,7 @@ MultiSegmentSegmenter::MultiSegmentSegmenter(const MuxerOptions& options, std::unique_ptr ftyp, std::unique_ptr moov) : Segmenter(options, std::move(ftyp), std::move(moov)), - styp_(new SegmentType), - num_segments_(0) { + styp_(new SegmentType) { // Use the same brands for styp as ftyp. styp_->major_brand = Segmenter::ftyp()->major_brand; styp_->compatible_brands = Segmenter::ftyp()->compatible_brands; @@ -67,8 +66,8 @@ Status MultiSegmentSegmenter::DoFinalize() { return Status::OK; } -Status MultiSegmentSegmenter::DoFinalizeSegment() { - return WriteSegment(); +Status MultiSegmentSegmenter::DoFinalizeSegment(uint64_t segment_index) { + return WriteSegment(segment_index); } Status MultiSegmentSegmenter::WriteInitSegment() { @@ -87,7 +86,7 @@ Status MultiSegmentSegmenter::WriteInitSegment() { return buffer->WriteToFile(file.get()); } -Status MultiSegmentSegmenter::WriteSegment() { +Status MultiSegmentSegmenter::WriteSegment(uint64_t segment_index) { DCHECK(sidx()); DCHECK(fragment_buffer()); DCHECK(styp_); @@ -112,7 +111,7 @@ Status MultiSegmentSegmenter::WriteSegment() { } else { file_name = GetSegmentName(options().segment_template, sidx()->earliest_presentation_time, - num_segments_++, options().bandwidth); + segment_index - 1, options().bandwidth); file.reset(File::Open(file_name.c_str(), "w")); if (!file) { return Status(error::FILE_FAILURE, @@ -157,9 +156,9 @@ Status MultiSegmentSegmenter::WriteSegment() { UpdateProgress(segment_duration); if (muxer_listener()) { muxer_listener()->OnSampleDurationReady(sample_duration()); - muxer_listener()->OnNewSegment(file_name, - sidx()->earliest_presentation_time, - segment_duration, segment_size); + muxer_listener()->OnNewSegment( + file_name, sidx()->earliest_presentation_time, segment_duration, + segment_size, segment_index); } return Status::OK; diff --git a/packager/media/formats/mp4/multi_segment_segmenter.h b/packager/media/formats/mp4/multi_segment_segmenter.h index b1c524da216..8ef7258e842 100644 --- a/packager/media/formats/mp4/multi_segment_segmenter.h +++ b/packager/media/formats/mp4/multi_segment_segmenter.h @@ -33,19 +33,17 @@ class MultiSegmentSegmenter : public Segmenter { bool GetIndexRange(size_t* offset, size_t* size) override; std::vector GetSegmentRanges() override; /// @} - private: // Segmenter implementation overrides. Status DoInitialize() override; Status DoFinalize() override; - Status DoFinalizeSegment() override; + Status DoFinalizeSegment(uint64_t segment_index) override; // Write segment to file. Status WriteInitSegment(); - Status WriteSegment(); + Status WriteSegment(uint64_t segment_index); std::unique_ptr styp_; - uint32_t num_segments_; DISALLOW_COPY_AND_ASSIGN(MultiSegmentSegmenter); }; diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index cce500dea49..cf9097072c6 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -225,7 +225,7 @@ Status Segmenter::FinalizeSegment(size_t stream_id, for (std::unique_ptr& fragmenter : fragmenters_) fragmenter->ClearFragmentFinalized(); if (!segment_info.is_subsegment) { - Status status = DoFinalizeSegment(); + Status status = DoFinalizeSegment(segment_info.segment_index); // Reset segment information to initial state. sidx_->references.clear(); key_frame_infos_.clear(); @@ -250,8 +250,10 @@ double Segmenter::GetDuration() const { void Segmenter::UpdateProgress(uint64_t progress) { accumulated_progress_ += progress; - if (!progress_listener_) return; - if (progress_target_ == 0) return; + if (!progress_listener_) + return; + if (progress_target_ == 0) + return; // It might happen that accumulated progress exceeds progress_target due to // computation errors, e.g. rounding error. Cap it so it never reports > 100% // progress. @@ -264,7 +266,8 @@ void Segmenter::UpdateProgress(uint64_t progress) { } void Segmenter::SetComplete() { - if (!progress_listener_) return; + if (!progress_listener_) + return; progress_listener_->OnProgress(1.0); } diff --git a/packager/media/formats/mp4/segmenter.h b/packager/media/formats/mp4/segmenter.h index 724c12ef6fc..35a2bfa101c 100644 --- a/packager/media/formats/mp4/segmenter.h +++ b/packager/media/formats/mp4/segmenter.h @@ -124,7 +124,7 @@ class Segmenter { private: virtual Status DoInitialize() = 0; virtual Status DoFinalize() = 0; - virtual Status DoFinalizeSegment() = 0; + virtual Status DoFinalizeSegment(uint64_t segment_index) = 0; uint32_t GetReferenceStreamId(); diff --git a/packager/media/formats/mp4/single_segment_segmenter.cc b/packager/media/formats/mp4/single_segment_segmenter.cc index 7c54d9235a4..7298af95316 100644 --- a/packager/media/formats/mp4/single_segment_segmenter.cc +++ b/packager/media/formats/mp4/single_segment_segmenter.cc @@ -51,9 +51,8 @@ bool SingleSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) { std::vector SingleSegmentSegmenter::GetSegmentRanges() { std::vector ranges; - uint64_t next_offset = - ftyp()->ComputeSize() + moov()->ComputeSize() + vod_sidx_->ComputeSize() + - vod_sidx_->first_offset; + uint64_t next_offset = ftyp()->ComputeSize() + moov()->ComputeSize() + + vod_sidx_->ComputeSize() + vod_sidx_->first_offset; for (const SegmentReference& segment_reference : vod_sidx_->references) { Range r; r.start = next_offset; @@ -77,10 +76,9 @@ Status SingleSegmentSegmenter::DoInitialize() { if (!TempFilePath(options().temp_dir, &temp_file_name_)) return Status(error::FILE_FAILURE, "Unable to create temporary file."); temp_file_.reset(File::Open(temp_file_name_.c_str(), "w")); - return temp_file_ - ? Status::OK - : Status(error::FILE_FAILURE, - "Cannot open file to write " + temp_file_name_); + return temp_file_ ? Status::OK + : Status(error::FILE_FAILURE, + "Cannot open file to write " + temp_file_name_); } Status SingleSegmentSegmenter::DoFinalize() { @@ -159,7 +157,7 @@ Status SingleSegmentSegmenter::DoFinalize() { return Status::OK; } -Status SingleSegmentSegmenter::DoFinalizeSegment() { +Status SingleSegmentSegmenter::DoFinalizeSegment(uint64_t segment_index) { DCHECK(sidx()); DCHECK(fragment_buffer()); // sidx() contains pre-generated segment references with one reference per @@ -212,14 +210,15 @@ Status SingleSegmentSegmenter::DoFinalizeSegment() { // Append fragment buffer to temp file. size_t segment_size = fragment_buffer()->Size(); Status status = fragment_buffer()->WriteToFile(temp_file_.get()); - if (!status.ok()) return status; + if (!status.ok()) + return status; UpdateProgress(vod_ref.subsegment_duration); if (muxer_listener()) { muxer_listener()->OnSampleDurationReady(sample_duration()); - muxer_listener()->OnNewSegment(options().output_file_name, - vod_ref.earliest_presentation_time, - vod_ref.subsegment_duration, segment_size); + muxer_listener()->OnNewSegment( + options().output_file_name, vod_ref.earliest_presentation_time, + vod_ref.subsegment_duration, segment_size, segment_index); } return Status::OK; } diff --git a/packager/media/formats/mp4/single_segment_segmenter.h b/packager/media/formats/mp4/single_segment_segmenter.h index 4659f354359..6abe8bd1e42 100644 --- a/packager/media/formats/mp4/single_segment_segmenter.h +++ b/packager/media/formats/mp4/single_segment_segmenter.h @@ -44,7 +44,7 @@ class SingleSegmentSegmenter : public Segmenter { // Segmenter implementation overrides. Status DoInitialize() override; Status DoFinalize() override; - Status DoFinalizeSegment() override; + Status DoFinalizeSegment(uint64_t segment_index) override; std::unique_ptr vod_sidx_; std::string temp_file_name_; diff --git a/packager/media/formats/packed_audio/packed_audio_writer.cc b/packager/media/formats/packed_audio/packed_audio_writer.cc index 29f796c56e0..10229c84209 100644 --- a/packager/media/formats/packed_audio/packed_audio_writer.cc +++ b/packager/media/formats/packed_audio/packed_audio_writer.cc @@ -79,8 +79,7 @@ Status PackedAudioWriter::FinalizeSegment(size_t stream_id, options().segment_template.empty() ? options().output_file_name : GetSegmentName(options().segment_template, segment_timestamp, - segment_number_++, options().bandwidth); - + segment_info.segment_index - 1, options().bandwidth); // Save |segment_size| as it will be cleared after writing. const size_t segment_size = segmenter_->segment_buffer()->Size(); @@ -90,7 +89,8 @@ Status PackedAudioWriter::FinalizeSegment(size_t stream_id, if (muxer_listener()) { muxer_listener()->OnNewSegment( segment_path, segment_timestamp + transport_stream_timestamp_offset_, - segment_info.duration * segmenter_->TimescaleScale(), segment_size); + segment_info.duration * segmenter_->TimescaleScale(), segment_size, + segment_info.segment_index); } return Status::OK; } diff --git a/packager/media/formats/packed_audio/packed_audio_writer_unittest.cc b/packager/media/formats/packed_audio/packed_audio_writer_unittest.cc index 18c68fe5150..2897f642b63 100644 --- a/packager/media/formats/packed_audio/packed_audio_writer_unittest.cc +++ b/packager/media/formats/packed_audio/packed_audio_writer_unittest.cc @@ -42,8 +42,8 @@ const uint32_t kTimescale = 9000; const char kOutputFile[] = "memory://test.aac"; // For multi-segment mode. const char kSegmentTemplate[] = "memory://test_$Number$.aac"; -const char kSegment1Name[] = "memory://test_1.aac"; -const char kSegment2Name[] = "memory://test_2.aac"; +const char kSegment124Name[] = "memory://test_124.aac"; +const char kSegment125Name[] = "memory://test_125.aac"; class MockPackedAudioSegmenter : public PackedAudioSegmenter { public: @@ -124,7 +124,7 @@ TEST_P(PackedAudioWriterTest, SubsegmentIgnored) { auto subsegment_stream_data = StreamData::FromSegmentInfo( kStreamIndex, GetSegmentInfo(kTimestamp, kDuration, kSubsegment)); - EXPECT_CALL(*mock_muxer_listener_ptr_, OnNewSegment(_, _, _, _)).Times(0); + EXPECT_CALL(*mock_muxer_listener_ptr_, OnNewSegment(_, _, _, _, _)).Times(0); EXPECT_CALL(*mock_segmenter_ptr_, FinalizeSegment()).Times(0); ASSERT_OK(Input(kInput)->Dispatch(std::move(subsegment_stream_data))); } @@ -145,9 +145,9 @@ TEST_P(PackedAudioWriterTest, OneSegment) { EXPECT_CALL( *mock_muxer_listener_ptr_, - OnNewSegment(is_single_segment_mode_ ? kOutputFile : kSegment1Name, + OnNewSegment(is_single_segment_mode_ ? kOutputFile : kSegment124Name, kTimestamp * kMockTimescaleScale, - kDuration * kMockTimescaleScale, kSegmentDataSize)); + kDuration * kMockTimescaleScale, kSegmentDataSize, 124)); EXPECT_CALL(*mock_segmenter_ptr_, TimescaleScale()) .WillRepeatedly(Return(kMockTimescaleScale)); @@ -178,7 +178,7 @@ TEST_P(PackedAudioWriterTest, OneSegment) { } ASSERT_OK(Input(kInput)->FlushDownstream(kStreamIndex)); - ASSERT_FILE_STREQ(is_single_segment_mode_ ? kOutputFile : kSegment1Name, + ASSERT_FILE_STREQ(is_single_segment_mode_ ? kOutputFile : kSegment124Name, kMockSegmentData); } @@ -203,15 +203,15 @@ TEST_P(PackedAudioWriterTest, TwoSegments) { EXPECT_CALL( *mock_muxer_listener_ptr_, - OnNewSegment(is_single_segment_mode_ ? kOutputFile : kSegment1Name, + OnNewSegment(is_single_segment_mode_ ? kOutputFile : kSegment124Name, kTimestamp * kMockTimescaleScale, kDuration * kMockTimescaleScale, - sizeof(kMockSegment1Data) - 1)); + sizeof(kMockSegment1Data) - 1, 124)); EXPECT_CALL( *mock_muxer_listener_ptr_, - OnNewSegment(is_single_segment_mode_ ? kOutputFile : kSegment2Name, + OnNewSegment(is_single_segment_mode_ ? kOutputFile : kSegment125Name, (kTimestamp + kDuration) * kMockTimescaleScale, - kDuration * kMockTimescaleScale, kSegment2DataSize)); + kDuration * kMockTimescaleScale, kSegment2DataSize, 125)); EXPECT_CALL(*mock_segmenter_ptr_, TimescaleScale()) .WillRepeatedly(Return(kMockTimescaleScale)); @@ -251,8 +251,8 @@ TEST_P(PackedAudioWriterTest, TwoSegments) { ASSERT_FILE_STREQ(kOutputFile, std::string(kMockSegment1Data) + std::string(kMockSegment2Data)); } else { - ASSERT_FILE_STREQ(kSegment1Name, kMockSegment1Data); - ASSERT_FILE_STREQ(kSegment2Name, kMockSegment2Data); + ASSERT_FILE_STREQ(kSegment124Name, kMockSegment1Data); + ASSERT_FILE_STREQ(kSegment125Name, kMockSegment2Data); } } diff --git a/packager/media/formats/webm/encrypted_segmenter_unittest.cc b/packager/media/formats/webm/encrypted_segmenter_unittest.cc index 3054e193f45..8974eac0bc4 100644 --- a/packager/media/formats/webm/encrypted_segmenter_unittest.cc +++ b/packager/media/formats/webm/encrypted_segmenter_unittest.cc @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "packager/media/formats/webm/two_pass_single_segment_segmenter.h" - #include + #include + #include "packager/media/formats/webm/segmenter_test_base.h" +#include "packager/media/formats/webm/two_pass_single_segment_segmenter.h" namespace shaka { namespace media { @@ -24,173 +25,529 @@ const uint8_t kIv[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, }; const uint8_t kBasicSupportData[] = { - // ID: EBML Header omitted. - // ID: Segment, Payload Size: 432 - 0x18, 0x53, 0x80, 0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, + // ID: EBML Header omitted. + // ID: Segment, Payload Size: 432 + 0x18, + 0x53, + 0x80, + 0x67, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xb0, // ID: SeekHead, Payload Size: 58 - 0x11, 0x4d, 0x9b, 0x74, 0xba, - // ID: Seek, Payload Size: 11 - 0x4d, 0xbb, 0x8b, - // SeekID: binary(4) (Info) - 0x53, 0xab, 0x84, 0x15, 0x49, 0xa9, 0x66, - // SeekPosition: 89 - 0x53, 0xac, 0x81, 0x59, - // ID: Seek, Payload Size: 11 - 0x4d, 0xbb, 0x8b, - // SeekID: binary(4) (Tracks) - 0x53, 0xab, 0x84, 0x16, 0x54, 0xae, 0x6b, - // SeekPosition: 182 - 0x53, 0xac, 0x81, 0xb6, - // ID: Seek, Payload Size: 12 - 0x4d, 0xbb, 0x8c, - // SeekID: binary(4) (Cues) - 0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b, - // SeekPosition: 279 - 0x53, 0xac, 0x82, 0x01, 0x17, - // ID: Seek, Payload Size: 12 - 0x4d, 0xbb, 0x8c, - // SeekID: binary(4) (Cluster) - 0x53, 0xab, 0x84, 0x1f, 0x43, 0xb6, 0x75, - // SeekPosition: 313 - 0x53, 0xac, 0x82, 0x01, 0x39, + 0x11, + 0x4d, + 0x9b, + 0x74, + 0xba, + // ID: Seek, Payload Size: 11 + 0x4d, + 0xbb, + 0x8b, + // SeekID: binary(4) (Info) + 0x53, + 0xab, + 0x84, + 0x15, + 0x49, + 0xa9, + 0x66, + // SeekPosition: 89 + 0x53, + 0xac, + 0x81, + 0x59, + // ID: Seek, Payload Size: 11 + 0x4d, + 0xbb, + 0x8b, + // SeekID: binary(4) (Tracks) + 0x53, + 0xab, + 0x84, + 0x16, + 0x54, + 0xae, + 0x6b, + // SeekPosition: 182 + 0x53, + 0xac, + 0x81, + 0xb6, + // ID: Seek, Payload Size: 12 + 0x4d, + 0xbb, + 0x8c, + // SeekID: binary(4) (Cues) + 0x53, + 0xab, + 0x84, + 0x1c, + 0x53, + 0xbb, + 0x6b, + // SeekPosition: 279 + 0x53, + 0xac, + 0x82, + 0x01, + 0x17, + // ID: Seek, Payload Size: 12 + 0x4d, + 0xbb, + 0x8c, + // SeekID: binary(4) (Cluster) + 0x53, + 0xab, + 0x84, + 0x1f, + 0x43, + 0xb6, + 0x75, + // SeekPosition: 313 + 0x53, + 0xac, + 0x82, + 0x01, + 0x39, // ID: Void, Payload Size: 24 - 0xec, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, + 0xec, + 0x98, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // ID: Info, Payload Size: 88 - 0x15, 0x49, 0xa9, 0x66, 0xd8, - // TimecodeScale: 1000000 - 0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40, - // Duration: float(5000) - 0x44, 0x89, 0x84, 0x45, 0x9c, 0x40, 0x00, - // MuxingApp: 'libwebm-0.2.1.0' - 0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30, - 0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30, - // WritingApp: 'https://github.com/google/shaka-packager version test' - 0x57, 0x41, 0xb5, - 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2f, 0x73, 0x68, 0x61, 0x6b, 0x61, 0x2d, 0x70, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x20, 0x74, 0x65, 0x73, 0x74, + 0x15, + 0x49, + 0xa9, + 0x66, + 0xd8, + // TimecodeScale: 1000000 + 0x2a, + 0xd7, + 0xb1, + 0x83, + 0x0f, + 0x42, + 0x40, + // Duration: float(5000) + 0x44, + 0x89, + 0x84, + 0x45, + 0x9c, + 0x40, + 0x00, + // MuxingApp: 'libwebm-0.2.1.0' + 0x4d, + 0x80, + 0x8f, + 0x6c, + 0x69, + 0x62, + 0x77, + 0x65, + 0x62, + 0x6d, + 0x2d, + 0x30, + 0x2e, + 0x32, + 0x2e, + 0x31, + 0x2e, + 0x30, + // WritingApp: 'https://github.com/google/shaka-packager version test' + 0x57, + 0x41, + 0xb5, + 0x68, + 0x74, + 0x74, + 0x70, + 0x73, + 0x3a, + 0x2f, + 0x2f, + 0x67, + 0x69, + 0x74, + 0x68, + 0x75, + 0x62, + 0x2e, + 0x63, + 0x6f, + 0x6d, + 0x2f, + 0x67, + 0x6f, + 0x6f, + 0x67, + 0x6c, + 0x65, + 0x2f, + 0x73, + 0x68, + 0x61, + 0x6b, + 0x61, + 0x2d, + 0x70, + 0x61, + 0x63, + 0x6b, + 0x61, + 0x67, + 0x65, + 0x72, + 0x20, + 0x76, + 0x65, + 0x72, + 0x73, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x74, + 0x65, + 0x73, + 0x74, // ID: Tracks, Payload Size: 92 - 0x16, 0x54, 0xae, 0x6b, 0xdc, - // ID: Track, Payload Size: 90 - 0xae, 0xda, - // TrackNumber: 1 - 0xd7, 0x81, 0x01, - // TrackUID: 1 - 0x73, 0xc5, 0x81, 0x01, - // TrackType: 1 - 0x83, 0x81, 0x01, - // CodecID: 'V_VP8' - 0x86, 0x85, 0x56, 0x5f, 0x56, 0x50, 0x38, - // Language: 'en' - 0x22, 0xb5, 0x9c, 0x82, 0x65, 0x6e, - // ID: ContentEncodings, Payload Size: 48 - 0x6d, 0x80, 0xb0, - // ID: ContentEncoding, Payload Size: 45 - 0x62, 0x40, 0xad, - // ContentEncodingOrder: 0 - 0x50, 0x31, 0x81, 0x00, - // ContentEncodingScope: 1 - 0x50, 0x32, 0x81, 0x01, - // ContentEncodingType: 1 - 0x50, 0x33, 0x81, 0x01, - // ID: ContentEncryption, Payload Size: 30 - 0x50, 0x35, 0x9e, - // ContentEncAlgo: 5 - 0x47, 0xe1, 0x81, 0x05, - // ContentEncKeyID: binary(16) - 0x47, 0xe2, 0x90, - 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, - 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, - // ID: ContentEncAESSettings, Payload Size: 4 - 0x47, 0xe7, 0x84, - // AESSettingsCipherMode: 1 - 0x47, 0xe8, 0x81, 0x01, - // ID: Video, Payload Size: 14 - 0xe0, 0x8e, - // PixelWidth: 100 - 0xb0, 0x81, 0x64, - // PixelHeight: 100 - 0xba, 0x81, 0x64, - // DisplayWidth: 100 - 0x54, 0xb0, 0x81, 0x64, - // DisplayHeight: 100 - 0x54, 0xba, 0x81, 0x64, + 0x16, + 0x54, + 0xae, + 0x6b, + 0xdc, + // ID: Track, Payload Size: 90 + 0xae, + 0xda, + // TrackNumber: 1 + 0xd7, + 0x81, + 0x01, + // TrackUID: 1 + 0x73, + 0xc5, + 0x81, + 0x01, + // TrackType: 1 + 0x83, + 0x81, + 0x01, + // CodecID: 'V_VP8' + 0x86, + 0x85, + 0x56, + 0x5f, + 0x56, + 0x50, + 0x38, + // Language: 'en' + 0x22, + 0xb5, + 0x9c, + 0x82, + 0x65, + 0x6e, + // ID: ContentEncodings, Payload Size: 48 + 0x6d, + 0x80, + 0xb0, + // ID: ContentEncoding, Payload Size: 45 + 0x62, + 0x40, + 0xad, + // ContentEncodingOrder: 0 + 0x50, + 0x31, + 0x81, + 0x00, + // ContentEncodingScope: 1 + 0x50, + 0x32, + 0x81, + 0x01, + // ContentEncodingType: 1 + 0x50, + 0x33, + 0x81, + 0x01, + // ID: ContentEncryption, Payload Size: 30 + 0x50, + 0x35, + 0x9e, + // ContentEncAlgo: 5 + 0x47, + 0xe1, + 0x81, + 0x05, + // ContentEncKeyID: binary(16) + 0x47, + 0xe2, + 0x90, + 0x4c, + 0x6f, + 0x72, + 0x65, + 0x6d, + 0x20, + 0x69, + 0x70, + 0x73, + 0x75, + 0x6d, + 0x20, + 0x64, + 0x6f, + 0x6c, + 0x6f, + // ID: ContentEncAESSettings, Payload Size: 4 + 0x47, + 0xe7, + 0x84, + // AESSettingsCipherMode: 1 + 0x47, + 0xe8, + 0x81, + 0x01, + // ID: Video, Payload Size: 14 + 0xe0, + 0x8e, + // PixelWidth: 100 + 0xb0, + 0x81, + 0x64, + // PixelHeight: 100 + 0xba, + 0x81, + 0x64, + // DisplayWidth: 100 + 0x54, + 0xb0, + 0x81, + 0x64, + // DisplayHeight: 100 + 0x54, + 0xba, + 0x81, + 0x64, // ID: Cues, Payload Size: 29 - 0x1c, 0x53, 0xbb, 0x6b, 0x9d, - // ID: CuePoint, Payload Size: 12 - 0xbb, 0x8c, - // CueTime: 0 - 0xb3, 0x81, 0x00, - // ID: CueTrackPositions, Payload Size: 7 - 0xb7, 0x87, - // CueTrack: 1 - 0xf7, 0x81, 0x01, - // CueClusterPosition: 313 - 0xf1, 0x82, 0x01, 0x39, - // ID: CuePoint, Payload Size: 13 - 0xbb, 0x8d, - // CueTime: 3000 - 0xb3, 0x82, 0x0b, 0xb8, - // ID: CueTrackPositions, Payload Size: 7 - 0xb7, 0x87, - // CueTrack: 1 - 0xf7, 0x81, 0x01, - // CueClusterPosition: 370 - 0xf1, 0x82, 0x01, 0x72, + 0x1c, + 0x53, + 0xbb, + 0x6b, + 0x9d, + // ID: CuePoint, Payload Size: 12 + 0xbb, + 0x8c, + // CueTime: 0 + 0xb3, + 0x81, + 0x00, + // ID: CueTrackPositions, Payload Size: 7 + 0xb7, + 0x87, + // CueTrack: 1 + 0xf7, + 0x81, + 0x01, + // CueClusterPosition: 313 + 0xf1, + 0x82, + 0x01, + 0x39, + // ID: CuePoint, Payload Size: 13 + 0xbb, + 0x8d, + // CueTime: 3000 + 0xb3, + 0x82, + 0x0b, + 0xb8, + // ID: CueTrackPositions, Payload Size: 7 + 0xb7, + 0x87, + // CueTrack: 1 + 0xf7, + 0x81, + 0x01, + // CueClusterPosition: 370 + 0xf1, + 0x82, + 0x01, + 0x72, // ID: Cluster, Payload Size: 45 - 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, - // Timecode: 0 - 0xe7, 0x81, 0x00, - // ID: SimpleBlock, Payload Size: 10 - 0xa3, 0x8a, 0x81, 0x00, 0x00, 0x80, - // Signal Byte: Clear - 0x00, - // Frame Data: - 0xde, 0xad, 0xbe, 0xef, 0x00, - // ID: SimpleBlock, Payload Size: 10 - 0xa3, 0x8a, 0x81, 0x03, 0xe8, 0x80, - // Signal Byte: Clear - 0x00, - // Frame Data: - 0xde, 0xad, 0xbe, 0xef, 0x00, - // ID: BlockGroup, Payload Size: 16 - 0xa0, 0x90, - // ID: Block, Payload Size: 10 - 0xa1, 0x8a, 0x81, 0x07, 0xd0, 0x00, - // Signal Byte: Clear - 0x00, - // Frame Data: - 0xde, 0xad, 0xbe, 0xef, 0x00, - // BlockDuration: 1000 - 0x9b, 0x82, 0x03, 0xe8, + 0x1f, + 0x43, + 0xb6, + 0x75, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x2d, + // Timecode: 0 + 0xe7, + 0x81, + 0x00, + // ID: SimpleBlock, Payload Size: 10 + 0xa3, + 0x8a, + 0x81, + 0x00, + 0x00, + 0x80, + // Signal Byte: Clear + 0x00, + // Frame Data: + 0xde, + 0xad, + 0xbe, + 0xef, + 0x00, + // ID: SimpleBlock, Payload Size: 10 + 0xa3, + 0x8a, + 0x81, + 0x03, + 0xe8, + 0x80, + // Signal Byte: Clear + 0x00, + // Frame Data: + 0xde, + 0xad, + 0xbe, + 0xef, + 0x00, + // ID: BlockGroup, Payload Size: 16 + 0xa0, + 0x90, + // ID: Block, Payload Size: 10 + 0xa1, + 0x8a, + 0x81, + 0x07, + 0xd0, + 0x00, + // Signal Byte: Clear + 0x00, + // Frame Data: + 0xde, + 0xad, + 0xbe, + 0xef, + 0x00, + // BlockDuration: 1000 + 0x9b, + 0x82, + 0x03, + 0xe8, // ID: Cluster, Payload Size: 50 - 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, - // Timecode: 3000 - 0xe7, 0x82, 0x0b, 0xb8, - // ID: SimpleBlock: Payload Size: 18 - 0xa3, 0x92, 0x81, 0x00,0x00, 0x80, - // Signal Byte: Encrypted - 0x01, - // IV: - 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, - // Frame Data: - 0xde, 0xad, 0xbe, 0xef, 0x00, - // ID: BlockGroup, Payload Size: 24 - 0xa0, 0x98, - // ID: Block, Payload Size: 18 - 0xa1, 0x92, 0x81, 0x03, 0xe8, 0x00, - // Signal Byte: Encrypted - 0x01, - // IV: - 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, - // Frame Data: - 0xde, 0xad, 0xbe, 0xef, 0x00, - // BlockDuration: 1000 - 0x9b, 0x82, 0x03, 0xe8, + 0x1f, + 0x43, + 0xb6, + 0x75, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x32, + // Timecode: 3000 + 0xe7, + 0x82, + 0x0b, + 0xb8, + // ID: SimpleBlock: Payload Size: 18 + 0xa3, + 0x92, + 0x81, + 0x00, + 0x00, + 0x80, + // Signal Byte: Encrypted + 0x01, + // IV: + 0x01, + 0x23, + 0x45, + 0x67, + 0x89, + 0x01, + 0x23, + 0x45, + // Frame Data: + 0xde, + 0xad, + 0xbe, + 0xef, + 0x00, + // ID: BlockGroup, Payload Size: 24 + 0xa0, + 0x98, + // ID: Block, Payload Size: 18 + 0xa1, + 0x92, + 0x81, + 0x03, + 0xe8, + 0x00, + // Signal Byte: Encrypted + 0x01, + // IV: + 0x01, + 0x23, + 0x45, + 0x67, + 0x89, + 0x01, + 0x23, + 0x45, + // Frame Data: + 0xde, + 0xad, + 0xbe, + 0xef, + 0x00, + // BlockDuration: 1000 + 0x9b, + 0x82, + 0x03, + 0xe8, }; } // namespace @@ -225,7 +582,7 @@ TEST_F(EncryptedSegmenterTest, BasicSupport) { // segment encrypted. for (int i = 0; i < 5; i++) { if (i == 3) { - ASSERT_OK(segmenter_->FinalizeSegment(0, 3 * kDuration, !kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(0, 3 * kDuration, !kSubsegment, 3)); } std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, kNoSideData); @@ -239,8 +596,8 @@ TEST_F(EncryptedSegmenterTest, BasicSupport) { } ASSERT_OK(segmenter_->AddSample(*sample)); } - ASSERT_OK( - segmenter_->FinalizeSegment(3 * kDuration, 2 * kDuration, !kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(3 * kDuration, 2 * kDuration, + !kSubsegment, 2)); ASSERT_OK(segmenter_->Finalize()); ASSERT_FILE_ENDS_WITH(OutputFileName().c_str(), kBasicSupportData); diff --git a/packager/media/formats/webm/multi_segment_segmenter.cc b/packager/media/formats/webm/multi_segment_segmenter.cc index 4a2f24bae21..86df7b6356e 100644 --- a/packager/media/formats/webm/multi_segment_segmenter.cc +++ b/packager/media/formats/webm/multi_segment_segmenter.cc @@ -24,15 +24,24 @@ MultiSegmentSegmenter::~MultiSegmentSegmenter() {} Status MultiSegmentSegmenter::FinalizeSegment(uint64_t start_timestamp, uint64_t duration_timestamp, - bool is_subsegment) { + bool is_subsegment, + uint64_t segment_index) { CHECK(cluster()); RETURN_IF_ERROR(Segmenter::FinalizeSegment( - start_timestamp, duration_timestamp, is_subsegment)); + start_timestamp, duration_timestamp, is_subsegment, segment_index)); if (!cluster()->Finalize()) return Status(error::FILE_FAILURE, "Error finalizing segment."); if (!is_subsegment) { + std::string segment_name_segment_index = + GetSegmentName(options().segment_template, start_timestamp, + segment_index - 1, options().bandwidth); + + if (writer_->file()->Rename(segment_name_segment_index) != 1) { + LOG(ERROR) << "Error renaming WEBM segment"; + } const std::string segment_name = writer_->file()->file_name(); + // Close the file, which also does flushing, to make sure the file is // written before manifest is updated. RETURN_IF_ERROR(writer_->Close()); @@ -40,7 +49,7 @@ Status MultiSegmentSegmenter::FinalizeSegment(uint64_t start_timestamp, if (muxer_listener()) { const uint64_t size = cluster()->Size(); muxer_listener()->OnNewSegment(segment_name, start_timestamp, - duration_timestamp, size); + duration_timestamp, size, segment_index); } VLOG(1) << "WEBM file '" << writer_->file()->file_name() << "' finalized."; } diff --git a/packager/media/formats/webm/multi_segment_segmenter.h b/packager/media/formats/webm/multi_segment_segmenter.h index 4d788ca7937..c1ddf20d13d 100644 --- a/packager/media/formats/webm/multi_segment_segmenter.h +++ b/packager/media/formats/webm/multi_segment_segmenter.h @@ -8,6 +8,7 @@ #define PACKAGER_MEDIA_FORMATS_WEBM_MULTI_SEGMENT_SEGMENTER_H_ #include + #include "packager/media/formats/webm/mkv_writer.h" #include "packager/media/formats/webm/segmenter.h" #include "packager/status.h" @@ -30,7 +31,8 @@ class MultiSegmentSegmenter : public Segmenter { /// @{ Status FinalizeSegment(uint64_t start_timestamp, uint64_t duration_timestamp, - bool is_subsegment) override; + bool is_subsegment, + uint64_t segment_index) override; bool GetInitRangeStartAndEnd(uint64_t* start, uint64_t* end) override; bool GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) override; std::vector GetSegmentRanges() override; diff --git a/packager/media/formats/webm/multi_segment_segmenter_unittest.cc b/packager/media/formats/webm/multi_segment_segmenter_unittest.cc index 5513f00c51a..346cb8dd68d 100644 --- a/packager/media/formats/webm/multi_segment_segmenter_unittest.cc +++ b/packager/media/formats/webm/multi_segment_segmenter_unittest.cc @@ -5,7 +5,9 @@ #include "packager/media/formats/webm/multi_segment_segmenter.h" #include + #include + #include "packager/media/base/muxer_util.h" #include "packager/media/formats/webm/segmenter_test_base.h" @@ -18,9 +20,9 @@ const uint64_t kDuration = 1000000u; const bool kSubsegment = true; const uint8_t kBasicSupportDataInit[] = { - // ID: EBML Header omitted. - // ID: Segment, Payload Size: Unknown - 0x18, 0x53, 0x80, 0x67, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + // ID: EBML Header omitted. + // ID: Segment, Payload Size: Unknown + 0x18, 0x53, 0x80, 0x67, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ID: Void, Payload Size: 87 0xec, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -32,46 +34,44 @@ const uint8_t kBasicSupportDataInit[] = { 0x00, 0x00, 0x00, 0x00, 0x00, // ID: Info, Payload Size: 81 0x15, 0x49, 0xa9, 0x66, 0xd1, - // TimecodeScale: 1000000 - 0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40, - // MuxingApp: 'libwebm-0.2.1.0' - 0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30, - 0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30, - // WritingApp: 'https://github.com/google/shaka-packager version test' - 0x57, 0x41, 0xb5, - 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2f, 0x73, 0x68, 0x61, 0x6b, 0x61, 0x2d, 0x70, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x20, 0x74, 0x65, 0x73, 0x74, + // TimecodeScale: 1000000 + 0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40, + // MuxingApp: 'libwebm-0.2.1.0' + 0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30, + 0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30, + // WritingApp: 'https://github.com/google/shaka-packager version test' + 0x57, 0x41, 0xb5, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x73, 0x68, 0x61, 0x6b, 0x61, 0x2d, 0x70, + 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x65, 0x73, 0x74, // ID: Tracks, Payload Size: 41 0x16, 0x54, 0xae, 0x6b, 0xa9, - // ID: Track, Payload Size: 39 - 0xae, 0xa7, - // TrackNumber: 1 - 0xd7, 0x81, 0x01, - // TrackUID: 1 - 0x73, 0xc5, 0x81, 0x01, - // TrackType: 1 - 0x83, 0x81, 0x01, - // CodecID: 'V_VP8' - 0x86, 0x85, 0x56, 0x5f, 0x56, 0x50, 0x38, - // Language: 'en' - 0x22, 0xb5, 0x9c, 0x82, 0x65, 0x6e, - // ID: Video, Payload Size: 14 - 0xe0, 0x8e, - // PixelWidth: 100 - 0xb0, 0x81, 0x64, - // PixelHeight: 100 - 0xba, 0x81, 0x64, - // DisplayWidth: 100 - 0x54, 0xb0, 0x81, 0x64, - // DisplayHeight: 100 - 0x54, 0xba, 0x81, 0x64 -}; + // ID: Track, Payload Size: 39 + 0xae, 0xa7, + // TrackNumber: 1 + 0xd7, 0x81, 0x01, + // TrackUID: 1 + 0x73, 0xc5, 0x81, 0x01, + // TrackType: 1 + 0x83, 0x81, 0x01, + // CodecID: 'V_VP8' + 0x86, 0x85, 0x56, 0x5f, 0x56, 0x50, 0x38, + // Language: 'en' + 0x22, 0xb5, 0x9c, 0x82, 0x65, 0x6e, + // ID: Video, Payload Size: 14 + 0xe0, 0x8e, + // PixelWidth: 100 + 0xb0, 0x81, 0x64, + // PixelHeight: 100 + 0xba, 0x81, 0x64, + // DisplayWidth: 100 + 0x54, 0xb0, 0x81, 0x64, + // DisplayHeight: 100 + 0x54, 0xba, 0x81, 0x64}; const uint8_t kBasicSupportDataSegment[] = { - // ID: Cluster, Payload Size: 64 - 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + // ID: Cluster, Payload Size: 64 + 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, // Timecode: 0 0xe7, 0x81, 0x00, // ID: SimpleBlock, Payload Size: 9 @@ -84,11 +84,10 @@ const uint8_t kBasicSupportDataSegment[] = { 0xa3, 0x89, 0x81, 0x0b, 0xb8, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, // ID: BlockGroup, Payload Size: 15 0xa0, 0x8f, - // ID: Block, Payload Size: 9 - 0xa1, 0x89, 0x81, 0x0f, 0xa0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x00, - // BlockDuration: 1000 - 0x9b, 0x82, 0x03, 0xe8 -}; + // ID: Block, Payload Size: 9 + 0xa1, 0x89, 0x81, 0x0f, 0xa0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x00, + // BlockDuration: 1000 + 0x9b, 0x82, 0x03, 0xe8}; } // namespace @@ -126,7 +125,7 @@ TEST_F(MultiSegmentSegmenterTest, BasicSupport) { CreateSample(kKeyFrame, kDuration, kNoSideData); ASSERT_OK(segmenter_->AddSample(*sample)); } - ASSERT_OK(segmenter_->FinalizeSegment(0, 8 * kDuration, !kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(0, 8 * kDuration, !kSubsegment, 8)); ASSERT_OK(segmenter_->Finalize()); // Verify the resulting data. @@ -145,14 +144,14 @@ TEST_F(MultiSegmentSegmenterTest, SplitsFilesOnSegment) { // Write the samples to the Segmenter. for (int i = 0; i < 8; i++) { if (i == 5) { - ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, !kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, !kSubsegment, 5)); } std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, kNoSideData); ASSERT_OK(segmenter_->AddSample(*sample)); } - ASSERT_OK( - segmenter_->FinalizeSegment(5 * kDuration, 8 * kDuration, !kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(5 * kDuration, 8 * kDuration, + !kSubsegment, 8)); ASSERT_OK(segmenter_->Finalize()); // Verify the resulting data. @@ -176,13 +175,13 @@ TEST_F(MultiSegmentSegmenterTest, SplitsClustersOnSubsegment) { // Write the samples to the Segmenter. for (int i = 0; i < 8; i++) { if (i == 5) { - ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, kSubsegment, 5)); } std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, kNoSideData); ASSERT_OK(segmenter_->AddSample(*sample)); } - ASSERT_OK(segmenter_->FinalizeSegment(0, 8 * kDuration, !kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(0, 8 * kDuration, !kSubsegment, 8)); ASSERT_OK(segmenter_->Finalize()); // Verify the resulting data. diff --git a/packager/media/formats/webm/segmenter.cc b/packager/media/formats/webm/segmenter.cc index b3ffdcfb188..70c29cf3920 100644 --- a/packager/media/formats/webm/segmenter.cc +++ b/packager/media/formats/webm/segmenter.cc @@ -135,8 +135,8 @@ Status Segmenter::Initialize(const StreamInfo& info, if (info.is_encrypted()) { if (info.encryption_config().per_sample_iv_size != kWebMIvSize) return Status(error::MUXER_FAILURE, "Incorrect size WebM encryption IV."); - status = UpdateTrackForEncryption(info.encryption_config().key_id, - track.get()); + status = + UpdateTrackForEncryption(info.encryption_config().key_id, track.get()); if (!status.ok()) return status; } @@ -196,7 +196,8 @@ Status Segmenter::AddSample(const MediaSample& source_sample) { Status Segmenter::FinalizeSegment(uint64_t start_timestamp, uint64_t duration_timestamp, - bool is_subsegment) { + bool is_subsegment, + uint64_t segment_index) { if (is_subsegment) new_subsegment_ = true; else @@ -211,9 +212,8 @@ float Segmenter::GetDurationInSeconds() const { } uint64_t Segmenter::FromBmffTimestamp(uint64_t bmff_timestamp) { - return NsToWebMTimecode( - BmffTimestampToNs(bmff_timestamp, time_scale_), - segment_info_.timecode_scale()); + return NsToWebMTimecode(BmffTimestampToNs(bmff_timestamp, time_scale_), + segment_info_.timecode_scale()); } uint64_t Segmenter::FromWebMTimecode(uint64_t webm_timecode) { @@ -389,8 +389,7 @@ Status Segmenter::WriteFrame(bool write_duration) { BmffTimestampToNs(prev_sample_->duration(), time_scale_)); } frame.set_is_key(prev_sample_->is_key_frame()); - frame.set_timestamp( - BmffTimestampToNs(prev_sample_->pts(), time_scale_)); + frame.set_timestamp(BmffTimestampToNs(prev_sample_->pts(), time_scale_)); frame.set_track_number(track_id_); if (prev_sample_->side_data_size() > 0) { diff --git a/packager/media/formats/webm/segmenter.h b/packager/media/formats/webm/segmenter.h index f6c7c2184d1..0284c6d6970 100644 --- a/packager/media/formats/webm/segmenter.h +++ b/packager/media/formats/webm/segmenter.h @@ -57,7 +57,8 @@ class Segmenter { /// Finalize the (sub)segment. virtual Status FinalizeSegment(uint64_t start_timestamp, uint64_t duration_timestamp, - bool is_subsegment) = 0; + bool is_subsegment, + uint64_t segment_index) = 0; /// @return true if there is an initialization range, while setting @a start /// and @a end; or false if initialization range does not apply. diff --git a/packager/media/formats/webm/single_segment_segmenter.cc b/packager/media/formats/webm/single_segment_segmenter.cc index 29a6be75722..3cb9c269e44 100644 --- a/packager/media/formats/webm/single_segment_segmenter.cc +++ b/packager/media/formats/webm/single_segment_segmenter.cc @@ -21,9 +21,10 @@ SingleSegmentSegmenter::~SingleSegmentSegmenter() {} Status SingleSegmentSegmenter::FinalizeSegment(uint64_t start_timestamp, uint64_t duration_timestamp, - bool is_subsegment) { - Status status = Segmenter::FinalizeSegment(start_timestamp, - duration_timestamp, is_subsegment); + bool is_subsegment, + uint64_t segment_index) { + Status status = Segmenter::FinalizeSegment( + start_timestamp, duration_timestamp, is_subsegment, segment_index); if (!status.ok()) return status; // No-op for subsegment in single segment mode. @@ -35,7 +36,7 @@ Status SingleSegmentSegmenter::FinalizeSegment(uint64_t start_timestamp, if (muxer_listener()) { const uint64_t size = cluster()->Size(); muxer_listener()->OnNewSegment(options().output_file_name, start_timestamp, - duration_timestamp, size); + duration_timestamp, size, segment_index); } return Status::OK; } diff --git a/packager/media/formats/webm/single_segment_segmenter.h b/packager/media/formats/webm/single_segment_segmenter.h index 0115fcd0afe..201ca1e1743 100644 --- a/packager/media/formats/webm/single_segment_segmenter.h +++ b/packager/media/formats/webm/single_segment_segmenter.h @@ -7,10 +7,10 @@ #ifndef PACKAGER_MEDIA_FORMATS_WEBM_SINGLE_SEGMENT_SEGMENTER_H_ #define PACKAGER_MEDIA_FORMATS_WEBM_SINGLE_SEGMENT_SEGMENTER_H_ -#include "packager/media/formats/webm/segmenter.h" - #include + #include "packager/media/formats/webm/mkv_writer.h" +#include "packager/media/formats/webm/segmenter.h" #include "packager/status.h" namespace shaka { @@ -32,7 +32,8 @@ class SingleSegmentSegmenter : public Segmenter { /// @{ Status FinalizeSegment(uint64_t start_timestamp, uint64_t duration_timestamp, - bool is_subsegment) override; + bool is_subsegment, + uint64_t segment_index) override; bool GetInitRangeStartAndEnd(uint64_t* start, uint64_t* end) override; bool GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) override; std::vector GetSegmentRanges() override; diff --git a/packager/media/formats/webm/single_segment_segmenter_unittest.cc b/packager/media/formats/webm/single_segment_segmenter_unittest.cc index 175931183b4..c8d48086177 100644 --- a/packager/media/formats/webm/single_segment_segmenter_unittest.cc +++ b/packager/media/formats/webm/single_segment_segmenter_unittest.cc @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "packager/media/formats/webm/two_pass_single_segment_segmenter.h" - #include + #include + #include "packager/media/formats/webm/segmenter_test_base.h" +#include "packager/media/formats/webm/two_pass_single_segment_segmenter.h" namespace shaka { namespace media { @@ -19,119 +20,414 @@ const uint64_t kDuration = 1000000; const bool kSubsegment = true; const uint8_t kBasicSupportData[] = { - // ID: EBML Header omitted. - // ID: Segment, Payload Size: 343 - 0x18, 0x53, 0x80, 0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x57, + // ID: EBML Header omitted. + // ID: Segment, Payload Size: 343 + 0x18, + 0x53, + 0x80, + 0x67, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0x57, // ID: SeekHead, Payload Size: 57 - 0x11, 0x4d, 0x9b, 0x74, 0xb8, - // ID: Seek, Payload Size: 11 - 0x4d, 0xbb, 0x8b, - // SeekID: binary(4) (Info) - 0x53, 0xab, 0x84, 0x15, 0x49, 0xa9, 0x66, - // SeekPosition: 89 - 0x53, 0xac, 0x81, 0x59, - // ID: Seek, Payload Size: 11 - 0x4d, 0xbb, 0x8b, - // SeekID: binary(4) (Tracks) - 0x53, 0xab, 0x84, 0x16, 0x54, 0xae, 0x6b, - // SeekPosition: 182 - 0x53, 0xac, 0x81, 0xb6, - // ID: Seek, Payload Size: 12 - 0x4d, 0xbb, 0x8b, - // SeekID: binary(4) (Cues) - 0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b, - // SeekPosition: 228 - 0x53, 0xac, 0x81, 0xe4, - // ID: Seek, Payload Size: 11 - 0x4d, 0xbb, 0x8b, - // SeekID: binary(4) (Cluster) - 0x53, 0xab, 0x84, 0x1f, 0x43, 0xb6, 0x75, - // SeekPosition: 246 - 0x53, 0xac, 0x81, 0xf6, + 0x11, + 0x4d, + 0x9b, + 0x74, + 0xb8, + // ID: Seek, Payload Size: 11 + 0x4d, + 0xbb, + 0x8b, + // SeekID: binary(4) (Info) + 0x53, + 0xab, + 0x84, + 0x15, + 0x49, + 0xa9, + 0x66, + // SeekPosition: 89 + 0x53, + 0xac, + 0x81, + 0x59, + // ID: Seek, Payload Size: 11 + 0x4d, + 0xbb, + 0x8b, + // SeekID: binary(4) (Tracks) + 0x53, + 0xab, + 0x84, + 0x16, + 0x54, + 0xae, + 0x6b, + // SeekPosition: 182 + 0x53, + 0xac, + 0x81, + 0xb6, + // ID: Seek, Payload Size: 12 + 0x4d, + 0xbb, + 0x8b, + // SeekID: binary(4) (Cues) + 0x53, + 0xab, + 0x84, + 0x1c, + 0x53, + 0xbb, + 0x6b, + // SeekPosition: 228 + 0x53, + 0xac, + 0x81, + 0xe4, + // ID: Seek, Payload Size: 11 + 0x4d, + 0xbb, + 0x8b, + // SeekID: binary(4) (Cluster) + 0x53, + 0xab, + 0x84, + 0x1f, + 0x43, + 0xb6, + 0x75, + // SeekPosition: 246 + 0x53, + 0xac, + 0x81, + 0xf6, // ID: Void, Payload Size: 26 - 0xec, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + 0xec, + 0x9a, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, // ID: Info, Payload Size: 88 - 0x15, 0x49, 0xa9, 0x66, 0xd8, - // TimecodeScale: 1000000 - 0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40, - // Duration: float(5000) - 0x44, 0x89, 0x84, 0x45, 0x9c, 0x40, 0x00, - // MuxingApp: 'libwebm-0.2.1.0' - 0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30, - 0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30, - // WritingApp: 'https://github.com/google/shaka-packager version test' - 0x57, 0x41, 0xb5, - 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2f, 0x73, 0x68, 0x61, 0x6b, 0x61, 0x2d, 0x70, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x20, 0x74, 0x65, 0x73, 0x74, + 0x15, + 0x49, + 0xa9, + 0x66, + 0xd8, + // TimecodeScale: 1000000 + 0x2a, + 0xd7, + 0xb1, + 0x83, + 0x0f, + 0x42, + 0x40, + // Duration: float(5000) + 0x44, + 0x89, + 0x84, + 0x45, + 0x9c, + 0x40, + 0x00, + // MuxingApp: 'libwebm-0.2.1.0' + 0x4d, + 0x80, + 0x8f, + 0x6c, + 0x69, + 0x62, + 0x77, + 0x65, + 0x62, + 0x6d, + 0x2d, + 0x30, + 0x2e, + 0x32, + 0x2e, + 0x31, + 0x2e, + 0x30, + // WritingApp: 'https://github.com/google/shaka-packager version test' + 0x57, + 0x41, + 0xb5, + 0x68, + 0x74, + 0x74, + 0x70, + 0x73, + 0x3a, + 0x2f, + 0x2f, + 0x67, + 0x69, + 0x74, + 0x68, + 0x75, + 0x62, + 0x2e, + 0x63, + 0x6f, + 0x6d, + 0x2f, + 0x67, + 0x6f, + 0x6f, + 0x67, + 0x6c, + 0x65, + 0x2f, + 0x73, + 0x68, + 0x61, + 0x6b, + 0x61, + 0x2d, + 0x70, + 0x61, + 0x63, + 0x6b, + 0x61, + 0x67, + 0x65, + 0x72, + 0x20, + 0x76, + 0x65, + 0x72, + 0x73, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x74, + 0x65, + 0x73, + 0x74, // ID: Tracks, Payload Size: 41 - 0x16, 0x54, 0xae, 0x6b, 0xa9, - // ID: Track, Payload Size: 39 - 0xae, 0xa7, - // TrackNumber: 1 - 0xd7, 0x81, 0x01, - // TrackUID: 1 - 0x73, 0xc5, 0x81, 0x01, - // TrackType: 1 - 0x83, 0x81, 0x01, - // CodecID: 'V_VP8' - 0x86, 0x85, 0x56, 0x5f, 0x56, 0x50, 0x38, - // Language: 'en' - 0x22, 0xb5, 0x9c, 0x82, 0x65, 0x6e, - // ID: Video, Payload Size: 14 - 0xe0, 0x8e, - // PixelWidth: 100 - 0xb0, 0x81, 0x64, - // PixelHeight: 100 - 0xba, 0x81, 0x64, - // DisplayWidth: 100 - 0x54, 0xb0, 0x81, 0x64, - // DisplayHeight: 100 - 0x54, 0xba, 0x81, 0x64, + 0x16, + 0x54, + 0xae, + 0x6b, + 0xa9, + // ID: Track, Payload Size: 39 + 0xae, + 0xa7, + // TrackNumber: 1 + 0xd7, + 0x81, + 0x01, + // TrackUID: 1 + 0x73, + 0xc5, + 0x81, + 0x01, + // TrackType: 1 + 0x83, + 0x81, + 0x01, + // CodecID: 'V_VP8' + 0x86, + 0x85, + 0x56, + 0x5f, + 0x56, + 0x50, + 0x38, + // Language: 'en' + 0x22, + 0xb5, + 0x9c, + 0x82, + 0x65, + 0x6e, + // ID: Video, Payload Size: 14 + 0xe0, + 0x8e, + // PixelWidth: 100 + 0xb0, + 0x81, + 0x64, + // PixelHeight: 100 + 0xba, + 0x81, + 0x64, + // DisplayWidth: 100 + 0x54, + 0xb0, + 0x81, + 0x64, + // DisplayHeight: 100 + 0x54, + 0xba, + 0x81, + 0x64, // ID: Cues, Payload Size: 13 - 0x1c, 0x53, 0xbb, 0x6b, 0x8d, - // ID: CuePoint, Payload Size: 11 - 0xbb, 0x8b, - // CueTime: 0 - 0xb3, 0x81, 0x00, - // ID: CueTrackPositions, Payload Size: 6 - 0xb7, 0x86, - // CueTrack: 1 - 0xf7, 0x81, 0x01, - // CueClusterPosition: 246 - 0xf1, 0x81, 0xf6, + 0x1c, + 0x53, + 0xbb, + 0x6b, + 0x8d, + // ID: CuePoint, Payload Size: 11 + 0xbb, + 0x8b, + // CueTime: 0 + 0xb3, + 0x81, + 0x00, + // ID: CueTrackPositions, Payload Size: 6 + 0xb7, + 0x86, + // CueTrack: 1 + 0xf7, + 0x81, + 0x01, + // CueClusterPosition: 246 + 0xf1, + 0x81, + 0xf6, // ID: Cluster, Payload Size: 85 - 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, - // Timecode: 0 - 0xe7, 0x81, 0x00, - // ID: SimpleBlock, Payload Size: 9 - 0xa3, 0x89, 0x81, 0x00, 0x00, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, - // ID: SimpleBlock, Payload Size: 9 - 0xa3, 0x89, 0x81, 0x03, 0xe8, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, - // ID: SimpleBlock, Payload Size: 9 - 0xa3, 0x89, 0x81, 0x07, 0xd0, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, - // ID: BlockGroup, Payload Size: 30 - 0xa0, 0x9e, - // ID: Block, Payload Size: 9 - 0xa1, 0x89, 0x81, 0x0b, 0xb8, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x00, - // ID: BlockAdditions, Payload Size: 16 - 0x75, 0xa1, 0x90, - // ID: BlockMore, Payload Size: 14 - 0xa6, 0x8e, - // ID: BlockAddID, Payload Size: 1 - 0xee, 0x85, 0x9a, 0x78, 0x56, 0x34, 0x12, - // ID: BlockAdditional, Payload Size: 5 - 0xa5, 0x85, 0x73, 0x69, 0x64, 0x65, 0x00, - // ID: BlockGroup, Payload Size: 15 - 0xa0, 0x8f, - // ID: Block, Payload Size: 9 - 0xa1, 0x89, 0x81, 0x0f, 0xa0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x00, - // BlockDuration: 1000 - 0x9b, 0x82, 0x03, 0xe8, + 0x1f, + 0x43, + 0xb6, + 0x75, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x55, + // Timecode: 0 + 0xe7, + 0x81, + 0x00, + // ID: SimpleBlock, Payload Size: 9 + 0xa3, + 0x89, + 0x81, + 0x00, + 0x00, + 0x80, + 0xde, + 0xad, + 0xbe, + 0xef, + 0x00, + // ID: SimpleBlock, Payload Size: 9 + 0xa3, + 0x89, + 0x81, + 0x03, + 0xe8, + 0x80, + 0xde, + 0xad, + 0xbe, + 0xef, + 0x00, + // ID: SimpleBlock, Payload Size: 9 + 0xa3, + 0x89, + 0x81, + 0x07, + 0xd0, + 0x80, + 0xde, + 0xad, + 0xbe, + 0xef, + 0x00, + // ID: BlockGroup, Payload Size: 30 + 0xa0, + 0x9e, + // ID: Block, Payload Size: 9 + 0xa1, + 0x89, + 0x81, + 0x0b, + 0xb8, + 0x00, + 0xde, + 0xad, + 0xbe, + 0xef, + 0x00, + // ID: BlockAdditions, Payload Size: 16 + 0x75, + 0xa1, + 0x90, + // ID: BlockMore, Payload Size: 14 + 0xa6, + 0x8e, + // ID: BlockAddID, Payload Size: 1 + 0xee, + 0x85, + 0x9a, + 0x78, + 0x56, + 0x34, + 0x12, + // ID: BlockAdditional, Payload Size: 5 + 0xa5, + 0x85, + 0x73, + 0x69, + 0x64, + 0x65, + 0x00, + // ID: BlockGroup, Payload Size: 15 + 0xa0, + 0x8f, + // ID: Block, Payload Size: 9 + 0xa1, + 0x89, + 0x81, + 0x0f, + 0xa0, + 0x00, + 0xde, + 0xad, + 0xbe, + 0xef, + 0x00, + // BlockDuration: 1000 + 0x9b, + 0x82, + 0x03, + 0xe8, }; } // namespace @@ -163,7 +459,7 @@ TEST_F(SingleSegmentSegmenterTest, BasicSupport) { CreateSample(kKeyFrame, kDuration, side_data_flag); ASSERT_OK(segmenter_->AddSample(*sample)); } - ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, !kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, !kSubsegment, 5)); ASSERT_OK(segmenter_->Finalize()); ASSERT_FILE_ENDS_WITH(OutputFileName().c_str(), kBasicSupportData); @@ -176,14 +472,14 @@ TEST_F(SingleSegmentSegmenterTest, SplitsClustersOnSegment) { // Write the samples to the Segmenter. for (int i = 0; i < 8; i++) { if (i == 5) { - ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, !kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, !kSubsegment, 5)); } std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, kNoSideData); ASSERT_OK(segmenter_->AddSample(*sample)); } - ASSERT_OK( - segmenter_->FinalizeSegment(5 * kDuration, 8 * kDuration, !kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(5 * kDuration, 8 * kDuration, + !kSubsegment, 8)); ASSERT_OK(segmenter_->Finalize()); // Verify the resulting data. @@ -201,13 +497,13 @@ TEST_F(SingleSegmentSegmenterTest, IgnoresSubsegment) { // Write the samples to the Segmenter. for (int i = 0; i < 8; i++) { if (i == 5) { - ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, kSubsegment, 5)); } std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, kNoSideData); ASSERT_OK(segmenter_->AddSample(*sample)); } - ASSERT_OK(segmenter_->FinalizeSegment(0, 8 * kDuration, !kSubsegment)); + ASSERT_OK(segmenter_->FinalizeSegment(0, 8 * kDuration, !kSubsegment, 8)); ASSERT_OK(segmenter_->Finalize()); // Verify the resulting data. @@ -234,7 +530,7 @@ TEST_F(SingleSegmentSegmenterTest, LargeTimestamp) { ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK(segmenter_->FinalizeSegment(kLargeTimestamp, 5 * kDuration, - !kSubsegment)); + !kSubsegment, 5)); ASSERT_OK(segmenter_->Finalize()); // Verify the resulting data. @@ -270,7 +566,7 @@ TEST_F(SingleSegmentSegmenterTest, ReallyLargeTimestamp) { ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK(segmenter_->FinalizeSegment(kReallyLargeTimestamp, 5 * kDuration, - !kSubsegment)); + !kSubsegment, 5)); ASSERT_OK(segmenter_->Finalize()); // Verify the resulting data. diff --git a/packager/media/formats/webm/webm_muxer.cc b/packager/media/formats/webm/webm_muxer.cc index dcf02be7a7a..786640e5780 100644 --- a/packager/media/formats/webm/webm_muxer.cc +++ b/packager/media/formats/webm/webm_muxer.cc @@ -78,9 +78,9 @@ Status WebMMuxer::FinalizeSegment(size_t stream_id, return Status(error::UNIMPLEMENTED, "Key rotation is not implemented for WebM"); } - return segmenter_->FinalizeSegment(segment_info.start_timestamp, - segment_info.duration, - segment_info.is_subsegment); + return segmenter_->FinalizeSegment( + segment_info.start_timestamp, segment_info.duration, + segment_info.is_subsegment, segment_info.segment_index); } void WebMMuxer::FireOnMediaStartEvent() { diff --git a/packager/media/formats/webvtt/webvtt_text_output_handler.cc b/packager/media/formats/webvtt/webvtt_text_output_handler.cc index 3043209114e..e338bdbccc2 100644 --- a/packager/media/formats/webvtt/webvtt_text_output_handler.cc +++ b/packager/media/formats/webvtt/webvtt_text_output_handler.cc @@ -85,13 +85,13 @@ Status WebVttTextOutputHandler::OnSegmentInfo(const SegmentInfo& info) { total_duration_ms_ += info.duration; const std::string& segment_template = muxer_options_.segment_template; - const uint32_t index = segment_index_++; + const uint32_t index = info.segment_index; const uint64_t start = info.start_timestamp; const uint64_t duration = info.duration; const uint32_t bandwidth = muxer_options_.bandwidth; const std::string filename = - GetSegmentName(segment_template, start, index, bandwidth); + GetSegmentName(segment_template, start, index - 1, bandwidth); // Write everything to the file before telling the manifest so that the // file will exist on disk. @@ -110,7 +110,8 @@ Status WebVttTextOutputHandler::OnSegmentInfo(const SegmentInfo& info) { // Update the manifest with our new file. const uint64_t size = File::GetFileSize(filename.c_str()); - muxer_listener_->OnNewSegment(filename, start, duration, size); + muxer_listener_->OnNewSegment(filename, start, duration, size, + info.segment_index); return Status::OK; } diff --git a/packager/media/formats/webvtt/webvtt_text_output_handler_unittest.cc b/packager/media/formats/webvtt/webvtt_text_output_handler_unittest.cc index cde1afa78dc..3212f5061a6 100644 --- a/packager/media/formats/webvtt/webvtt_text_output_handler_unittest.cc +++ b/packager/media/formats/webvtt/webvtt_text_output_handler_unittest.cc @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd +#include "packager/media/formats/webvtt/webvtt_text_output_handler.h" + #include #include @@ -12,7 +14,6 @@ #include "packager/media/base/text_stream_info.h" #include "packager/media/event/combined_muxer_listener.h" #include "packager/media/event/mock_muxer_listener.h" -#include "packager/media/formats/webvtt/webvtt_text_output_handler.h" #include "packager/status_test_util.h" namespace shaka { @@ -60,7 +61,7 @@ class WebVttSegmentedOutputTest : public MediaHandlerTestBase { }; TEST_F(WebVttSegmentedOutputTest, WithNoSegmentAndWithNoSamples) { - EXPECT_CALL(*muxer_listener_, OnNewSegment(_, _, _, _)).Times(0); + EXPECT_CALL(*muxer_listener_, OnNewSegment(_, _, _, _, _)).Times(0); { // No segments should have be created as there were no samples. @@ -94,7 +95,7 @@ TEST_F(WebVttSegmentedOutputTest, WithOneSegmentAndWithOneSample) { EXPECT_CALL(*muxer_listener_, OnMediaStart(_, _, _, _)); EXPECT_CALL(*muxer_listener_, OnNewSegment(kSegmentedFileOutput1, kSegmentStart, - kSegmentDuration, _)); + kSegmentDuration, _, 1)); const float kMediaDuration = 1 * kSegmentDuration / kMillisecondsPerSecond; EXPECT_CALL(*muxer_listener_, @@ -141,10 +142,10 @@ TEST_F(WebVttSegmentedOutputTest, WithTwoSegmentAndWithOneSample) { EXPECT_CALL(*muxer_listener_, OnMediaStart(_, _, _, _)); EXPECT_CALL(*muxer_listener_, OnNewSegment(kSegmentedFileOutput1, kSegment1Start, - kSegmentDuration, _)); + kSegmentDuration, _, _)); EXPECT_CALL(*muxer_listener_, OnNewSegment(kSegmentedFileOutput2, kSegment2Start, - kSegmentDuration, _)); + kSegmentDuration, _, _)); const float kMediaDuration = 2 * kSegmentDuration / kMillisecondsPerSecond; EXPECT_CALL(*muxer_listener_, @@ -201,10 +202,10 @@ TEST_F(WebVttSegmentedOutputTest, WithAnEmptySegment) { EXPECT_CALL(*muxer_listener_, OnMediaStart(_, _, _, _)); EXPECT_CALL(*muxer_listener_, OnNewSegment(kSegmentedFileOutput1, kSegment1Start, - kSegmentDuration, _)); + kSegmentDuration, _, _)); EXPECT_CALL(*muxer_listener_, OnNewSegment(kSegmentedFileOutput2, kSegment2Start, - kSegmentDuration, _)); + kSegmentDuration, _, _)); const float kMediaDuration = 2 * kSegmentDuration / kMillisecondsPerSecond; EXPECT_CALL(*muxer_listener_, diff --git a/packager/mpd/base/adaptation_set_unittest.cc b/packager/mpd/base/adaptation_set_unittest.cc index 0d6e37d3b89..1b602b98582 100644 --- a/packager/mpd/base/adaptation_set_unittest.cc +++ b/packager/mpd/base/adaptation_set_unittest.cc @@ -682,30 +682,33 @@ TEST_F(OnDemandAdaptationSetTest, SubsegmentAlignment) { const uint64_t kStartTime = 0u; const uint64_t kDuration = 10u; const uint64_t kAnySize = 19834u; + const uint64_t kSegmentIndex = 1; auto adaptation_set = CreateAdaptationSet(kNoLanguage); Representation* representation_480p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo)); // Add a subsegment immediately before adding the 360p Representation. // This should still work for VOD. - representation_480p->AddNewSegment(kStartTime, kDuration, kAnySize); + representation_480p->AddNewSegment(kStartTime, kDuration, kAnySize, + kSegmentIndex); Representation* representation_360p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k360pMediaInfo)); - representation_360p->AddNewSegment(kStartTime, kDuration, kAnySize); + representation_360p->AddNewSegment(kStartTime, kDuration, kAnySize, + kSegmentIndex); xml::scoped_xml_ptr aligned(adaptation_set->GetXml()); EXPECT_THAT(aligned.get(), AttributeEqual("subsegmentAlignment", "true")); // Unknown because 480p has an extra subsegments. - representation_480p->AddNewSegment(11, 20, kAnySize); + representation_480p->AddNewSegment(11, 20, kAnySize, (11 / 20) + 1); xml::scoped_xml_ptr alignment_unknown(adaptation_set->GetXml()); EXPECT_THAT(alignment_unknown.get(), Not(AttributeSet("subsegmentAlignment"))); // Add segments that make them not aligned. - representation_360p->AddNewSegment(10, 1, kAnySize); - representation_360p->AddNewSegment(11, 19, kAnySize); + representation_360p->AddNewSegment(10, 1, kAnySize, (10 / 1) + 1); + representation_360p->AddNewSegment(11, 19, kAnySize, (11 / 19) + 1); xml::scoped_xml_ptr unaligned(adaptation_set->GetXml()); EXPECT_THAT(unaligned.get(), Not(AttributeSet("subsegmentAlignment"))); @@ -747,8 +750,8 @@ TEST_F(OnDemandAdaptationSetTest, ForceSetsubsegmentAlignment) { static_assert(kStartTime1 != kStartTime2, "StartTimesShouldBeDifferent"); const uint64_t kDuration = 10u; const uint64_t kAnySize = 19834u; - representation_480p->AddNewSegment(kStartTime1, kDuration, kAnySize); - representation_360p->AddNewSegment(kStartTime2, kDuration, kAnySize); + representation_480p->AddNewSegment(kStartTime1, kDuration, kAnySize, 1); + representation_360p->AddNewSegment(kStartTime2, kDuration, kAnySize, 1); xml::scoped_xml_ptr unaligned(adaptation_set->GetXml()); EXPECT_THAT(unaligned.get(), Not(AttributeSet("subsegmentAlignment"))); @@ -798,15 +801,15 @@ TEST_F(LiveAdaptationSetTest, SegmentAlignmentDynamicMpd) { Representation* representation_360p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k360pMediaInfo)); - representation_480p->AddNewSegment(kStartTime, kDuration, kAnySize); - representation_360p->AddNewSegment(kStartTime, kDuration, kAnySize); + representation_480p->AddNewSegment(kStartTime, kDuration, kAnySize, 1); + representation_360p->AddNewSegment(kStartTime, kDuration, kAnySize, 1); xml::scoped_xml_ptr aligned(adaptation_set->GetXml()); EXPECT_THAT(aligned.get(), AttributeEqual("segmentAlignment", "true")); // Add segments that make them not aligned. - representation_480p->AddNewSegment(11, 20, kAnySize); - representation_360p->AddNewSegment(10, 1, kAnySize); - representation_360p->AddNewSegment(11, 19, kAnySize); + representation_480p->AddNewSegment(11, 20, kAnySize, (11 / 20) + 1); + representation_360p->AddNewSegment(10, 1, kAnySize, (10 / 1) + 1); + representation_360p->AddNewSegment(11, 19, kAnySize, (11 / 19) + 1); xml::scoped_xml_ptr unaligned(adaptation_set->GetXml()); EXPECT_THAT(unaligned.get(), Not(AttributeSet("segmentAlignment"))); @@ -851,16 +854,16 @@ TEST_F(LiveAdaptationSetTest, SegmentAlignmentStaticMpd) { // Representation. Representation* representation_480p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k480pMediaInfo)); - representation_480p->AddNewSegment(kStartTime, kDuration, kAnySize); + representation_480p->AddNewSegment(kStartTime, kDuration, kAnySize, 1); Representation* representation_360p = adaptation_set->AddRepresentation(ConvertToMediaInfo(k360pMediaInfo)); - representation_360p->AddNewSegment(kStartTime, kDuration, kAnySize); + representation_360p->AddNewSegment(kStartTime, kDuration, kAnySize, 1); representation_480p->AddNewSegment(kStartTime + kDuration, kDuration, - kAnySize); + kAnySize, 2); representation_360p->AddNewSegment(kStartTime + kDuration, kDuration, - kAnySize); + kAnySize, 2); xml::scoped_xml_ptr aligned(adaptation_set->GetXml()); EXPECT_THAT(aligned.get(), AttributeEqual("segmentAlignment", "true")); diff --git a/packager/mpd/base/mock_mpd_builder.h b/packager/mpd/base/mock_mpd_builder.h index 6d46396f306..7c4b70cdeab 100644 --- a/packager/mpd/base/mock_mpd_builder.h +++ b/packager/mpd/base/mock_mpd_builder.h @@ -75,8 +75,11 @@ class MockRepresentation : public Representation { void(const ContentProtectionElement& element)); MOCK_METHOD2(UpdateContentProtectionPssh, void(const std::string& drm_uuid, const std::string& pssh)); - MOCK_METHOD3(AddNewSegment, - void(int64_t start_time, int64_t duration, uint64_t size)); + MOCK_METHOD4(AddNewSegment, + void(int64_t start_time, + int64_t duration, + uint64_t size, + uint64_t segment_index)); MOCK_METHOD1(SetSampleDuration, void(uint32_t sample_duration)); MOCK_CONST_METHOD0(GetMediaInfo, const MediaInfo&()); }; diff --git a/packager/mpd/base/mock_mpd_notifier.h b/packager/mpd/base/mock_mpd_notifier.h index 0726f520925..7d2eacd8254 100644 --- a/packager/mpd/base/mock_mpd_notifier.h +++ b/packager/mpd/base/mock_mpd_notifier.h @@ -7,12 +7,11 @@ #ifndef MPD_BASE_MOCK_MPD_NOTIFIER_H_ #define MPD_BASE_MOCK_MPD_NOTIFIER_H_ -#include "packager/mpd/base/mpd_notifier.h" - #include #include "packager/mpd/base/content_protection_element.h" #include "packager/mpd/base/media_info.pb.h" +#include "packager/mpd/base/mpd_notifier.h" namespace shaka { @@ -26,11 +25,12 @@ class MockMpdNotifier : public MpdNotifier { bool(const MediaInfo& media_info, uint32_t* container_id)); MOCK_METHOD2(NotifySampleDuration, bool(uint32_t container_id, uint32_t sample_duration)); - MOCK_METHOD4(NotifyNewSegment, + MOCK_METHOD5(NotifyNewSegment, bool(uint32_t container_id, uint64_t start_time, uint64_t duration, - uint64_t size)); + uint64_t size, + uint64_t segment_index)); MOCK_METHOD2(NotifyCueEvent, bool(uint32_t container_id, uint64_t timestamp)); MOCK_METHOD4(NotifyEncryptionUpdate, bool(uint32_t container_id, diff --git a/packager/mpd/base/mpd_builder_unittest.cc b/packager/mpd/base/mpd_builder_unittest.cc index acd7ed1c8e1..69a3d170749 100644 --- a/packager/mpd/base/mpd_builder_unittest.cc +++ b/packager/mpd/base/mpd_builder_unittest.cc @@ -4,11 +4,12 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd +#include "packager/mpd/base/mpd_builder.h" + #include #include #include "packager/mpd/base/adaptation_set.h" -#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/period.h" #include "packager/mpd/base/representation.h" #include "packager/mpd/test/mpd_builder_test_helper.h" @@ -71,7 +72,8 @@ class MpdBuilderTest : public ::testing::Test { adaptation_set->AddRepresentation(media_info); representation->AddNewSegment( segment_start_time_seconds * media_info.reference_time_scale(), - segment_duration_seconds * media_info.reference_time_scale(), kBytes); + segment_duration_seconds * media_info.reference_time_scale(), kBytes, + (segment_start_time_seconds / segment_duration_seconds) + 1); } protected: @@ -117,14 +119,14 @@ class LiveMpdBuilderTest : public MpdBuilderTest { // Injects a clock that always returns 2016 Jan 11 15:10:24 in UTC. void InjectTestClock() { - base::Time::Exploded test_time = { 2016, // year. - 1, // month - 1, // day_of_week = Monday. - 11, // day_of_month - 15, // hour. - 10, // minute. - 24, // second. - 0 }; // millisecond. + base::Time::Exploded test_time = {2016, // year. + 1, // month + 1, // day_of_week = Monday. + 11, // day_of_month + 15, // hour. + 10, // minute. + 24, // second. + 0}; // millisecond. ASSERT_TRUE(test_time.HasValidValues()); mpd_.InjectClockForTesting(std::unique_ptr( new TestClock(base::Time::FromUTCExploded(test_time)))); diff --git a/packager/mpd/base/mpd_notifier.h b/packager/mpd/base/mpd_notifier.h index 6576b7c625d..db3701a7477 100644 --- a/packager/mpd/base/mpd_notifier.h +++ b/packager/mpd/base/mpd_notifier.h @@ -11,6 +11,7 @@ #define MPD_BASE_MPD_NOTIFIER_H_ #include + #include #include @@ -65,11 +66,13 @@ class MpdNotifier { /// @param duration is the duration of the new segment, in units of the /// stream's time scale. /// @param size is the new segment size in bytes. + /// @param segment_index is the segment index. /// @return true on success, false otherwise. virtual bool NotifyNewSegment(uint32_t container_id, uint64_t start_time, uint64_t duration, - uint64_t size) = 0; + uint64_t size, + uint64_t segment_index) = 0; /// Notifies MpdBuilder that there is a new CueEvent. /// @param container_id Container ID obtained from calling @@ -105,7 +108,9 @@ class MpdNotifier { virtual bool Flush() = 0; /// @return include_mspr_pro option flag - bool include_mspr_pro() const { return mpd_options_.mpd_params.include_mspr_pro; } + bool include_mspr_pro() const { + return mpd_options_.mpd_params.include_mspr_pro; + } /// @return The dash profile for this object. DashProfile dash_profile() const { return mpd_options_.dash_profile; } diff --git a/packager/mpd/base/representation.cc b/packager/mpd/base/representation.cc index 501f5f42c4c..04442193cea 100644 --- a/packager/mpd/base/representation.cc +++ b/packager/mpd/base/representation.cc @@ -170,7 +170,8 @@ void Representation::UpdateContentProtectionPssh(const std::string& drm_uuid, void Representation::AddNewSegment(int64_t start_time, int64_t duration, - uint64_t size) { + uint64_t size, + uint64_t segment_index) { if (start_time == 0 && duration == 0) { LOG(WARNING) << "Got segment with start_time and duration == 0. Ignoring."; return; @@ -186,7 +187,7 @@ void Representation::AddNewSegment(int64_t start_time, if (state_change_listener_) state_change_listener_->OnNewSegmentForRepresentation(start_time, duration); - AddSegmentInfo(start_time, duration); + AddSegmentInfo(start_time, duration, segment_index); current_buffer_depth_ += segment_infos_.back().duration; bandwidth_estimator_.AddBlock( @@ -269,8 +270,8 @@ xml::scoped_xml_ptr Representation::GetXml() { } if (HasLiveOnlyFields(media_info_) && - !representation.AddLiveOnlyInfo( - media_info_, segment_infos_, start_number_)) { + !representation.AddLiveOnlyInfo(media_info_, segment_infos_, + start_number_)) { LOG(ERROR) << "Failed to add Live info."; return xml::scoped_xml_ptr(); } @@ -328,18 +329,16 @@ bool Representation::HasRequiredMediaInfoFields() const { return true; } -void Representation::AddSegmentInfo(int64_t start_time, int64_t duration) { +void Representation::AddSegmentInfo(int64_t start_time, + int64_t duration, + uint64_t segment_index) { const uint64_t kNoRepeat = 0; const int64_t adjusted_duration = AdjustDuration(duration); if (segment_infos_.empty()) { - const int64_t scaled_target_duration = - mpd_options_.mpd_params.target_segment_duration * - media_info_.reference_time_scale(); - if (mpd_options_.mpd_params.target_segment_duration > 0 && mpd_options_.mpd_params.allow_approximate_segment_timeline) { - start_number_ = start_time / scaled_target_duration + 1; + start_number_ = segment_index; stream_just_started_ = true; } } diff --git a/packager/mpd/base/representation.h b/packager/mpd/base/representation.h index 91c24c30ae6..f0621ea4f68 100644 --- a/packager/mpd/base/representation.h +++ b/packager/mpd/base/representation.h @@ -100,10 +100,12 @@ class Representation { /// stream's time scale. /// @param duration is the duration of the segment, in units of the stream's /// time scale. + /// @param segment_index is the current segment index. /// @param size of the segment in bytes. virtual void AddNewSegment(int64_t start_time, int64_t duration, - uint64_t size); + uint64_t size, + uint64_t segment_index); /// Set the sample duration of this Representation. /// Sample duration is not available right away especially for live. This @@ -182,7 +184,9 @@ class Representation { // Add a SegmentInfo. This function may insert an adjusted SegmentInfo if // |allow_approximate_segment_timeline_| is set. - void AddSegmentInfo(int64_t start_time, int64_t duration); + void AddSegmentInfo(int64_t start_time, + int64_t duration, + uint64_t segment_index); // Check if two timestamps are approximately equal if // |allow_approximate_segment_timeline_| is set; Otherwise check whether the diff --git a/packager/mpd/base/representation_unittest.cc b/packager/mpd/base/representation_unittest.cc index 4f26bc6199a..6a73062e522 100644 --- a/packager/mpd/base/representation_unittest.cc +++ b/packager/mpd/base/representation_unittest.cc @@ -280,7 +280,8 @@ TEST_F(RepresentationTest, kAnyRepresentationId, std::move(listener)); EXPECT_TRUE(representation->Init()); - representation->AddNewSegment(kStartTime, kDuration, 10 /* any size */); + representation->AddNewSegment(kStartTime, kDuration, 10 /* any size */, + (kStartTime / kDuration) + 1); } // Make sure @@ -468,7 +469,8 @@ class SegmentTemplateTest : public RepresentationTest { } for (int i = 0; i < repeat + 1; ++i) { - representation_->AddNewSegment(start_time, duration, size); + representation_->AddNewSegment(start_time, duration, size, + (start_time / duration) + 1); start_time += duration; bandwidth_estimator_.AddBlock( size, static_cast(duration) / kDefaultTimeScale); @@ -801,7 +803,7 @@ TEST_P(ApproximateSegmentTimelineTest, kSElementTemplateWithoutR, kStartTime, kScaledTargetSegmentDuration); EXPECT_THAT(representation_->GetXml().get(), XmlNodeEqual(SegmentTimelineTestBase::ExpectedXml( - expected_s_elements, 1235))); + expected_s_elements, 1372))); } else { expected_s_elements = base::StringPrintf(kSElementTemplateWithoutR, kStartTime, kDurationSmaller); diff --git a/packager/mpd/base/simple_mpd_notifier.cc b/packager/mpd/base/simple_mpd_notifier.cc index 9f93a4a642f..34d9c7f5895 100644 --- a/packager/mpd/base/simple_mpd_notifier.cc +++ b/packager/mpd/base/simple_mpd_notifier.cc @@ -86,14 +86,15 @@ bool SimpleMpdNotifier::NotifySampleDuration(uint32_t container_id, bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id, uint64_t start_time, uint64_t duration, - uint64_t size) { + uint64_t size, + uint64_t segment_index) { base::AutoLock auto_lock(lock_); auto it = representation_map_.find(container_id); if (it == representation_map_.end()) { LOG(ERROR) << "Unexpected container_id: " << container_id; return false; } - it->second->AddNewSegment(start_time, duration, size); + it->second->AddNewSegment(start_time, duration, size, segment_index); return true; } diff --git a/packager/mpd/base/simple_mpd_notifier.h b/packager/mpd/base/simple_mpd_notifier.h index 749da46a1cf..b90e105d63e 100644 --- a/packager/mpd/base/simple_mpd_notifier.h +++ b/packager/mpd/base/simple_mpd_notifier.h @@ -41,7 +41,8 @@ class SimpleMpdNotifier : public MpdNotifier { bool NotifyNewSegment(uint32_t container_id, uint64_t start_time, uint64_t duration, - uint64_t size) override; + uint64_t size, + uint64_t segment_index) override; bool NotifyCueEvent(uint32_t container_id, uint64_t timestamp) override; bool NotifyEncryptionUpdate(uint32_t container_id, const std::string& drm_uuid, diff --git a/packager/mpd/base/simple_mpd_notifier_unittest.cc b/packager/mpd/base/simple_mpd_notifier_unittest.cc index f9d34928676..5032ee89d40 100644 --- a/packager/mpd/base/simple_mpd_notifier_unittest.cc +++ b/packager/mpd/base/simple_mpd_notifier_unittest.cc @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd +#include "packager/mpd/base/simple_mpd_notifier.h" + #include #include #include @@ -13,7 +15,6 @@ #include "packager/mpd/base/mock_mpd_builder.h" #include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_options.h" -#include "packager/mpd/base/simple_mpd_notifier.h" #include "packager/mpd/test/mpd_builder_test_helper.h" namespace shaka { @@ -189,10 +190,10 @@ TEST_F(SimpleMpdNotifierTest, NotifyNewSegment) { const uint32_t kSegmentDuration = 100u; const uint64_t kSegmentSize = 123456u; EXPECT_CALL(*mock_representation, - AddNewSegment(kStartTime, kSegmentDuration, kSegmentSize)); + AddNewSegment(kStartTime, kSegmentDuration, kSegmentSize, 1)); EXPECT_TRUE(notifier.NotifyNewSegment(kRepresentationId, kStartTime, - kSegmentDuration, kSegmentSize)); + kSegmentDuration, kSegmentSize, 1)); } TEST_F(SimpleMpdNotifierTest, NotifyCueEvent) { From 606fd692d439e5744b9f7d0046aaf324c7654435 Mon Sep 17 00:00:00 2001 From: sr90 Date: Mon, 11 May 2020 23:06:26 -0700 Subject: [PATCH 06/10] Removing std::max. --- packager/media/chunking/text_chunker.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packager/media/chunking/text_chunker.cc b/packager/media/chunking/text_chunker.cc index c3a549571a8..60f3bb6fc0b 100644 --- a/packager/media/chunking/text_chunker.cc +++ b/packager/media/chunking/text_chunker.cc @@ -108,8 +108,9 @@ Status TextChunker::DispatchSegment(int64_t duration) { std::shared_ptr info = std::make_shared(); info->start_timestamp = segment_start_; info->duration = duration; - segment_index_ = - std::max(((segment_start_ / segment_duration_) + 1), segment_index_); + if (((segment_start_ / segment_duration_) + 1) > segment_index_) { + segment_index_ = (segment_start_ / segment_duration_) + 1; + } info->segment_index = segment_index_; RETURN_IF_ERROR(DispatchSegmentInfo(kStreamIndex, std::move(info))); From 33f3a594c4593522ff71d54857b35cbd29db5601 Mon Sep 17 00:00:00 2001 From: sr90 Date: Mon, 11 May 2020 23:12:52 -0700 Subject: [PATCH 07/10] Removing unwanted logs. --- packager/file/memory_file.h | 1 - 1 file changed, 1 deletion(-) diff --git a/packager/file/memory_file.h b/packager/file/memory_file.h index a16857609de..472eb29e406 100644 --- a/packager/file/memory_file.h +++ b/packager/file/memory_file.h @@ -32,7 +32,6 @@ class MemoryFile : public File { bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; bool Rename(const std::string& new_file_name) override; - // { LOG(ERROR) << "NIKKI: In memory file"; return false;} /// @} /// Deletes all memory file data created. This assumes that there are no From 07e1e0dea2a53bc788836b9965192efeff074403 Mon Sep 17 00:00:00 2001 From: sr90 Date: Thu, 11 Jun 2020 12:42:08 -0700 Subject: [PATCH 08/10] Using buffer instead of renaming file in FinalizeSegment for m2ts and webm. --- packager/file/callback_file.cc | 8 - packager/file/callback_file.h | 1 - packager/file/file.h | 10 - packager/file/local_file.cc | 8 - packager/file/local_file.h | 1 - packager/file/memory_file.cc | 7 - packager/file/memory_file.h | 1 - packager/file/threaded_io_file.cc | 8 - packager/file/threaded_io_file.h | 1 - packager/file/udp_file.cc | 7 - packager/file/udp_file.h | 1 - packager/media/chunking/chunking_handler.cc | 2 +- .../media/event/hls_notify_muxer_listener.cc | 1 - .../hls_notify_muxer_listener_unittest.cc | 12 +- .../mpd_notify_muxer_listener_unittest.cc | 13 +- .../vod_media_info_dump_muxer_listener.cc | 7 +- ...media_info_dump_muxer_listener_unittest.cc | 7 +- packager/media/formats/mp2t/ts_segmenter.cc | 32 +- packager/media/formats/mp2t/ts_segmenter.h | 10 +- .../formats/mp2t/ts_segmenter_unittest.cc | 20 +- packager/media/formats/mp2t/ts_writer.cc | 72 +- packager/media/formats/mp2t/ts_writer.h | 26 +- .../media/formats/mp2t/ts_writer_unittest.cc | 30 +- .../webm/encrypted_segmenter_unittest.cc | 676 +++++------------- packager/media/formats/webm/mkv_writer.cc | 60 +- packager/media/formats/webm/mkv_writer.h | 15 +- .../formats/webm/multi_segment_segmenter.cc | 22 +- .../formats/webm/multi_segment_segmenter.h | 1 - .../webm/multi_segment_segmenter_unittest.cc | 102 +-- .../webm/single_segment_segmenter_unittest.cc | 509 +++---------- packager/mpd/base/mpd_notifier.h | 1 - 31 files changed, 494 insertions(+), 1177 deletions(-) diff --git a/packager/file/callback_file.cc b/packager/file/callback_file.cc index 7f715681d65..55dae0a98e3 100644 --- a/packager/file/callback_file.cc +++ b/packager/file/callback_file.cc @@ -65,12 +65,4 @@ bool CallbackFile::Open() { return ParseCallbackFileName(file_name(), &callback_params_, &name_); } -bool CallbackFile::Rename(const std::string& new_file_name) { - if (rename(file_name().c_str(), new_file_name.c_str()) != 0) { - return false; - } - set_file_name(new_file_name); - return true; -} - } // namespace shaka diff --git a/packager/file/callback_file.h b/packager/file/callback_file.h index 618cbb0bf82..60e31a498fd 100644 --- a/packager/file/callback_file.h +++ b/packager/file/callback_file.h @@ -28,7 +28,6 @@ class CallbackFile : public File { bool Flush() override; bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; - bool Rename(const std::string& new_file_name) override; /// @} protected: diff --git a/packager/file/file.h b/packager/file/file.h index 33e8115c05c..295fddb75c8 100644 --- a/packager/file/file.h +++ b/packager/file/file.h @@ -93,16 +93,6 @@ class File { /// off. const std::string& file_name() const { return file_name_; } - /// Set the file name. - void set_file_name(const std::string& newFileName) { - file_name_ = newFileName; - } - - /// Rename the file - /// @param new file name. - /// @return true on success, false otherwise. - virtual bool Rename(const std::string& new_file_name) = 0; - // ************************************************************ // * Static Methods: File-on-the-filesystem status // ************************************************************ diff --git a/packager/file/local_file.cc b/packager/file/local_file.cc index c6430a0ba32..4120df1812b 100644 --- a/packager/file/local_file.cc +++ b/packager/file/local_file.cc @@ -200,12 +200,4 @@ bool LocalFile::Delete(const char* file_name) { return base::DeleteFile(base::FilePath::FromUTF8Unsafe(file_name), false); } -bool LocalFile::Rename(const std::string& new_file_name) { - if (rename(file_name().c_str(), new_file_name.c_str()) != 0) { - return false; - } - set_file_name(new_file_name); - return true; -} - } // namespace shaka diff --git a/packager/file/local_file.h b/packager/file/local_file.h index c9e5a8b4a08..ecc3c0b62c3 100644 --- a/packager/file/local_file.h +++ b/packager/file/local_file.h @@ -33,7 +33,6 @@ class LocalFile : public File { bool Flush() override; bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; - bool Rename(const std::string& new_file_name) override; /// @} /// Delete a local file. diff --git a/packager/file/memory_file.cc b/packager/file/memory_file.cc index 221124137cc..415a738723f 100644 --- a/packager/file/memory_file.cc +++ b/packager/file/memory_file.cc @@ -191,11 +191,4 @@ void MemoryFile::Delete(const std::string& file_name) { FileSystem::Instance()->Delete(file_name); } -bool MemoryFile::Rename(const std::string& new_file_name) { - if (rename(file_name().c_str(), new_file_name.c_str()) != 0) { - return false; - } - set_file_name(new_file_name); - return true; -} } // namespace shaka diff --git a/packager/file/memory_file.h b/packager/file/memory_file.h index 472eb29e406..02f80199da5 100644 --- a/packager/file/memory_file.h +++ b/packager/file/memory_file.h @@ -31,7 +31,6 @@ class MemoryFile : public File { bool Flush() override; bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; - bool Rename(const std::string& new_file_name) override; /// @} /// Deletes all memory file data created. This assumes that there are no diff --git a/packager/file/threaded_io_file.cc b/packager/file/threaded_io_file.cc index 83066d55373..ff8ca7313ac 100644 --- a/packager/file/threaded_io_file.cc +++ b/packager/file/threaded_io_file.cc @@ -183,14 +183,6 @@ void ThreadedIoFile::RunInInputMode() { } } -bool ThreadedIoFile::Rename(const std::string& new_file_name) { - if (rename(file_name().c_str(), new_file_name.c_str()) != 0) { - return false; - } - set_file_name(new_file_name); - return true; -} - void ThreadedIoFile::RunInOutputMode() { DCHECK(internal_file_); DCHECK_EQ(kOutputMode, mode_); diff --git a/packager/file/threaded_io_file.h b/packager/file/threaded_io_file.h index c1fb289fb3d..7e228dd1bff 100644 --- a/packager/file/threaded_io_file.h +++ b/packager/file/threaded_io_file.h @@ -36,7 +36,6 @@ class ThreadedIoFile : public File { bool Flush() override; bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; - bool Rename(const std::string& new_file_name) override; /// @} protected: diff --git a/packager/file/udp_file.cc b/packager/file/udp_file.cc index 2b54b18a018..64c4c6458a1 100644 --- a/packager/file/udp_file.cc +++ b/packager/file/udp_file.cc @@ -290,11 +290,4 @@ bool UdpFile::Open() { return true; } -bool UdpFile::Rename(const std::string& new_file_name) { - if (rename(file_name().c_str(), new_file_name.c_str()) != 0) { - return false; - } - set_file_name(new_file_name); - return true; -} } // namespace shaka diff --git a/packager/file/udp_file.h b/packager/file/udp_file.h index 55cbb36aa6f..039563abdbc 100644 --- a/packager/file/udp_file.h +++ b/packager/file/udp_file.h @@ -38,7 +38,6 @@ class UdpFile : public File { bool Flush() override; bool Seek(uint64_t position) override; bool Tell(uint64_t* position) override; - bool Rename(const std::string& new_file_name) override; /// @} protected: diff --git a/packager/media/chunking/chunking_handler.cc b/packager/media/chunking/chunking_handler.cc index 18ab3ffd2af..25a18eeed27 100644 --- a/packager/media/chunking/chunking_handler.cc +++ b/packager/media/chunking/chunking_handler.cc @@ -106,7 +106,7 @@ Status ChunkingHandler::OnMediaSample( : (timestamp - cue_offset_) / segment_duration_; if (!segment_start_time_ || IsNewSegmentIndex(segment_index, current_segment_index_)) { - if (!isGreaterSegmentIndex(segment_index, current_segment_index_)) { + if (!isGreaterSegmentIndex(segment_index, set_segment_index_)) { set_segment_index_ = current_segment_index_ + 1; } else { set_segment_index_ = segment_index; diff --git a/packager/media/event/hls_notify_muxer_listener.cc b/packager/media/event/hls_notify_muxer_listener.cc index 4553269e992..7dc75a4152e 100644 --- a/packager/media/event/hls_notify_muxer_listener.cc +++ b/packager/media/event/hls_notify_muxer_listener.cc @@ -7,7 +7,6 @@ #include "packager/media/event/hls_notify_muxer_listener.h" #include - #include "packager/base/logging.h" #include "packager/hls/base/hls_notifier.h" #include "packager/media/base/muxer_options.h" diff --git a/packager/media/event/hls_notify_muxer_listener_unittest.cc b/packager/media/event/hls_notify_muxer_listener_unittest.cc index c73c9fa380c..622525721c1 100644 --- a/packager/media/event/hls_notify_muxer_listener_unittest.cc +++ b/packager/media/event/hls_notify_muxer_listener_unittest.cc @@ -4,14 +4,13 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#include "packager/media/event/hls_notify_muxer_listener.h" - #include #include #include "packager/hls/base/hls_notifier.h" #include "packager/media/base/muxer_options.h" #include "packager/media/base/protection_system_specific_info.h" +#include "packager/media/event/hls_notify_muxer_listener.h" #include "packager/media/event/muxer_listener_test_helper.h" namespace shaka { @@ -67,15 +66,12 @@ class MockHlsNotifier : public hls::HlsNotifier { // Doesn't really matter what the values are as long as it is a system ID (16 // bytes). const uint8_t kAnySystemId[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, }; const uint8_t kAnyData[] = { - 0xFF, - 0x78, - 0xAA, - 0x6B, + 0xFF, 0x78, 0xAA, 0x6B, }; const uint64_t kSegmentStartOffset = 10000; diff --git a/packager/media/event/mpd_notify_muxer_listener_unittest.cc b/packager/media/event/mpd_notify_muxer_listener_unittest.cc index 69a70e25349..a102ef11d18 100644 --- a/packager/media/event/mpd_notify_muxer_listener_unittest.cc +++ b/packager/media/event/mpd_notify_muxer_listener_unittest.cc @@ -10,7 +10,6 @@ #include #include #include - #include #include @@ -53,8 +52,8 @@ void SetDefaultLiveMuxerOptions(media::MuxerOptions* muxer_options) { } const uint8_t kBogusIv[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x83, 0xC3, 0x66, 0xEE, 0xAB, 0xB2, 0xF1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x83, 0xC3, 0x66, 0xEE, 0xAB, 0xB2, 0xF1, }; } // namespace @@ -164,9 +163,7 @@ TEST_F(MpdNotifyMuxerListenerTest, VodEncryptedContent) { " protection_scheme: 'cenc'\n" " content_protection_entry {\n" " uuid: '00010203-0405-0607-0809-0a0b0c0d0e0f'\n" - " pssh: '" + - std::string(kExpectedDefaultPsshBox) + - "'\n" + " pssh: '" + std::string(kExpectedDefaultPsshBox) + "'\n" " }\n" " default_key_id: 'defaultkeyid'\n" " include_mspr_pro: 1\n" @@ -370,9 +367,7 @@ TEST_P(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) { " default_key_id: \"defaultkeyid\"\n" " content_protection_entry {\n" " uuid: '00010203-0405-0607-0809-0a0b0c0d0e0f'\n" - " pssh: \"" + - std::string(kExpectedDefaultPsshBox) + - "\"\n" + " pssh: \"" + std::string(kExpectedDefaultPsshBox) + "\"\n" " }\n" " protection_scheme: 'cbcs'\n" " include_mspr_pro: 1\n" diff --git a/packager/media/event/vod_media_info_dump_muxer_listener.cc b/packager/media/event/vod_media_info_dump_muxer_listener.cc index b23b8a304db..590714bb717 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener.cc @@ -49,8 +49,11 @@ void VodMediaInfoDumpMuxerListener::OnMediaStart( ContainerType container_type) { DCHECK(muxer_options.segment_template.empty()); media_info_.reset(new MediaInfo()); - if (!internal::GenerateMediaInfo(muxer_options, stream_info, time_scale, - container_type, media_info_.get())) { + if (!internal::GenerateMediaInfo(muxer_options, + stream_info, + time_scale, + container_type, + media_info_.get())) { LOG(ERROR) << "Failed to generate MediaInfo from input."; return; } diff --git a/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc b/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc index 28ca608046e..406774cfb8d 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc @@ -4,8 +4,6 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#include "packager/media/event/vod_media_info_dump_muxer_listener.h" - #include #include #include @@ -20,6 +18,7 @@ #include "packager/media/base/muxer_options.h" #include "packager/media/base/video_stream_info.h" #include "packager/media/event/muxer_listener_test_helper.h" +#include "packager/media/event/vod_media_info_dump_muxer_listener.h" #include "packager/mpd/base/media_info.pb.h" namespace { @@ -30,8 +29,8 @@ const uint8_t kBogusDefaultKeyId[] = {0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x5f, 0x69, 0x64, 0x5f}; const uint8_t kBogusIv[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x83, 0xC3, 0x66, 0xEE, 0xAB, 0xB2, 0xF1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x83, 0xC3, 0x66, 0xEE, 0xAB, 0xB2, 0xF1, }; const bool kInitialEncryptionInfo = true; diff --git a/packager/media/formats/mp2t/ts_segmenter.cc b/packager/media/formats/mp2t/ts_segmenter.cc index 2feca59ac9f..b492e54be57 100644 --- a/packager/media/formats/mp2t/ts_segmenter.cc +++ b/packager/media/formats/mp2t/ts_segmenter.cc @@ -102,7 +102,7 @@ Status TsSegmenter::AddSample(const MediaSample& sample) { if (sample.is_encrypted()) ts_writer_->SignalEncrypted(); - if (!ts_writer_file_opened_ && !sample.is_key_frame()) + if (!ts_writer_buffer_initialized_ && !sample.is_key_frame()) LOG(WARNING) << "A segment will start with a non key frame."; if (!pes_packet_generator_->PushSample(sample)) { @@ -122,20 +122,16 @@ void TsSegmenter::InjectPesPacketGeneratorForTesting( } void TsSegmenter::SetTsWriterFileOpenedForTesting(bool value) { - ts_writer_file_opened_ = value; + ts_writer_buffer_initialized_ = value; } Status TsSegmenter::OpenNewSegmentIfClosed(int64_t next_pts) { - if (ts_writer_file_opened_) + if (ts_writer_buffer_initialized_) return Status::OK; - const std::string segment_name = - GetSegmentName(muxer_options_.segment_template, next_pts, - segment_number_++, muxer_options_.bandwidth); next_pts_ = next_pts; - if (!ts_writer_->NewSegment(segment_name)) + if (!ts_writer_->NewSegment()) return Status(error::MUXER_FAILURE, "Failed to initilize TsPacketWriter."); - current_segment_path_ = segment_name; - ts_writer_file_opened_ = true; + ts_writer_buffer_initialized_ = true; return Status::OK; } @@ -149,13 +145,13 @@ Status TsSegmenter::WritePesPacketsToFile() { return status; if (listener_ && IsVideoCodec(codec_) && pes_packet->is_key_frame()) { - base::Optional start_pos = ts_writer_->GetFilePosition(); + base::Optional start_pos = ts_writer_->GetPosition(); const int64_t timestamp = pes_packet->pts(); if (!ts_writer_->AddPesPacket(std::move(pes_packet))) return Status(error::MUXER_FAILURE, "Failed to add PES packet."); - base::Optional end_pos = ts_writer_->GetFilePosition(); + base::Optional end_pos = ts_writer_->GetPosition(); if (!start_pos || !end_pos) { return Status(error::MUXER_FAILURE, "Failed to get file position in WritePesPacketsToFile."); @@ -169,7 +165,6 @@ Status TsSegmenter::WritePesPacketsToFile() { return Status::OK; } -// OPTION : Set segment_number_ data member here. Status TsSegmenter::FinalizeSegment(uint64_t start_timestamp, uint64_t duration, uint64_t segment_index) { @@ -180,21 +175,22 @@ Status TsSegmenter::FinalizeSegment(uint64_t start_timestamp, if (!status.ok()) return status; - // This method may be called from Finalize() so ts_writer_file_opened_ could + // This method may be called from Finalize() so ts_writer_buffer_initialized_ could // be false. - if (ts_writer_file_opened_) { - // RENAME FILE: + if (ts_writer_buffer_initialized_) { std::string segment_name_segment_index = GetSegmentName(muxer_options_.segment_template, next_pts_, segment_index - 1, muxer_options_.bandwidth); - if (!ts_writer_->RenameFile(segment_name_segment_index)) { - LOG(ERROR) << "TS Error renaming the file"; + current_segment_path_ = segment_name_segment_index; + if (!ts_writer_->CreateFileAndFlushBuffer(segment_name_segment_index)) { + LOG(ERROR) << "Failed to create new ts segment file."; } if (!ts_writer_->FinalizeSegment()) { return Status(error::MUXER_FAILURE, "Failed to finalize TsWriter."); } + if (listener_) { const int64_t file_size = File::GetFileSize(current_segment_path_.c_str()); @@ -204,7 +200,7 @@ Status TsSegmenter::FinalizeSegment(uint64_t start_timestamp, duration * timescale_scale_, file_size, segment_index); } - ts_writer_file_opened_ = false; + ts_writer_buffer_initialized_ = false; } current_segment_path_.clear(); return Status::OK; diff --git a/packager/media/formats/mp2t/ts_segmenter.h b/packager/media/formats/mp2t/ts_segmenter.h index b5f7c801f81..dad7e840379 100644 --- a/packager/media/formats/mp2t/ts_segmenter.h +++ b/packager/media/formats/mp2t/ts_segmenter.h @@ -8,7 +8,6 @@ #define PACKAGER_MEDIA_FORMATS_MP2T_TS_SEGMENTER_H_ #include - #include "packager/file/file.h" #include "packager/media/base/muxer_options.h" #include "packager/media/formats/mp2t/pes_packet_generator.h" @@ -95,13 +94,10 @@ class TsSegmenter { // Used for calculating the duration in seconds fo the current segment. double timescale_scale_ = 1.0; - // Used for segment template. - uint64_t segment_number_ = 0; - std::unique_ptr ts_writer_; - // Set to true if TsWriter::NewFile() succeeds, set to false after - // TsWriter::FinalizeFile() succeeds. - bool ts_writer_file_opened_ = false; + // Set to true if ts_writer buffer is initialized, set to false after + // TsWriter::FinalizeSegment() succeeds. + bool ts_writer_buffer_initialized_ = false; std::unique_ptr pes_packet_generator_; // For OnNewSegment(). diff --git a/packager/media/formats/mp2t/ts_segmenter_unittest.cc b/packager/media/formats/mp2t/ts_segmenter_unittest.cc index d6bfaf0e9fb..bc5b436bc62 100644 --- a/packager/media/formats/mp2t/ts_segmenter_unittest.cc +++ b/packager/media/formats/mp2t/ts_segmenter_unittest.cc @@ -4,8 +4,6 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -#include "packager/media/formats/mp2t/ts_segmenter.h" - #include #include @@ -14,17 +12,18 @@ #include "packager/media/event/mock_muxer_listener.h" #include "packager/media/formats/mp2t/pes_packet.h" #include "packager/media/formats/mp2t/program_map_table_writer.h" +#include "packager/media/formats/mp2t/ts_segmenter.h" #include "packager/status_test_util.h" namespace shaka { namespace media { namespace mp2t { -using ::testing::_; using ::testing::InSequence; using ::testing::Return; using ::testing::Sequence; using ::testing::StrEq; +using ::testing::_; namespace { @@ -51,9 +50,7 @@ const uint8_t kNaluLengthSize = 1; const bool kIsEncrypted = false; const uint8_t kAnyData[] = { - 0x01, - 0x0F, - 0x3C, + 0x01, 0x0F, 0x3C, }; class MockPesPacketGenerator : public PesPacketGenerator { @@ -84,10 +81,9 @@ class MockTsWriter : public TsWriter { // Create a bogus pmt writer, which we don't really care. new VideoProgramMapTableWriter(kUnknownCodec))) {} - MOCK_METHOD1(NewSegment, bool(const std::string& file_name)); + MOCK_METHOD0(NewSegment, bool()); MOCK_METHOD0(SignalEncrypted, void()); MOCK_METHOD0(FinalizeSegment, bool()); - MOCK_METHOD1(RenameFile, bool(const std::string& file_name)); // Similar to the hack above but takes a std::unique_ptr. MOCK_METHOD1(AddPesPacketMock, bool(PesPacket* pes_packet)); @@ -148,7 +144,7 @@ TEST_F(TsSegmenterTest, AddSample) { MediaSample::CopyFrom(kAnyData, arraysize(kAnyData), kIsKeyFrame); Sequence writer_sequence; - EXPECT_CALL(*mock_ts_writer_, NewSegment(StrEq("file1.ts"))) + EXPECT_CALL(*mock_ts_writer_, NewSegment()) .InSequence(writer_sequence) .WillOnce(Return(true)); @@ -214,7 +210,7 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { kTimeScale * 11, _, _)); Sequence writer_sequence; - EXPECT_CALL(*mock_ts_writer_, NewSegment(StrEq("file1.ts"))) + EXPECT_CALL(*mock_ts_writer_, NewSegment()) .InSequence(writer_sequence) .WillOnce(Return(true)); @@ -247,7 +243,7 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { .InSequence(writer_sequence) .WillOnce(Return(true)); - EXPECT_CALL(*mock_ts_writer_, NewSegment(StrEq("file2.ts"))) + EXPECT_CALL(*mock_ts_writer_, NewSegment()) .InSequence(writer_sequence) .WillOnce(Return(true)); @@ -344,7 +340,7 @@ TEST_F(TsSegmenterTest, EncryptedSample) { MockMuxerListener mock_listener; TsSegmenter segmenter(options, &mock_listener); - ON_CALL(*mock_ts_writer_, NewSegment(_)).WillByDefault(Return(true)); + ON_CALL(*mock_ts_writer_, NewSegment()).WillByDefault(Return(true)); ON_CALL(*mock_ts_writer_, FinalizeSegment()).WillByDefault(Return(true)); ON_CALL(*mock_ts_writer_, AddPesPacketMock(_)).WillByDefault(Return(true)); ON_CALL(*mock_pes_packet_generator_, Initialize(_)) diff --git a/packager/media/formats/mp2t/ts_writer.cc b/packager/media/formats/mp2t/ts_writer.cc index 3dc35a1c4e1..a473d0ca5c9 100644 --- a/packager/media/formats/mp2t/ts_writer.cc +++ b/packager/media/formats/mp2t/ts_writer.cc @@ -30,23 +30,18 @@ const uint8_t kProgramAssociationTableId = 0x00; const uint8_t kPat[] = { 0x00, // pointer field kProgramAssociationTableId, - 0xB0, // The last 2 '00' assumes that this PAT is not very long. - 0x0D, // Length of the rest of this array. - 0x00, - 0x00, // Transport stream ID is 0. - 0xC1, // version number 0, current next indicator 1. - 0x00, // section number - 0x00, // last section number + 0xB0, // The last 2 '00' assumes that this PAT is not very long. + 0x0D, // Length of the rest of this array. + 0x00, 0x00, // Transport stream ID is 0. + 0xC1, // version number 0, current next indicator 1. + 0x00, // section number + 0x00, // last section number // program number -> PMT PID mapping. - 0x00, - 0x01, // program number is 1. - 0xE0, // first 3 bits is reserved. + 0x00, 0x01, // program number is 1. + 0xE0, // first 3 bits is reserved. ProgramMapTableWriter::kPmtPid, // CRC32. - 0xF9, - 0x62, - 0xF5, - 0x8B, + 0xF9, 0x62, 0xF5, 0x8B, }; const bool kHasPcr = true; @@ -56,7 +51,8 @@ const bool kPayloadUnitStartIndicator = true; // without adaptation field or the payload. const int kTsPacketHeaderSize = 4; const int kTsPacketSize = 188; -const int kTsPacketMaximumPayloadSize = kTsPacketSize - kTsPacketHeaderSize; +const int kTsPacketMaximumPayloadSize = + kTsPacketSize - kTsPacketHeaderSize; const size_t kMaxPesPacketLengthValue = 0xFFFF; @@ -91,9 +87,9 @@ void WritePtsOrDts(uint8_t leading_bits, writer->AppendInt(fifth_byte); } -bool WritePesToFile(const PesPacket& pes, - ContinuityCounter* continuity_counter, - File* file) { +bool WritePesToBuffer(const PesPacket& pes, + ContinuityCounter* continuity_counter, + BufferWriter* current_buffer) { // The size of the length field. const int kAdaptationFieldLengthSize = 1; // The size of the flags field. @@ -157,7 +153,9 @@ bool WritePesToFile(const PesPacket& pes, !kPayloadUnitStartIndicator, pid, !kHasPcr, 0, continuity_counter, &output_writer); } - return output_writer.WriteToFile(file).ok(); + + current_buffer->AppendBuffer(output_writer); + return true; } } // namespace @@ -167,7 +165,8 @@ TsWriter::TsWriter(std::unique_ptr pmt_writer) TsWriter::~TsWriter() {} -bool TsWriter::NewSegment(const std::string& file_name) { +bool TsWriter::CreateFileAndFlushBuffer(const std::string& file_name) { + if (current_file_) { LOG(ERROR) << "File " << current_file_->file_name() << " still open."; return false; @@ -177,6 +176,12 @@ bool TsWriter::NewSegment(const std::string& file_name) { LOG(ERROR) << "Failed to open file " << file_name; return false; } + return current_buffer_.get()->WriteToFile(current_file_.get()).ok(); +} + +bool TsWriter::NewSegment() { + + current_buffer_ = std::unique_ptr(new BufferWriter()); BufferWriter psi; WritePatToBuffer(kPat, arraysize(kPat), &pat_continuity_counter_, &psi); @@ -189,12 +194,7 @@ bool TsWriter::NewSegment(const std::string& file_name) { return false; } } - - if (!psi.WriteToFile(current_file_.get()).ok()) { - LOG(ERROR) << "Failed to write PSI to file."; - return false; - } - + current_buffer_.get()->AppendBuffer(psi); return true; } @@ -207,10 +207,9 @@ bool TsWriter::FinalizeSegment() { } bool TsWriter::AddPesPacket(std::unique_ptr pes_packet) { - DCHECK(current_file_); - if (!WritePesToFile(*pes_packet, &elementary_stream_continuity_counter_, - current_file_.get())) { - LOG(ERROR) << "Failed to write pes to file."; + if (!WritePesToBuffer(*pes_packet, &elementary_stream_continuity_counter_, + current_buffer_.get())) { + LOG(ERROR) << "Failed to write pes to buffer."; return false; } @@ -218,16 +217,11 @@ bool TsWriter::AddPesPacket(std::unique_ptr pes_packet) { return true; } -base::Optional TsWriter::GetFilePosition() { - if (!current_file_) +base::Optional TsWriter::GetPosition() { + if (!current_buffer_.get()) return base::nullopt; - uint64_t position; - return current_file_->Tell(&position) ? base::make_optional(position) - : base::nullopt; -} - -bool TsWriter::RenameFile(const std::string& new_segment_name) { - return file()->Rename(new_segment_name); + uint64_t position = current_buffer_.get()->Size(); + return position != 0 ? base::make_optional(position) : base::nullopt; } } // namespace mp2t diff --git a/packager/media/formats/mp2t/ts_writer.h b/packager/media/formats/mp2t/ts_writer.h index 244eb7e1d25..057c517446a 100644 --- a/packager/media/formats/mp2t/ts_writer.h +++ b/packager/media/formats/mp2t/ts_writer.h @@ -16,6 +16,7 @@ #include "packager/file/file.h" #include "packager/file/file_closer.h" #include "packager/media/formats/mp2t/continuity_counter.h" +#include "packager/media/base/buffer_writer.h" namespace shaka { namespace media { @@ -32,33 +33,31 @@ class TsWriter { virtual ~TsWriter(); /// This will fail if the current segment is not finalized. - /// @param file_name is the output file name. - /// @param encrypted must be true if the new segment is encrypted. /// @return true on success, false otherwise. - virtual bool NewSegment(const std::string& file_name); + virtual bool NewSegment(); /// Signals the writer that the rest of the segments are encrypted. virtual void SignalEncrypted(); - /// Flush all the pending PesPackets that have not been written to file and - /// close the file. + /// Flush all the pending PesPackets that have not been added to the buffer. + /// Create the file, flush the buffer and close the file. /// @return true on success, false otherwise. virtual bool FinalizeSegment(); - /// Add PesPacket to the instance. PesPacket might not get written to file + /// Add PesPacket to the instance. PesPacket might not be added to the buffer /// immediately. /// @param pes_packet gets added to the writer. /// @return true on success, false otherwise. virtual bool AddPesPacket(std::unique_ptr pes_packet); - /// @return current file position on success, nullopt otherwise. - base::Optional GetFilePosition(); + /// @return current buffer position on success, nullopt otherwise. + base::Optional GetPosition(); - File* file() { return current_file_.get(); } - - /// Rename the file to newSegmentName. - /// @param new_segment_name - virtual bool RenameFile(const std::string& new_segment_name); + /// Creates a file with name @a file_name and flushes + /// current_buffer_ to it. + /// @param file_name The path to the file to open. + /// @return File creation and buffer flushing succeeded or failed. + virtual bool CreateFileAndFlushBuffer(const std::string& file_name); private: TsWriter(const TsWriter&) = delete; @@ -73,6 +72,7 @@ class TsWriter { std::unique_ptr pmt_writer_; std::unique_ptr current_file_; + std::unique_ptr current_buffer_; }; } // namespace mp2t diff --git a/packager/media/formats/mp2t/ts_writer_unittest.cc b/packager/media/formats/mp2t/ts_writer_unittest.cc index 83ceedd0d42..15c4b5e2308 100644 --- a/packager/media/formats/mp2t/ts_writer_unittest.cc +++ b/packager/media/formats/mp2t/ts_writer_unittest.cc @@ -144,7 +144,7 @@ TEST_F(TsWriterTest, ClearH264Psi) { EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(WriteOnePmt()); TsWriter ts_writer(std::move(mock_pmt_writer)); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); ASSERT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -192,7 +192,7 @@ TEST_F(TsWriterTest, ClearAacPmt) { EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(WriteOnePmt()); TsWriter ts_writer(std::move(mock_pmt_writer)); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); ASSERT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -213,7 +213,7 @@ TEST_F(TsWriterTest, ClearLeadH264Pmt) { .WillOnce(WriteTwoPmts()); TsWriter ts_writer(std::move(mock_pmt_writer)); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); EXPECT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -233,7 +233,7 @@ TEST_F(TsWriterTest, ClearSegmentPmtFailure) { EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(Return(false)); TsWriter ts_writer(std::move(mock_pmt_writer)); - EXPECT_FALSE(ts_writer.NewSegment(test_file_name_)); + EXPECT_FALSE(ts_writer.NewSegment()); } // Check the encrypted segments' PMT (after clear lead). @@ -245,12 +245,12 @@ TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) { EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt()); TsWriter ts_writer(std::move(mock_pmt_writer)); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); EXPECT_TRUE(ts_writer.FinalizeSegment()); // Overwrite the file but as encrypted segment. ts_writer.SignalEncrypted(); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); EXPECT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -270,11 +270,11 @@ TEST_F(TsWriterTest, EncryptedSegmentPmtFailure) { EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(Return(false)); TsWriter ts_writer(std::move(mock_pmt_writer)); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); EXPECT_TRUE(ts_writer.FinalizeSegment()); ts_writer.SignalEncrypted(); - EXPECT_FALSE(ts_writer.NewSegment(test_file_name_)); + EXPECT_FALSE(ts_writer.NewSegment()); } // Same as ClearLeadH264Pmt but for AAC. @@ -285,7 +285,7 @@ TEST_F(TsWriterTest, ClearLeadAacPmt) { .WillOnce(WriteTwoPmts()); TsWriter ts_writer(std::move(mock_pmt_writer)); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); ASSERT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -308,12 +308,12 @@ TEST_F(TsWriterTest, EncryptedSegmentsAacPmt) { EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt()); TsWriter ts_writer(std::move(mock_pmt_writer)); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); EXPECT_TRUE(ts_writer.FinalizeSegment()); // Overwrite the file but as encrypted segment. ts_writer.SignalEncrypted(); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); EXPECT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -329,7 +329,7 @@ TEST_F(TsWriterTest, EncryptedSegmentsAacPmt) { TEST_F(TsWriterTest, AddPesPacket) { TsWriter ts_writer(std::unique_ptr( new VideoProgramMapTableWriter(kCodecForTesting))); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); std::unique_ptr pes(new PesPacket()); pes->set_stream_id(0xE0); @@ -391,7 +391,7 @@ TEST_F(TsWriterTest, AddPesPacket) { TEST_F(TsWriterTest, BigPesPacket) { TsWriter ts_writer(std::unique_ptr( new VideoProgramMapTableWriter(kCodecForTesting))); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); std::unique_ptr pes(new PesPacket()); pes->set_pts(0); @@ -424,7 +424,7 @@ TEST_F(TsWriterTest, BigPesPacket) { TEST_F(TsWriterTest, PesPtsZeroNoDts) { TsWriter ts_writer(std::unique_ptr( new VideoProgramMapTableWriter(kCodecForTesting))); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); std::unique_ptr pes(new PesPacket()); pes->set_stream_id(0xE0); @@ -481,7 +481,7 @@ TEST_F(TsWriterTest, PesPtsZeroNoDts) { TEST_F(TsWriterTest, TsPacketPayload183Bytes) { TsWriter ts_writer(std::unique_ptr( new VideoProgramMapTableWriter(kCodecForTesting))); - EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); + EXPECT_TRUE(ts_writer.NewSegment()); std::unique_ptr pes(new PesPacket()); pes->set_stream_id(0xE0); diff --git a/packager/media/formats/webm/encrypted_segmenter_unittest.cc b/packager/media/formats/webm/encrypted_segmenter_unittest.cc index 8974eac0bc4..d9ccd0fc3e8 100644 --- a/packager/media/formats/webm/encrypted_segmenter_unittest.cc +++ b/packager/media/formats/webm/encrypted_segmenter_unittest.cc @@ -25,529 +25,173 @@ const uint8_t kIv[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, }; const uint8_t kBasicSupportData[] = { - // ID: EBML Header omitted. - // ID: Segment, Payload Size: 432 - 0x18, - 0x53, - 0x80, - 0x67, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x01, - 0xb0, + // ID: EBML Header omitted. + // ID: Segment, Payload Size: 432 + 0x18, 0x53, 0x80, 0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, // ID: SeekHead, Payload Size: 58 - 0x11, - 0x4d, - 0x9b, - 0x74, - 0xba, - // ID: Seek, Payload Size: 11 - 0x4d, - 0xbb, - 0x8b, - // SeekID: binary(4) (Info) - 0x53, - 0xab, - 0x84, - 0x15, - 0x49, - 0xa9, - 0x66, - // SeekPosition: 89 - 0x53, - 0xac, - 0x81, - 0x59, - // ID: Seek, Payload Size: 11 - 0x4d, - 0xbb, - 0x8b, - // SeekID: binary(4) (Tracks) - 0x53, - 0xab, - 0x84, - 0x16, - 0x54, - 0xae, - 0x6b, - // SeekPosition: 182 - 0x53, - 0xac, - 0x81, - 0xb6, - // ID: Seek, Payload Size: 12 - 0x4d, - 0xbb, - 0x8c, - // SeekID: binary(4) (Cues) - 0x53, - 0xab, - 0x84, - 0x1c, - 0x53, - 0xbb, - 0x6b, - // SeekPosition: 279 - 0x53, - 0xac, - 0x82, - 0x01, - 0x17, - // ID: Seek, Payload Size: 12 - 0x4d, - 0xbb, - 0x8c, - // SeekID: binary(4) (Cluster) - 0x53, - 0xab, - 0x84, - 0x1f, - 0x43, - 0xb6, - 0x75, - // SeekPosition: 313 - 0x53, - 0xac, - 0x82, - 0x01, - 0x39, + 0x11, 0x4d, 0x9b, 0x74, 0xba, + // ID: Seek, Payload Size: 11 + 0x4d, 0xbb, 0x8b, + // SeekID: binary(4) (Info) + 0x53, 0xab, 0x84, 0x15, 0x49, 0xa9, 0x66, + // SeekPosition: 89 + 0x53, 0xac, 0x81, 0x59, + // ID: Seek, Payload Size: 11 + 0x4d, 0xbb, 0x8b, + // SeekID: binary(4) (Tracks) + 0x53, 0xab, 0x84, 0x16, 0x54, 0xae, 0x6b, + // SeekPosition: 182 + 0x53, 0xac, 0x81, 0xb6, + // ID: Seek, Payload Size: 12 + 0x4d, 0xbb, 0x8c, + // SeekID: binary(4) (Cues) + 0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b, + // SeekPosition: 279 + 0x53, 0xac, 0x82, 0x01, 0x17, + // ID: Seek, Payload Size: 12 + 0x4d, 0xbb, 0x8c, + // SeekID: binary(4) (Cluster) + 0x53, 0xab, 0x84, 0x1f, 0x43, 0xb6, 0x75, + // SeekPosition: 313 + 0x53, 0xac, 0x82, 0x01, 0x39, // ID: Void, Payload Size: 24 - 0xec, - 0x98, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, + 0xec, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, // ID: Info, Payload Size: 88 - 0x15, - 0x49, - 0xa9, - 0x66, - 0xd8, - // TimecodeScale: 1000000 - 0x2a, - 0xd7, - 0xb1, - 0x83, - 0x0f, - 0x42, - 0x40, - // Duration: float(5000) - 0x44, - 0x89, - 0x84, - 0x45, - 0x9c, - 0x40, - 0x00, - // MuxingApp: 'libwebm-0.2.1.0' - 0x4d, - 0x80, - 0x8f, - 0x6c, - 0x69, - 0x62, - 0x77, - 0x65, - 0x62, - 0x6d, - 0x2d, - 0x30, - 0x2e, - 0x32, - 0x2e, - 0x31, - 0x2e, - 0x30, - // WritingApp: 'https://github.com/google/shaka-packager version test' - 0x57, - 0x41, - 0xb5, - 0x68, - 0x74, - 0x74, - 0x70, - 0x73, - 0x3a, - 0x2f, - 0x2f, - 0x67, - 0x69, - 0x74, - 0x68, - 0x75, - 0x62, - 0x2e, - 0x63, - 0x6f, - 0x6d, - 0x2f, - 0x67, - 0x6f, - 0x6f, - 0x67, - 0x6c, - 0x65, - 0x2f, - 0x73, - 0x68, - 0x61, - 0x6b, - 0x61, - 0x2d, - 0x70, - 0x61, - 0x63, - 0x6b, - 0x61, - 0x67, - 0x65, - 0x72, - 0x20, - 0x76, - 0x65, - 0x72, - 0x73, - 0x69, - 0x6f, - 0x6e, - 0x20, - 0x74, - 0x65, - 0x73, - 0x74, + 0x15, 0x49, 0xa9, 0x66, 0xd8, + // TimecodeScale: 1000000 + 0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40, + // Duration: float(5000) + 0x44, 0x89, 0x84, 0x45, 0x9c, 0x40, 0x00, + // MuxingApp: 'libwebm-0.2.1.0' + 0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30, + 0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30, + // WritingApp: 'https://github.com/google/shaka-packager version test' + 0x57, 0x41, 0xb5, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x73, 0x68, 0x61, 0x6b, 0x61, 0x2d, 0x70, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x74, 0x65, 0x73, 0x74, // ID: Tracks, Payload Size: 92 - 0x16, - 0x54, - 0xae, - 0x6b, - 0xdc, - // ID: Track, Payload Size: 90 - 0xae, - 0xda, - // TrackNumber: 1 - 0xd7, - 0x81, - 0x01, - // TrackUID: 1 - 0x73, - 0xc5, - 0x81, - 0x01, - // TrackType: 1 - 0x83, - 0x81, - 0x01, - // CodecID: 'V_VP8' - 0x86, - 0x85, - 0x56, - 0x5f, - 0x56, - 0x50, - 0x38, - // Language: 'en' - 0x22, - 0xb5, - 0x9c, - 0x82, - 0x65, - 0x6e, - // ID: ContentEncodings, Payload Size: 48 - 0x6d, - 0x80, - 0xb0, - // ID: ContentEncoding, Payload Size: 45 - 0x62, - 0x40, - 0xad, - // ContentEncodingOrder: 0 - 0x50, - 0x31, - 0x81, - 0x00, - // ContentEncodingScope: 1 - 0x50, - 0x32, - 0x81, - 0x01, - // ContentEncodingType: 1 - 0x50, - 0x33, - 0x81, - 0x01, - // ID: ContentEncryption, Payload Size: 30 - 0x50, - 0x35, - 0x9e, - // ContentEncAlgo: 5 - 0x47, - 0xe1, - 0x81, - 0x05, - // ContentEncKeyID: binary(16) - 0x47, - 0xe2, - 0x90, - 0x4c, - 0x6f, - 0x72, - 0x65, - 0x6d, - 0x20, - 0x69, - 0x70, - 0x73, - 0x75, - 0x6d, - 0x20, - 0x64, - 0x6f, - 0x6c, - 0x6f, - // ID: ContentEncAESSettings, Payload Size: 4 - 0x47, - 0xe7, - 0x84, - // AESSettingsCipherMode: 1 - 0x47, - 0xe8, - 0x81, - 0x01, - // ID: Video, Payload Size: 14 - 0xe0, - 0x8e, - // PixelWidth: 100 - 0xb0, - 0x81, - 0x64, - // PixelHeight: 100 - 0xba, - 0x81, - 0x64, - // DisplayWidth: 100 - 0x54, - 0xb0, - 0x81, - 0x64, - // DisplayHeight: 100 - 0x54, - 0xba, - 0x81, - 0x64, + 0x16, 0x54, 0xae, 0x6b, 0xdc, + // ID: Track, Payload Size: 90 + 0xae, 0xda, + // TrackNumber: 1 + 0xd7, 0x81, 0x01, + // TrackUID: 1 + 0x73, 0xc5, 0x81, 0x01, + // TrackType: 1 + 0x83, 0x81, 0x01, + // CodecID: 'V_VP8' + 0x86, 0x85, 0x56, 0x5f, 0x56, 0x50, 0x38, + // Language: 'en' + 0x22, 0xb5, 0x9c, 0x82, 0x65, 0x6e, + // ID: ContentEncodings, Payload Size: 48 + 0x6d, 0x80, 0xb0, + // ID: ContentEncoding, Payload Size: 45 + 0x62, 0x40, 0xad, + // ContentEncodingOrder: 0 + 0x50, 0x31, 0x81, 0x00, + // ContentEncodingScope: 1 + 0x50, 0x32, 0x81, 0x01, + // ContentEncodingType: 1 + 0x50, 0x33, 0x81, 0x01, + // ID: ContentEncryption, Payload Size: 30 + 0x50, 0x35, 0x9e, + // ContentEncAlgo: 5 + 0x47, 0xe1, 0x81, 0x05, + // ContentEncKeyID: binary(16) + 0x47, 0xe2, 0x90, + 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, + 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, + // ID: ContentEncAESSettings, Payload Size: 4 + 0x47, 0xe7, 0x84, + // AESSettingsCipherMode: 1 + 0x47, 0xe8, 0x81, 0x01, + // ID: Video, Payload Size: 14 + 0xe0, 0x8e, + // PixelWidth: 100 + 0xb0, 0x81, 0x64, + // PixelHeight: 100 + 0xba, 0x81, 0x64, + // DisplayWidth: 100 + 0x54, 0xb0, 0x81, 0x64, + // DisplayHeight: 100 + 0x54, 0xba, 0x81, 0x64, // ID: Cues, Payload Size: 29 - 0x1c, - 0x53, - 0xbb, - 0x6b, - 0x9d, - // ID: CuePoint, Payload Size: 12 - 0xbb, - 0x8c, - // CueTime: 0 - 0xb3, - 0x81, - 0x00, - // ID: CueTrackPositions, Payload Size: 7 - 0xb7, - 0x87, - // CueTrack: 1 - 0xf7, - 0x81, - 0x01, - // CueClusterPosition: 313 - 0xf1, - 0x82, - 0x01, - 0x39, - // ID: CuePoint, Payload Size: 13 - 0xbb, - 0x8d, - // CueTime: 3000 - 0xb3, - 0x82, - 0x0b, - 0xb8, - // ID: CueTrackPositions, Payload Size: 7 - 0xb7, - 0x87, - // CueTrack: 1 - 0xf7, - 0x81, - 0x01, - // CueClusterPosition: 370 - 0xf1, - 0x82, - 0x01, - 0x72, + 0x1c, 0x53, 0xbb, 0x6b, 0x9d, + // ID: CuePoint, Payload Size: 12 + 0xbb, 0x8c, + // CueTime: 0 + 0xb3, 0x81, 0x00, + // ID: CueTrackPositions, Payload Size: 7 + 0xb7, 0x87, + // CueTrack: 1 + 0xf7, 0x81, 0x01, + // CueClusterPosition: 313 + 0xf1, 0x82, 0x01, 0x39, + // ID: CuePoint, Payload Size: 13 + 0xbb, 0x8d, + // CueTime: 3000 + 0xb3, 0x82, 0x0b, 0xb8, + // ID: CueTrackPositions, Payload Size: 7 + 0xb7, 0x87, + // CueTrack: 1 + 0xf7, 0x81, 0x01, + // CueClusterPosition: 370 + 0xf1, 0x82, 0x01, 0x72, // ID: Cluster, Payload Size: 45 - 0x1f, - 0x43, - 0xb6, - 0x75, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x2d, - // Timecode: 0 - 0xe7, - 0x81, - 0x00, - // ID: SimpleBlock, Payload Size: 10 - 0xa3, - 0x8a, - 0x81, - 0x00, - 0x00, - 0x80, - // Signal Byte: Clear - 0x00, - // Frame Data: - 0xde, - 0xad, - 0xbe, - 0xef, - 0x00, - // ID: SimpleBlock, Payload Size: 10 - 0xa3, - 0x8a, - 0x81, - 0x03, - 0xe8, - 0x80, - // Signal Byte: Clear - 0x00, - // Frame Data: - 0xde, - 0xad, - 0xbe, - 0xef, - 0x00, - // ID: BlockGroup, Payload Size: 16 - 0xa0, - 0x90, - // ID: Block, Payload Size: 10 - 0xa1, - 0x8a, - 0x81, - 0x07, - 0xd0, - 0x00, - // Signal Byte: Clear - 0x00, - // Frame Data: - 0xde, - 0xad, - 0xbe, - 0xef, - 0x00, - // BlockDuration: 1000 - 0x9b, - 0x82, - 0x03, - 0xe8, + 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, + // Timecode: 0 + 0xe7, 0x81, 0x00, + // ID: SimpleBlock, Payload Size: 10 + 0xa3, 0x8a, 0x81, 0x00, 0x00, 0x80, + // Signal Byte: Clear + 0x00, + // Frame Data: + 0xde, 0xad, 0xbe, 0xef, 0x00, + // ID: SimpleBlock, Payload Size: 10 + 0xa3, 0x8a, 0x81, 0x03, 0xe8, 0x80, + // Signal Byte: Clear + 0x00, + // Frame Data: + 0xde, 0xad, 0xbe, 0xef, 0x00, + // ID: BlockGroup, Payload Size: 16 + 0xa0, 0x90, + // ID: Block, Payload Size: 10 + 0xa1, 0x8a, 0x81, 0x07, 0xd0, 0x00, + // Signal Byte: Clear + 0x00, + // Frame Data: + 0xde, 0xad, 0xbe, 0xef, 0x00, + // BlockDuration: 1000 + 0x9b, 0x82, 0x03, 0xe8, // ID: Cluster, Payload Size: 50 - 0x1f, - 0x43, - 0xb6, - 0x75, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x32, - // Timecode: 3000 - 0xe7, - 0x82, - 0x0b, - 0xb8, - // ID: SimpleBlock: Payload Size: 18 - 0xa3, - 0x92, - 0x81, - 0x00, - 0x00, - 0x80, - // Signal Byte: Encrypted - 0x01, - // IV: - 0x01, - 0x23, - 0x45, - 0x67, - 0x89, - 0x01, - 0x23, - 0x45, - // Frame Data: - 0xde, - 0xad, - 0xbe, - 0xef, - 0x00, - // ID: BlockGroup, Payload Size: 24 - 0xa0, - 0x98, - // ID: Block, Payload Size: 18 - 0xa1, - 0x92, - 0x81, - 0x03, - 0xe8, - 0x00, - // Signal Byte: Encrypted - 0x01, - // IV: - 0x01, - 0x23, - 0x45, - 0x67, - 0x89, - 0x01, - 0x23, - 0x45, - // Frame Data: - 0xde, - 0xad, - 0xbe, - 0xef, - 0x00, - // BlockDuration: 1000 - 0x9b, - 0x82, - 0x03, - 0xe8, + 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, + // Timecode: 3000 + 0xe7, 0x82, 0x0b, 0xb8, + // ID: SimpleBlock: Payload Size: 18 + 0xa3, 0x92, 0x81, 0x00,0x00, 0x80, + // Signal Byte: Encrypted + 0x01, + // IV: + 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, + // Frame Data: + 0xde, 0xad, 0xbe, 0xef, 0x00, + // ID: BlockGroup, Payload Size: 24 + 0xa0, 0x98, + // ID: Block, Payload Size: 18 + 0xa1, 0x92, 0x81, 0x03, 0xe8, 0x00, + // Signal Byte: Encrypted + 0x01, + // IV: + 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, + // Frame Data: + 0xde, 0xad, 0xbe, 0xef, 0x00, + // BlockDuration: 1000 + 0x9b, 0x82, 0x03, 0xe8, }; } // namespace diff --git a/packager/media/formats/webm/mkv_writer.cc b/packager/media/formats/webm/mkv_writer.cc index 0d919d0ac62..0953772c35d 100644 --- a/packager/media/formats/webm/mkv_writer.cc +++ b/packager/media/formats/webm/mkv_writer.cc @@ -13,8 +13,17 @@ MkvWriter::MkvWriter() : position_(0) {} MkvWriter::~MkvWriter() {} +Status MkvWriter::OpenBuffer() { + DCHECK(!file_); + current_buffer_ = std::unique_ptr(new BufferWriter()); + seekable_ = false; + position_ = 0; + return Status::OK; +} + Status MkvWriter::Open(const std::string& name) { DCHECK(!file_); + DCHECK(!current_buffer_); file_.reset(File::Open(name.c_str(), "w")); if (!file_) return Status(error::FILE_FAILURE, "Unable to open file for writing."); @@ -26,6 +35,21 @@ Status MkvWriter::Open(const std::string& name) { return Status::OK; } +bool MkvWriter::CreateFileAndFlushBuffer(const std::string& file_name) { + + if (file_) { + LOG(ERROR) << "File " << file_->file_name() << " is open."; + return false; + } + file_.reset(File::Open(file_name.c_str(), "w")); + if (!file_) { + LOG(ERROR) << "Failed to open file " << file_name; + return false; + } + seekable_ = file_->Seek(0); + return current_buffer_.get()->WriteToFile(file_.get()).ok(); +} + Status MkvWriter::Close() { const std::string file_name = file_->file_name(); if (!file_.release()->Close()) { @@ -38,21 +62,28 @@ Status MkvWriter::Close() { } mkvmuxer::int32 MkvWriter::Write(const void* buf, mkvmuxer::uint32 len) { - DCHECK(file_); - - const char* data = reinterpret_cast(buf); - int64_t total_bytes_written = 0; - while (total_bytes_written < len) { - const int64_t written = - file_->Write(data + total_bytes_written, len - total_bytes_written); - if (written < 0) - return written; - - total_bytes_written += written; + if (seekable_) { + DCHECK(file_); + + const char* data = reinterpret_cast(buf); + int64_t total_bytes_written = 0; + while (total_bytes_written < len) { + const int64_t written = + file_->Write(data + total_bytes_written, len - total_bytes_written); + if (written < 0) + return written; + + total_bytes_written += written; + } + + DCHECK_EQ(total_bytes_written, len); + position_ += len; + } else { + // Write to buffer if file is not present. + const uint8_t* data = reinterpret_cast(buf); + current_buffer_.get()->AppendArray(data, len); + position_ = current_buffer_.get()->Size(); } - - DCHECK_EQ(total_bytes_written, len); - position_ += len; return 0; } @@ -61,6 +92,7 @@ int64_t MkvWriter::WriteFromFile(File* source) { } int64_t MkvWriter::WriteFromFile(File* source, int64_t max_copy) { + DCHECK(seekable_); DCHECK(file_); const int64_t size = File::CopyFile(source, file_.get(), max_copy); diff --git a/packager/media/formats/webm/mkv_writer.h b/packager/media/formats/webm/mkv_writer.h index cfed034a83a..9c2280385db 100644 --- a/packager/media/formats/webm/mkv_writer.h +++ b/packager/media/formats/webm/mkv_writer.h @@ -13,6 +13,7 @@ #include "packager/file/file_closer.h" #include "packager/status.h" #include "packager/third_party/libwebm/src/mkvmuxer.hpp" +#include "packager/media/base/buffer_writer.h" namespace shaka { namespace media { @@ -28,6 +29,11 @@ class MkvWriter : public mkvmuxer::IMkvWriter { /// @param name The path to the file to open. /// @return Whether the operation succeeded. Status Open(const std::string& name); + + /// Initialize a buffer. + /// @return Whether the operation succeeded. + Status OpenBuffer(); + /// Closes the file. MUST call Open before calling any other methods. Status Close(); @@ -58,10 +64,17 @@ class MkvWriter : public mkvmuxer::IMkvWriter { /// @return The number of bytes written; or < 0 on error. int64_t WriteFromFile(File* source, int64_t max_copy); + /// Creates a file with name @a file_name and flushes + /// current_buffer_ to it. + /// @param name The path to the file to open. + /// @return File creation and buffer flushing succeeded or failed. + virtual bool CreateFileAndFlushBuffer(const std::string& file_name); + File* file() { return file_.get(); } - + private: std::unique_ptr file_; + std::unique_ptr current_buffer_; // Keep track of the position and whether we can seek. mkvmuxer::int64 position_; bool seekable_; diff --git a/packager/media/formats/webm/multi_segment_segmenter.cc b/packager/media/formats/webm/multi_segment_segmenter.cc index 86df7b6356e..fe004365310 100644 --- a/packager/media/formats/webm/multi_segment_segmenter.cc +++ b/packager/media/formats/webm/multi_segment_segmenter.cc @@ -29,17 +29,20 @@ Status MultiSegmentSegmenter::FinalizeSegment(uint64_t start_timestamp, CHECK(cluster()); RETURN_IF_ERROR(Segmenter::FinalizeSegment( start_timestamp, duration_timestamp, is_subsegment, segment_index)); - if (!cluster()->Finalize()) - return Status(error::FILE_FAILURE, "Error finalizing segment."); - if (!is_subsegment) { - std::string segment_name_segment_index = + std::string segment_name = GetSegmentName(options().segment_template, start_timestamp, segment_index - 1, options().bandwidth); - if (writer_->file()->Rename(segment_name_segment_index) != 1) { - LOG(ERROR) << "Error renaming WEBM segment"; + if (!writer_->CreateFileAndFlushBuffer(segment_name)) { + LOG(ERROR) << "Failed creating file for webm segment."; } + } + + if (!cluster()->Finalize()) + return Status(error::FILE_FAILURE, "Error finalizing segment."); + + if (!is_subsegment) { const std::string segment_name = writer_->file()->file_name(); // Close the file, which also does flushing, to make sure the file is @@ -86,12 +89,9 @@ Status MultiSegmentSegmenter::DoFinalize() { Status MultiSegmentSegmenter::NewSegment(uint64_t start_timestamp, bool is_subsegment) { if (!is_subsegment) { - // Create a new file for the new segment. - std::string segment_name = - GetSegmentName(options().segment_template, start_timestamp, - num_segment_, options().bandwidth); writer_.reset(new MkvWriter); - Status status = writer_->Open(segment_name); + // Initialize the buffer for a new segment. + Status status = writer_->OpenBuffer(); if (!status.ok()) return status; num_segment_++; diff --git a/packager/media/formats/webm/multi_segment_segmenter.h b/packager/media/formats/webm/multi_segment_segmenter.h index c1ddf20d13d..5f86c625f0b 100644 --- a/packager/media/formats/webm/multi_segment_segmenter.h +++ b/packager/media/formats/webm/multi_segment_segmenter.h @@ -8,7 +8,6 @@ #define PACKAGER_MEDIA_FORMATS_WEBM_MULTI_SEGMENT_SEGMENTER_H_ #include - #include "packager/media/formats/webm/mkv_writer.h" #include "packager/media/formats/webm/segmenter.h" #include "packager/status.h" diff --git a/packager/media/formats/webm/multi_segment_segmenter_unittest.cc b/packager/media/formats/webm/multi_segment_segmenter_unittest.cc index 346cb8dd68d..42d37921ff3 100644 --- a/packager/media/formats/webm/multi_segment_segmenter_unittest.cc +++ b/packager/media/formats/webm/multi_segment_segmenter_unittest.cc @@ -20,9 +20,9 @@ const uint64_t kDuration = 1000000u; const bool kSubsegment = true; const uint8_t kBasicSupportDataInit[] = { - // ID: EBML Header omitted. - // ID: Segment, Payload Size: Unknown - 0x18, 0x53, 0x80, 0x67, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + // ID: EBML Header omitted. + // ID: Segment, Payload Size: Unknown + 0x18, 0x53, 0x80, 0x67, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ID: Void, Payload Size: 87 0xec, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -34,44 +34,47 @@ const uint8_t kBasicSupportDataInit[] = { 0x00, 0x00, 0x00, 0x00, 0x00, // ID: Info, Payload Size: 81 0x15, 0x49, 0xa9, 0x66, 0xd1, - // TimecodeScale: 1000000 - 0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40, - // MuxingApp: 'libwebm-0.2.1.0' - 0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30, - 0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30, - // WritingApp: 'https://github.com/google/shaka-packager version test' - 0x57, 0x41, 0xb5, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x73, 0x68, 0x61, 0x6b, 0x61, 0x2d, 0x70, - 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x65, 0x73, 0x74, + // TimecodeScale: 1000000 + 0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40, + // MuxingApp: 'libwebm-0.2.1.0' + 0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30, + 0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30, + // WritingApp: 'https://github.com/google/shaka-packager version test' + 0x57, 0x41, 0xb5, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x73, 0x68, 0x61, 0x6b, 0x61, 0x2d, 0x70, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x74, 0x65, 0x73, 0x74, // ID: Tracks, Payload Size: 41 0x16, 0x54, 0xae, 0x6b, 0xa9, - // ID: Track, Payload Size: 39 - 0xae, 0xa7, - // TrackNumber: 1 - 0xd7, 0x81, 0x01, - // TrackUID: 1 - 0x73, 0xc5, 0x81, 0x01, - // TrackType: 1 - 0x83, 0x81, 0x01, - // CodecID: 'V_VP8' - 0x86, 0x85, 0x56, 0x5f, 0x56, 0x50, 0x38, - // Language: 'en' - 0x22, 0xb5, 0x9c, 0x82, 0x65, 0x6e, - // ID: Video, Payload Size: 14 - 0xe0, 0x8e, - // PixelWidth: 100 - 0xb0, 0x81, 0x64, - // PixelHeight: 100 - 0xba, 0x81, 0x64, - // DisplayWidth: 100 - 0x54, 0xb0, 0x81, 0x64, - // DisplayHeight: 100 - 0x54, 0xba, 0x81, 0x64}; + // ID: Track, Payload Size: 39 + 0xae, 0xa7, + // TrackNumber: 1 + 0xd7, 0x81, 0x01, + // TrackUID: 1 + 0x73, 0xc5, 0x81, 0x01, + // TrackType: 1 + 0x83, 0x81, 0x01, + // CodecID: 'V_VP8' + 0x86, 0x85, 0x56, 0x5f, 0x56, 0x50, 0x38, + // Language: 'en' + 0x22, 0xb5, 0x9c, 0x82, 0x65, 0x6e, + // ID: Video, Payload Size: 14 + 0xe0, 0x8e, + // PixelWidth: 100 + 0xb0, 0x81, 0x64, + // PixelHeight: 100 + 0xba, 0x81, 0x64, + // DisplayWidth: 100 + 0x54, 0xb0, 0x81, 0x64, + // DisplayHeight: 100 + 0x54, 0xba, 0x81, 0x64 +}; + const uint8_t kBasicSupportDataSegment[] = { - // ID: Cluster, Payload Size: 64 - 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + // ID: Cluster, Payload Size: 64 + 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, // Timecode: 0 0xe7, 0x81, 0x00, // ID: SimpleBlock, Payload Size: 9 @@ -84,10 +87,11 @@ const uint8_t kBasicSupportDataSegment[] = { 0xa3, 0x89, 0x81, 0x0b, 0xb8, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, // ID: BlockGroup, Payload Size: 15 0xa0, 0x8f, - // ID: Block, Payload Size: 9 - 0xa1, 0x89, 0x81, 0x0f, 0xa0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x00, - // BlockDuration: 1000 - 0x9b, 0x82, 0x03, 0xe8}; + // ID: Block, Payload Size: 9 + 0xa1, 0x89, 0x81, 0x0f, 0xa0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x00, + // BlockDuration: 1000 + 0x9b, 0x82, 0x03, 0xe8 +}; } // namespace @@ -130,10 +134,10 @@ TEST_F(MultiSegmentSegmenterTest, BasicSupport) { // Verify the resulting data. ASSERT_FILE_ENDS_WITH(OutputFileName().c_str(), kBasicSupportDataInit); - ASSERT_FILE_EQ(TemplateFileName(0).c_str(), kBasicSupportDataSegment); + ASSERT_FILE_EQ(TemplateFileName(7).c_str(), kBasicSupportDataSegment); // There is no second segment. - EXPECT_FALSE(File::Open(TemplateFileName(1).c_str(), "r")); + EXPECT_FALSE(File::Open(TemplateFileName(8).c_str(), "r")); } TEST_F(MultiSegmentSegmenterTest, SplitsFilesOnSegment) { @@ -156,15 +160,15 @@ TEST_F(MultiSegmentSegmenterTest, SplitsFilesOnSegment) { // Verify the resulting data. ClusterParser parser; - ASSERT_NO_FATAL_FAILURE(parser.PopulateFromCluster(TemplateFileName(0))); + ASSERT_NO_FATAL_FAILURE(parser.PopulateFromCluster(TemplateFileName(4))); ASSERT_EQ(1u, parser.cluster_count()); EXPECT_EQ(5u, parser.GetFrameCountForCluster(0)); - ASSERT_NO_FATAL_FAILURE(parser.PopulateFromCluster(TemplateFileName(1))); + ASSERT_NO_FATAL_FAILURE(parser.PopulateFromCluster(TemplateFileName(7))); ASSERT_EQ(1u, parser.cluster_count()); EXPECT_EQ(3u, parser.GetFrameCountForCluster(0)); - EXPECT_FALSE(File::Open(TemplateFileName(2).c_str(), "r")); + EXPECT_FALSE(File::Open(TemplateFileName(9).c_str(), "r")); } TEST_F(MultiSegmentSegmenterTest, SplitsClustersOnSubsegment) { @@ -186,12 +190,12 @@ TEST_F(MultiSegmentSegmenterTest, SplitsClustersOnSubsegment) { // Verify the resulting data. ClusterParser parser; - ASSERT_NO_FATAL_FAILURE(parser.PopulateFromCluster(TemplateFileName(0))); + ASSERT_NO_FATAL_FAILURE(parser.PopulateFromCluster(TemplateFileName(7))); ASSERT_EQ(2u, parser.cluster_count()); EXPECT_EQ(5u, parser.GetFrameCountForCluster(0)); EXPECT_EQ(3u, parser.GetFrameCountForCluster(1)); - EXPECT_FALSE(File::Open(TemplateFileName(1).c_str(), "r")); + EXPECT_FALSE(File::Open(TemplateFileName(8).c_str(), "r")); } } // namespace media diff --git a/packager/media/formats/webm/single_segment_segmenter_unittest.cc b/packager/media/formats/webm/single_segment_segmenter_unittest.cc index c8d48086177..50260b8a18f 100644 --- a/packager/media/formats/webm/single_segment_segmenter_unittest.cc +++ b/packager/media/formats/webm/single_segment_segmenter_unittest.cc @@ -20,414 +20,119 @@ const uint64_t kDuration = 1000000; const bool kSubsegment = true; const uint8_t kBasicSupportData[] = { - // ID: EBML Header omitted. - // ID: Segment, Payload Size: 343 - 0x18, - 0x53, - 0x80, - 0x67, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x01, - 0x57, + // ID: EBML Header omitted. + // ID: Segment, Payload Size: 343 + 0x18, 0x53, 0x80, 0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x57, // ID: SeekHead, Payload Size: 57 - 0x11, - 0x4d, - 0x9b, - 0x74, - 0xb8, - // ID: Seek, Payload Size: 11 - 0x4d, - 0xbb, - 0x8b, - // SeekID: binary(4) (Info) - 0x53, - 0xab, - 0x84, - 0x15, - 0x49, - 0xa9, - 0x66, - // SeekPosition: 89 - 0x53, - 0xac, - 0x81, - 0x59, - // ID: Seek, Payload Size: 11 - 0x4d, - 0xbb, - 0x8b, - // SeekID: binary(4) (Tracks) - 0x53, - 0xab, - 0x84, - 0x16, - 0x54, - 0xae, - 0x6b, - // SeekPosition: 182 - 0x53, - 0xac, - 0x81, - 0xb6, - // ID: Seek, Payload Size: 12 - 0x4d, - 0xbb, - 0x8b, - // SeekID: binary(4) (Cues) - 0x53, - 0xab, - 0x84, - 0x1c, - 0x53, - 0xbb, - 0x6b, - // SeekPosition: 228 - 0x53, - 0xac, - 0x81, - 0xe4, - // ID: Seek, Payload Size: 11 - 0x4d, - 0xbb, - 0x8b, - // SeekID: binary(4) (Cluster) - 0x53, - 0xab, - 0x84, - 0x1f, - 0x43, - 0xb6, - 0x75, - // SeekPosition: 246 - 0x53, - 0xac, - 0x81, - 0xf6, + 0x11, 0x4d, 0x9b, 0x74, 0xb8, + // ID: Seek, Payload Size: 11 + 0x4d, 0xbb, 0x8b, + // SeekID: binary(4) (Info) + 0x53, 0xab, 0x84, 0x15, 0x49, 0xa9, 0x66, + // SeekPosition: 89 + 0x53, 0xac, 0x81, 0x59, + // ID: Seek, Payload Size: 11 + 0x4d, 0xbb, 0x8b, + // SeekID: binary(4) (Tracks) + 0x53, 0xab, 0x84, 0x16, 0x54, 0xae, 0x6b, + // SeekPosition: 182 + 0x53, 0xac, 0x81, 0xb6, + // ID: Seek, Payload Size: 12 + 0x4d, 0xbb, 0x8b, + // SeekID: binary(4) (Cues) + 0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b, + // SeekPosition: 228 + 0x53, 0xac, 0x81, 0xe4, + // ID: Seek, Payload Size: 11 + 0x4d, 0xbb, 0x8b, + // SeekID: binary(4) (Cluster) + 0x53, 0xab, 0x84, 0x1f, 0x43, 0xb6, 0x75, + // SeekPosition: 246 + 0x53, 0xac, 0x81, 0xf6, // ID: Void, Payload Size: 26 - 0xec, - 0x9a, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, + 0xec, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // ID: Info, Payload Size: 88 - 0x15, - 0x49, - 0xa9, - 0x66, - 0xd8, - // TimecodeScale: 1000000 - 0x2a, - 0xd7, - 0xb1, - 0x83, - 0x0f, - 0x42, - 0x40, - // Duration: float(5000) - 0x44, - 0x89, - 0x84, - 0x45, - 0x9c, - 0x40, - 0x00, - // MuxingApp: 'libwebm-0.2.1.0' - 0x4d, - 0x80, - 0x8f, - 0x6c, - 0x69, - 0x62, - 0x77, - 0x65, - 0x62, - 0x6d, - 0x2d, - 0x30, - 0x2e, - 0x32, - 0x2e, - 0x31, - 0x2e, - 0x30, - // WritingApp: 'https://github.com/google/shaka-packager version test' - 0x57, - 0x41, - 0xb5, - 0x68, - 0x74, - 0x74, - 0x70, - 0x73, - 0x3a, - 0x2f, - 0x2f, - 0x67, - 0x69, - 0x74, - 0x68, - 0x75, - 0x62, - 0x2e, - 0x63, - 0x6f, - 0x6d, - 0x2f, - 0x67, - 0x6f, - 0x6f, - 0x67, - 0x6c, - 0x65, - 0x2f, - 0x73, - 0x68, - 0x61, - 0x6b, - 0x61, - 0x2d, - 0x70, - 0x61, - 0x63, - 0x6b, - 0x61, - 0x67, - 0x65, - 0x72, - 0x20, - 0x76, - 0x65, - 0x72, - 0x73, - 0x69, - 0x6f, - 0x6e, - 0x20, - 0x74, - 0x65, - 0x73, - 0x74, + 0x15, 0x49, 0xa9, 0x66, 0xd8, + // TimecodeScale: 1000000 + 0x2a, 0xd7, 0xb1, 0x83, 0x0f, 0x42, 0x40, + // Duration: float(5000) + 0x44, 0x89, 0x84, 0x45, 0x9c, 0x40, 0x00, + // MuxingApp: 'libwebm-0.2.1.0' + 0x4d, 0x80, 0x8f, 0x6c, 0x69, 0x62, 0x77, 0x65, 0x62, 0x6d, 0x2d, 0x30, + 0x2e, 0x32, 0x2e, 0x31, 0x2e, 0x30, + // WritingApp: 'https://github.com/google/shaka-packager version test' + 0x57, 0x41, 0xb5, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x73, 0x68, 0x61, 0x6b, 0x61, 0x2d, 0x70, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x74, 0x65, 0x73, 0x74, // ID: Tracks, Payload Size: 41 - 0x16, - 0x54, - 0xae, - 0x6b, - 0xa9, - // ID: Track, Payload Size: 39 - 0xae, - 0xa7, - // TrackNumber: 1 - 0xd7, - 0x81, - 0x01, - // TrackUID: 1 - 0x73, - 0xc5, - 0x81, - 0x01, - // TrackType: 1 - 0x83, - 0x81, - 0x01, - // CodecID: 'V_VP8' - 0x86, - 0x85, - 0x56, - 0x5f, - 0x56, - 0x50, - 0x38, - // Language: 'en' - 0x22, - 0xb5, - 0x9c, - 0x82, - 0x65, - 0x6e, - // ID: Video, Payload Size: 14 - 0xe0, - 0x8e, - // PixelWidth: 100 - 0xb0, - 0x81, - 0x64, - // PixelHeight: 100 - 0xba, - 0x81, - 0x64, - // DisplayWidth: 100 - 0x54, - 0xb0, - 0x81, - 0x64, - // DisplayHeight: 100 - 0x54, - 0xba, - 0x81, - 0x64, + 0x16, 0x54, 0xae, 0x6b, 0xa9, + // ID: Track, Payload Size: 39 + 0xae, 0xa7, + // TrackNumber: 1 + 0xd7, 0x81, 0x01, + // TrackUID: 1 + 0x73, 0xc5, 0x81, 0x01, + // TrackType: 1 + 0x83, 0x81, 0x01, + // CodecID: 'V_VP8' + 0x86, 0x85, 0x56, 0x5f, 0x56, 0x50, 0x38, + // Language: 'en' + 0x22, 0xb5, 0x9c, 0x82, 0x65, 0x6e, + // ID: Video, Payload Size: 14 + 0xe0, 0x8e, + // PixelWidth: 100 + 0xb0, 0x81, 0x64, + // PixelHeight: 100 + 0xba, 0x81, 0x64, + // DisplayWidth: 100 + 0x54, 0xb0, 0x81, 0x64, + // DisplayHeight: 100 + 0x54, 0xba, 0x81, 0x64, // ID: Cues, Payload Size: 13 - 0x1c, - 0x53, - 0xbb, - 0x6b, - 0x8d, - // ID: CuePoint, Payload Size: 11 - 0xbb, - 0x8b, - // CueTime: 0 - 0xb3, - 0x81, - 0x00, - // ID: CueTrackPositions, Payload Size: 6 - 0xb7, - 0x86, - // CueTrack: 1 - 0xf7, - 0x81, - 0x01, - // CueClusterPosition: 246 - 0xf1, - 0x81, - 0xf6, + 0x1c, 0x53, 0xbb, 0x6b, 0x8d, + // ID: CuePoint, Payload Size: 11 + 0xbb, 0x8b, + // CueTime: 0 + 0xb3, 0x81, 0x00, + // ID: CueTrackPositions, Payload Size: 6 + 0xb7, 0x86, + // CueTrack: 1 + 0xf7, 0x81, 0x01, + // CueClusterPosition: 246 + 0xf1, 0x81, 0xf6, // ID: Cluster, Payload Size: 85 - 0x1f, - 0x43, - 0xb6, - 0x75, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x55, - // Timecode: 0 - 0xe7, - 0x81, - 0x00, - // ID: SimpleBlock, Payload Size: 9 - 0xa3, - 0x89, - 0x81, - 0x00, - 0x00, - 0x80, - 0xde, - 0xad, - 0xbe, - 0xef, - 0x00, - // ID: SimpleBlock, Payload Size: 9 - 0xa3, - 0x89, - 0x81, - 0x03, - 0xe8, - 0x80, - 0xde, - 0xad, - 0xbe, - 0xef, - 0x00, - // ID: SimpleBlock, Payload Size: 9 - 0xa3, - 0x89, - 0x81, - 0x07, - 0xd0, - 0x80, - 0xde, - 0xad, - 0xbe, - 0xef, - 0x00, - // ID: BlockGroup, Payload Size: 30 - 0xa0, - 0x9e, - // ID: Block, Payload Size: 9 - 0xa1, - 0x89, - 0x81, - 0x0b, - 0xb8, - 0x00, - 0xde, - 0xad, - 0xbe, - 0xef, - 0x00, - // ID: BlockAdditions, Payload Size: 16 - 0x75, - 0xa1, - 0x90, - // ID: BlockMore, Payload Size: 14 - 0xa6, - 0x8e, - // ID: BlockAddID, Payload Size: 1 - 0xee, - 0x85, - 0x9a, - 0x78, - 0x56, - 0x34, - 0x12, - // ID: BlockAdditional, Payload Size: 5 - 0xa5, - 0x85, - 0x73, - 0x69, - 0x64, - 0x65, - 0x00, - // ID: BlockGroup, Payload Size: 15 - 0xa0, - 0x8f, - // ID: Block, Payload Size: 9 - 0xa1, - 0x89, - 0x81, - 0x0f, - 0xa0, - 0x00, - 0xde, - 0xad, - 0xbe, - 0xef, - 0x00, - // BlockDuration: 1000 - 0x9b, - 0x82, - 0x03, - 0xe8, + 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, + // Timecode: 0 + 0xe7, 0x81, 0x00, + // ID: SimpleBlock, Payload Size: 9 + 0xa3, 0x89, 0x81, 0x00, 0x00, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, + // ID: SimpleBlock, Payload Size: 9 + 0xa3, 0x89, 0x81, 0x03, 0xe8, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, + // ID: SimpleBlock, Payload Size: 9 + 0xa3, 0x89, 0x81, 0x07, 0xd0, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, + // ID: BlockGroup, Payload Size: 30 + 0xa0, 0x9e, + // ID: Block, Payload Size: 9 + 0xa1, 0x89, 0x81, 0x0b, 0xb8, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x00, + // ID: BlockAdditions, Payload Size: 16 + 0x75, 0xa1, 0x90, + // ID: BlockMore, Payload Size: 14 + 0xa6, 0x8e, + // ID: BlockAddID, Payload Size: 1 + 0xee, 0x85, 0x9a, 0x78, 0x56, 0x34, 0x12, + // ID: BlockAdditional, Payload Size: 5 + 0xa5, 0x85, 0x73, 0x69, 0x64, 0x65, 0x00, + // ID: BlockGroup, Payload Size: 15 + 0xa0, 0x8f, + // ID: Block, Payload Size: 9 + 0xa1, 0x89, 0x81, 0x0f, 0xa0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x00, + // BlockDuration: 1000 + 0x9b, 0x82, 0x03, 0xe8, }; } // namespace diff --git a/packager/mpd/base/mpd_notifier.h b/packager/mpd/base/mpd_notifier.h index db3701a7477..ad954056034 100644 --- a/packager/mpd/base/mpd_notifier.h +++ b/packager/mpd/base/mpd_notifier.h @@ -11,7 +11,6 @@ #define MPD_BASE_MPD_NOTIFIER_H_ #include - #include #include From f522c01ed8be57c7aabc69a7172f977dcc13bfdf Mon Sep 17 00:00:00 2001 From: sr90 Date: Thu, 11 Jun 2020 23:34:08 -0700 Subject: [PATCH 09/10] Changing unit tests as per previous changes. --- .../media/formats/mp2t/ts_segmenter_unittest.cc | 1 + packager/media/formats/mp2t/ts_writer_unittest.cc | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/packager/media/formats/mp2t/ts_segmenter_unittest.cc b/packager/media/formats/mp2t/ts_segmenter_unittest.cc index bc5b436bc62..34e1051efc9 100644 --- a/packager/media/formats/mp2t/ts_segmenter_unittest.cc +++ b/packager/media/formats/mp2t/ts_segmenter_unittest.cc @@ -81,6 +81,7 @@ class MockTsWriter : public TsWriter { // Create a bogus pmt writer, which we don't really care. new VideoProgramMapTableWriter(kUnknownCodec))) {} + MOCK_METHOD1(CreateFileAndFlushBuffer, bool(const std::string& file_name)); MOCK_METHOD0(NewSegment, bool()); MOCK_METHOD0(SignalEncrypted, void()); MOCK_METHOD0(FinalizeSegment, bool()); diff --git a/packager/media/formats/mp2t/ts_writer_unittest.cc b/packager/media/formats/mp2t/ts_writer_unittest.cc index 15c4b5e2308..97be645e3f5 100644 --- a/packager/media/formats/mp2t/ts_writer_unittest.cc +++ b/packager/media/formats/mp2t/ts_writer_unittest.cc @@ -145,6 +145,8 @@ TEST_F(TsWriterTest, ClearH264Psi) { TsWriter ts_writer(std::move(mock_pmt_writer)); EXPECT_TRUE(ts_writer.NewSegment()); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); + ASSERT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -193,6 +195,7 @@ TEST_F(TsWriterTest, ClearAacPmt) { TsWriter ts_writer(std::move(mock_pmt_writer)); EXPECT_TRUE(ts_writer.NewSegment()); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); ASSERT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -214,6 +217,7 @@ TEST_F(TsWriterTest, ClearLeadH264Pmt) { TsWriter ts_writer(std::move(mock_pmt_writer)); EXPECT_TRUE(ts_writer.NewSegment()); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); EXPECT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -246,11 +250,13 @@ TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) { TsWriter ts_writer(std::move(mock_pmt_writer)); EXPECT_TRUE(ts_writer.NewSegment()); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); EXPECT_TRUE(ts_writer.FinalizeSegment()); // Overwrite the file but as encrypted segment. ts_writer.SignalEncrypted(); EXPECT_TRUE(ts_writer.NewSegment()); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); EXPECT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -271,6 +277,7 @@ TEST_F(TsWriterTest, EncryptedSegmentPmtFailure) { TsWriter ts_writer(std::move(mock_pmt_writer)); EXPECT_TRUE(ts_writer.NewSegment()); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); EXPECT_TRUE(ts_writer.FinalizeSegment()); ts_writer.SignalEncrypted(); @@ -286,6 +293,7 @@ TEST_F(TsWriterTest, ClearLeadAacPmt) { TsWriter ts_writer(std::move(mock_pmt_writer)); EXPECT_TRUE(ts_writer.NewSegment()); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); ASSERT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -309,11 +317,13 @@ TEST_F(TsWriterTest, EncryptedSegmentsAacPmt) { TsWriter ts_writer(std::move(mock_pmt_writer)); EXPECT_TRUE(ts_writer.NewSegment()); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); EXPECT_TRUE(ts_writer.FinalizeSegment()); // Overwrite the file but as encrypted segment. ts_writer.SignalEncrypted(); EXPECT_TRUE(ts_writer.NewSegment()); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); EXPECT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -341,6 +351,7 @@ TEST_F(TsWriterTest, AddPesPacket) { pes->mutable_data()->assign(kAnyData, kAnyData + arraysize(kAnyData)); EXPECT_TRUE(ts_writer.AddPesPacket(std::move(pes))); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); ASSERT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -401,6 +412,7 @@ TEST_F(TsWriterTest, BigPesPacket) { *pes->mutable_data() = big_data; EXPECT_TRUE(ts_writer.AddPesPacket(std::move(pes))); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); ASSERT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -435,6 +447,7 @@ TEST_F(TsWriterTest, PesPtsZeroNoDts) { pes->mutable_data()->assign(kAnyData, kAnyData + arraysize(kAnyData)); EXPECT_TRUE(ts_writer.AddPesPacket(std::move(pes))); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); ASSERT_TRUE(ts_writer.FinalizeSegment()); std::vector content; @@ -496,6 +509,7 @@ TEST_F(TsWriterTest, TsPacketPayload183Bytes) { *pes->mutable_data() = pes_payload; EXPECT_TRUE(ts_writer.AddPesPacket(std::move(pes))); + EXPECT_TRUE(ts_writer.CreateFileAndFlushBuffer(test_file_name_)); ASSERT_TRUE(ts_writer.FinalizeSegment()); const uint8_t kExpectedOutputPrefix[] = { From d5d376cb0a1157dad7d6cfcb3f1698b4f3464620 Mon Sep 17 00:00:00 2001 From: sr90 Date: Fri, 12 Jun 2020 16:12:55 -0700 Subject: [PATCH 10/10] Reverting back a few style changes for better review. --- packager/file/threaded_io_file.h | 1 - packager/file/udp_file.cc | 14 +++++++++----- .../media/formats/mp2t/ts_segmenter_unittest.cc | 6 ++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packager/file/threaded_io_file.h b/packager/file/threaded_io_file.h index 7e228dd1bff..dacfe8c43d1 100644 --- a/packager/file/threaded_io_file.h +++ b/packager/file/threaded_io_file.h @@ -9,7 +9,6 @@ #include #include - #include "packager/base/synchronization/waitable_event.h" #include "packager/file/file.h" #include "packager/file/file_closer.h" diff --git a/packager/file/udp_file.cc b/packager/file/udp_file.cc index 64c4c6458a1..d7fb03505c0 100644 --- a/packager/file/udp_file.cc +++ b/packager/file/udp_file.cc @@ -27,7 +27,7 @@ // IP_MULTICAST_ALL has been supported since kernel version 2.6.31 but we may be // building on a machine that is older than that. #ifndef IP_MULTICAST_ALL -#define IP_MULTICAST_ALL 49 +#define IP_MULTICAST_ALL 49 #endif #endif // defined(OS_WIN) @@ -205,20 +205,24 @@ bool UdpFile::Open() { struct ip_mreq_source source_multicast_group; source_multicast_group.imr_multiaddr = local_in_addr; - if (inet_pton(AF_INET, options->interface_address().c_str(), + if (inet_pton(AF_INET, + options->interface_address().c_str(), &source_multicast_group.imr_interface) != 1) { LOG(ERROR) << "Malformed IPv4 interface address " << options->interface_address(); return false; } - if (inet_pton(AF_INET, options->source_address().c_str(), + if (inet_pton(AF_INET, + options->source_address().c_str(), &source_multicast_group.imr_sourceaddr) != 1) { LOG(ERROR) << "Malformed IPv4 source specific multicast address " << options->source_address(); return false; } - if (setsockopt(new_socket.get(), IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, + if (setsockopt(new_socket.get(), + IPPROTO_IP, + IP_ADD_SOURCE_MEMBERSHIP, reinterpret_cast(&source_multicast_group), sizeof(source_multicast_group)) < 0) { LOG(ERROR) << "Failed to join multicast group, error = " @@ -245,7 +249,7 @@ bool UdpFile::Open() { << GetSocketErrorCode(); return false; } - } + } #if defined(__linux__) // Disable IP_MULTICAST_ALL to avoid interference caused when two sockets diff --git a/packager/media/formats/mp2t/ts_segmenter_unittest.cc b/packager/media/formats/mp2t/ts_segmenter_unittest.cc index 34e1051efc9..4fbd392e744 100644 --- a/packager/media/formats/mp2t/ts_segmenter_unittest.cc +++ b/packager/media/formats/mp2t/ts_segmenter_unittest.cc @@ -50,7 +50,7 @@ const uint8_t kNaluLengthSize = 1; const bool kIsEncrypted = false; const uint8_t kAnyData[] = { - 0x01, 0x0F, 0x3C, + 0x01, 0x0F, 0x3C, }; class MockPesPacketGenerator : public PesPacketGenerator { @@ -349,9 +349,7 @@ TEST_F(TsSegmenterTest, EncryptedSample) { ON_CALL(*mock_pes_packet_generator_, Flush()).WillByDefault(Return(true)); const uint8_t kAnyData[] = { - 0x01, - 0x0F, - 0x3C, + 0x01, 0x0F, 0x3C, }; std::shared_ptr sample1 = MediaSample::CopyFrom(kAnyData, arraysize(kAnyData), kIsKeyFrame);