Add logic to correctly set subSegmentAlignment for VOD
- subSegmentAlignment logic assumed that all Representations were added before calling Representations::AddNewSegment() on any Representation in an AdaptationSet. It is not a valid assumption for VOD and is removed. Change-Id: Ifb7e34ee103ee30027e45a804427baf281f3137c
This commit is contained in:
parent
e5b6096857
commit
bb8cf87617
|
@ -10,6 +10,7 @@
|
|||
#include <libxml/xmlstring.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
|
@ -732,6 +733,11 @@ xml::ScopedXmlPtr<xmlNode>::type AdaptationSet::GetXml() {
|
|||
video_frame_rates_.rbegin()->second);
|
||||
}
|
||||
|
||||
// Note: must be checked before checking segments_aligned_ (below).
|
||||
if (mpd_type_ == MpdBuilder::kStatic) {
|
||||
CheckVodSegmentAlignment();
|
||||
}
|
||||
|
||||
if (segments_aligned_ == kSegmentAlignmentTrue) {
|
||||
adaptation_set.SetStringAttribute(mpd_type_ == MpdBuilder::kStatic
|
||||
? "subSegmentAlignment"
|
||||
|
@ -767,11 +773,24 @@ int AdaptationSet::Group() const {
|
|||
return group_;
|
||||
}
|
||||
|
||||
// Check segmentAlignment for Live here. Storing all start_time and duration
|
||||
// will out-of-memory because there's no way of knowing when it will end.
|
||||
// VOD subSegmentAlignment check is *not* done here because it is possible
|
||||
// that some Representations might not have been added yet (e.g. a thread is
|
||||
// assigned per muxer so one might run faster than others).
|
||||
// To be clear, for Live, all Representations should be added before a
|
||||
// segment is added.
|
||||
void AdaptationSet::OnNewSegmentForRepresentation(uint32_t representation_id,
|
||||
uint64_t start_time,
|
||||
uint64_t duration) {
|
||||
base::AutoLock scoped_lock(lock_);
|
||||
CheckSegmentAlignment(representation_id, start_time, duration);
|
||||
|
||||
if (mpd_type_ == MpdBuilder::kDynamic) {
|
||||
CheckLiveSegmentAlignment(representation_id, start_time, duration);
|
||||
} else {
|
||||
representation_segment_start_times_[representation_id].push_back(
|
||||
start_time);
|
||||
}
|
||||
}
|
||||
|
||||
void AdaptationSet::OnSetFrameRateForRepresentation(
|
||||
|
@ -827,12 +846,9 @@ bool AdaptationSet::GetEarliestTimestamp(double* timestamp_seconds) {
|
|||
// They are not aligned but this will be marked as aligned.
|
||||
// But since this is unlikely to happen in the packager (and to save
|
||||
// computation), this isn't handled at the moment.
|
||||
// TODO(rkuroiwa): For VOD, not all Representations get added to an
|
||||
// AdaptationSet before this is called. Add a similar but separate method that
|
||||
// keeps the timestamps around. It shouldn't out-of-memory for VOD.
|
||||
void AdaptationSet::CheckSegmentAlignment(uint32_t representation_id,
|
||||
uint64_t start_time,
|
||||
uint64_t /* duration */) {
|
||||
void AdaptationSet::CheckLiveSegmentAlignment(uint32_t representation_id,
|
||||
uint64_t start_time,
|
||||
uint64_t /* duration */) {
|
||||
if (segments_aligned_ == kSegmentAlignmentFalse ||
|
||||
force_set_segment_alignment_) {
|
||||
return;
|
||||
|
@ -874,6 +890,66 @@ void AdaptationSet::CheckSegmentAlignment(uint32_t representation_id,
|
|||
}
|
||||
}
|
||||
|
||||
// Make sure all segements start times match for all Representations.
|
||||
// This assumes that the segments are contiguous.
|
||||
void AdaptationSet::CheckVodSegmentAlignment() {
|
||||
if (segments_aligned_ == kSegmentAlignmentFalse ||
|
||||
force_set_segment_alignment_) {
|
||||
return;
|
||||
}
|
||||
if (representation_segment_start_times_.empty())
|
||||
return;
|
||||
if (representation_segment_start_times_.size() == 1) {
|
||||
segments_aligned_ = kSegmentAlignmentTrue;
|
||||
return;
|
||||
}
|
||||
|
||||
// This is not the most efficient implementation to compare the values
|
||||
// because expected_time_line is compared against all other time lines, but
|
||||
// probably the most readable.
|
||||
const std::list<uint64_t>& expected_time_line =
|
||||
representation_segment_start_times_.begin()->second;
|
||||
|
||||
bool all_segment_time_line_same_length = true;
|
||||
// Note that the first entry is skipped because it is expected_time_line.
|
||||
RepresentationTimeline::const_iterator it =
|
||||
representation_segment_start_times_.begin();
|
||||
for (++it; it != representation_segment_start_times_.end(); ++it) {
|
||||
const std::list<uint64_t>& other_time_line = it->second;
|
||||
if (expected_time_line.size() != other_time_line.size()) {
|
||||
all_segment_time_line_same_length = false;
|
||||
}
|
||||
|
||||
const std::list<uint64_t>* longer_list = &other_time_line;
|
||||
const std::list<uint64_t>* shorter_list = &expected_time_line;
|
||||
if (expected_time_line.size() > other_time_line.size()) {
|
||||
shorter_list = &other_time_line;
|
||||
longer_list = &expected_time_line;
|
||||
}
|
||||
|
||||
if (!std::equal(shorter_list->begin(), shorter_list->end(),
|
||||
longer_list->begin())) {
|
||||
// Some segments are definitely unaligned.
|
||||
segments_aligned_ = kSegmentAlignmentFalse;
|
||||
representation_segment_start_times_.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rkuroiwa): The right way to do this is to also check the durations.
|
||||
// For example:
|
||||
// (a) 3 4 5
|
||||
// (b) 3 4 5 6
|
||||
// could be true or false depending on the length of the third segment of (a).
|
||||
// i.e. if length of the third segment is 2, then this is not aligned.
|
||||
if (!all_segment_time_line_same_length) {
|
||||
segments_aligned_ = kSegmentAlignmentUnknown;
|
||||
return;
|
||||
}
|
||||
|
||||
segments_aligned_ = kSegmentAlignmentTrue;
|
||||
}
|
||||
|
||||
// Since all AdaptationSet cares about is the maxFrameRate, representation_id
|
||||
// is not passed to this method.
|
||||
void AdaptationSet::RecordFrameRate(uint32_t frame_duration,
|
||||
|
|
|
@ -312,13 +312,18 @@ class AdaptationSet {
|
|||
|
||||
/// Called from OnNewSegmentForRepresentation(). Checks whether the segments
|
||||
/// are aligned. Sets segments_aligned_.
|
||||
/// This is only for Live. For VOD, CheckVodSegmentAlignment() should be used.
|
||||
/// @param representation_id is the id of the Representation with a new
|
||||
/// segment.
|
||||
/// @param start_time is the start time of the new segment.
|
||||
/// @param duration is the duration of the new segment.
|
||||
void CheckSegmentAlignment(uint32_t representation_id,
|
||||
uint64_t start_time,
|
||||
uint64_t duration);
|
||||
void CheckLiveSegmentAlignment(uint32_t representation_id,
|
||||
uint64_t start_time,
|
||||
uint64_t duration);
|
||||
|
||||
// Checks representation_segment_start_times_ and sets segments_aligned_.
|
||||
// Use this for VOD, do not use for Live.
|
||||
void CheckVodSegmentAlignment();
|
||||
|
||||
// Records the framerate of a Representation.
|
||||
void RecordFrameRate(uint32_t frame_duration, uint32_t timescale);
|
||||
|
@ -377,6 +382,12 @@ class AdaptationSet {
|
|||
bool force_set_segment_alignment_;
|
||||
|
||||
// Keeps track of segment start times of Representations.
|
||||
// For VOD, this will not be cleared, all the segment start times are
|
||||
// stored in this. This should not out-of-memory for a reasonable length
|
||||
// video and reasonable subsegment length.
|
||||
// For Live, the entries are deleted (see CheckLiveSegmentAlignment()
|
||||
// implementation comment) because storing the entire timeline is not
|
||||
// reasonable and may cause an out-of-memory problem.
|
||||
RepresentationTimeline representation_segment_start_times_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AdaptationSet);
|
||||
|
|
|
@ -947,6 +947,8 @@ TEST_F(CommonMpdBuilderTest,
|
|||
|
||||
// Verify that subSegmentAlignment is set to true if all the Representations'
|
||||
// segments are aligned and the MPD type is static.
|
||||
// Also checking that not all Representations have to be added before calling
|
||||
// AddNewSegment() on a Representation.
|
||||
TEST_F(StaticMpdBuilderTest, SubSegmentAlignment) {
|
||||
base::AtomicSequenceNumber sequence_counter;
|
||||
const char k480pMediaInfo[] =
|
||||
|
@ -971,37 +973,36 @@ TEST_F(StaticMpdBuilderTest, SubSegmentAlignment) {
|
|||
" pixel_height: 1\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
// First use same start time and duration, and verify that subSegmentAlignment
|
||||
// is set to true.
|
||||
const uint64_t kStartTime = 0u;
|
||||
const uint64_t kDuration = 10u;
|
||||
const uint64_t kAnySize = 19834u;
|
||||
|
||||
AdaptationSet adaptation_set(kAnyAdaptationSetId, "", MpdOptions(),
|
||||
MpdBuilder::kStatic, &sequence_counter);
|
||||
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* representation_360p =
|
||||
adaptation_set.AddRepresentation(ConvertToMediaInfo(k360pMediaInfo));
|
||||
|
||||
// First use same start time and duration, and verify that that
|
||||
// segmentAlignment is set.
|
||||
const uint64_t kStartTime = 0u;
|
||||
const uint64_t kDuration = 10u;
|
||||
const uint64_t kAnySize = 19834u;
|
||||
representation_480p->AddNewSegment(kStartTime, kDuration, kAnySize);
|
||||
representation_360p->AddNewSegment(kStartTime, kDuration, kAnySize);
|
||||
|
||||
xml::ScopedXmlPtr<xmlNode>::type aligned(adaptation_set.GetXml());
|
||||
EXPECT_NO_FATAL_FAILURE(
|
||||
ExpectAttributeEqString("subSegmentAlignment", "true", aligned.get()));
|
||||
EXPECT_EQ(2u, adaptation_set.representation_segment_start_times_.size());
|
||||
|
||||
// Also check that the start times are removed from the vector.
|
||||
EXPECT_EQ(0u,
|
||||
adaptation_set
|
||||
.representation_segment_start_times_[representation_480p->id()]
|
||||
.size());
|
||||
EXPECT_EQ(0u,
|
||||
adaptation_set
|
||||
.representation_segment_start_times_[representation_360p->id()]
|
||||
.size());
|
||||
// Unknown because 480p has an extra subsegments.
|
||||
representation_480p->AddNewSegment(11, 20, kAnySize);
|
||||
xml::ScopedXmlPtr<xmlNode>::type alignment_unknown(adaptation_set.GetXml());
|
||||
EXPECT_NO_FATAL_FAILURE(
|
||||
ExpectAttributeNotSet("subSegmentAlignment", alignment_unknown.get()));
|
||||
|
||||
// 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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue