Calculate (sub)SegmentAlignment for AdaptationSets
- AdaptationSet::OnNewSegmentForRepresentation() calculates whether the Representations are aligned by looking at the start time and duration. (Except that duration is not used in this implementation). - Add RepresentationStateChangeListener to callback AdaptationSet::OnNewSegmentForRepresentation() from Representation. Change-Id: I3c30bd6652880dabb9d5c619d8a733ffc789eec9
This commit is contained in:
parent
8d84ebfed7
commit
fe851f692b
|
@ -329,16 +329,39 @@ class LibXmlInitializer {
|
||||||
DISALLOW_COPY_AND_ASSIGN(LibXmlInitializer);
|
DISALLOW_COPY_AND_ASSIGN(LibXmlInitializer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RepresentationStateChangeListenerImpl
|
||||||
|
: public RepresentationStateChangeListener {
|
||||||
|
public:
|
||||||
|
// |adaptation_set| is not owned by this class.
|
||||||
|
RepresentationStateChangeListenerImpl(uint32_t representation_id,
|
||||||
|
AdaptationSet* adaptation_set)
|
||||||
|
: representation_id_(representation_id), adaptation_set_(adaptation_set) {
|
||||||
|
DCHECK(adaptation_set_);
|
||||||
|
}
|
||||||
|
virtual ~RepresentationStateChangeListenerImpl() OVERRIDE {}
|
||||||
|
|
||||||
|
// RepresentationStateChangeListener implementation.
|
||||||
|
virtual void OnNewSegmentForRepresentation(uint64_t start_time,
|
||||||
|
uint64_t duration) OVERRIDE {
|
||||||
|
adaptation_set_->OnNewSegmentForRepresentation(representation_id_,
|
||||||
|
start_time, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const uint32_t representation_id_;
|
||||||
|
AdaptationSet* const adaptation_set_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(RepresentationStateChangeListenerImpl);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
MpdBuilder::MpdBuilder(MpdType type, const MpdOptions& mpd_options)
|
MpdBuilder::MpdBuilder(MpdType type, const MpdOptions& mpd_options)
|
||||||
: type_(type),
|
: type_(type),
|
||||||
mpd_options_(mpd_options),
|
mpd_options_(mpd_options),
|
||||||
adaptation_sets_deleter_(&adaptation_sets_) {
|
adaptation_sets_deleter_(&adaptation_sets_) {}
|
||||||
}
|
|
||||||
|
|
||||||
MpdBuilder::~MpdBuilder() {
|
MpdBuilder::~MpdBuilder() {}
|
||||||
}
|
|
||||||
|
|
||||||
void MpdBuilder::AddBaseUrl(const std::string& base_url) {
|
void MpdBuilder::AddBaseUrl(const std::string& base_url) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
|
@ -349,6 +372,7 @@ AdaptationSet* MpdBuilder::AddAdaptationSet(const std::string& lang) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
scoped_ptr<AdaptationSet> adaptation_set(
|
scoped_ptr<AdaptationSet> adaptation_set(
|
||||||
new AdaptationSet(adaptation_set_counter_.GetNext(), lang, mpd_options_,
|
new AdaptationSet(adaptation_set_counter_.GetNext(), lang, mpd_options_,
|
||||||
|
type_,
|
||||||
&representation_counter_));
|
&representation_counter_));
|
||||||
|
|
||||||
DCHECK(adaptation_set);
|
DCHECK(adaptation_set);
|
||||||
|
@ -592,30 +616,38 @@ void MpdBuilder::MakePathsRelativeToMpd(const std::string& mpd_path,
|
||||||
AdaptationSet::AdaptationSet(uint32_t adaptation_set_id,
|
AdaptationSet::AdaptationSet(uint32_t adaptation_set_id,
|
||||||
const std::string& lang,
|
const std::string& lang,
|
||||||
const MpdOptions& mpd_options,
|
const MpdOptions& mpd_options,
|
||||||
|
MpdBuilder::MpdType mpd_type,
|
||||||
base::AtomicSequenceNumber* counter)
|
base::AtomicSequenceNumber* counter)
|
||||||
: representations_deleter_(&representations_),
|
: representations_deleter_(&representations_),
|
||||||
representation_counter_(counter),
|
representation_counter_(counter),
|
||||||
id_(adaptation_set_id),
|
id_(adaptation_set_id),
|
||||||
lang_(lang),
|
lang_(lang),
|
||||||
mpd_options_(mpd_options),
|
mpd_options_(mpd_options),
|
||||||
group_(kAdaptationSetGroupNotSet) {
|
mpd_type_(mpd_type),
|
||||||
|
group_(kAdaptationSetGroupNotSet),
|
||||||
|
segments_aligned_(kSegmentAlignmentUnknown),
|
||||||
|
force_set_segment_alignment_(false) {
|
||||||
DCHECK(counter);
|
DCHECK(counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
AdaptationSet::~AdaptationSet() {
|
AdaptationSet::~AdaptationSet() {}
|
||||||
}
|
|
||||||
|
|
||||||
Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) {
|
Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
|
const uint32_t representation_id = representation_counter_->GetNext();
|
||||||
|
// Note that AdaptationSet outlive Representation, so this object
|
||||||
|
// will die before AdaptationSet.
|
||||||
|
scoped_ptr<RepresentationStateChangeListener> listener(
|
||||||
|
new RepresentationStateChangeListenerImpl(representation_id, this));
|
||||||
scoped_ptr<Representation> representation(new Representation(
|
scoped_ptr<Representation> representation(new Representation(
|
||||||
media_info, mpd_options_, representation_counter_->GetNext()));
|
media_info, mpd_options_, representation_id, listener.Pass()));
|
||||||
|
|
||||||
if (!representation->Init())
|
if (!representation->Init())
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// For videos, record the width, height, and the frame rate to calculate the
|
// For videos, record the width, height, and the frame rate to calculate the
|
||||||
// max {width,height,framerate} required for DASH IOP.
|
// max {width,height,framerate} required for DASH IOP.
|
||||||
if(media_info.has_video_info()) {
|
if (media_info.has_video_info()) {
|
||||||
const MediaInfo::VideoInfo& video_info = media_info.video_info();
|
const MediaInfo::VideoInfo& video_info = media_info.video_info();
|
||||||
DCHECK(video_info.has_width());
|
DCHECK(video_info.has_width());
|
||||||
DCHECK(video_info.has_height());
|
DCHECK(video_info.has_height());
|
||||||
|
@ -699,6 +731,13 @@ xml::ScopedXmlPtr<xmlNode>::type AdaptationSet::GetXml() {
|
||||||
video_frame_rates_.rbegin()->second);
|
video_frame_rates_.rbegin()->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (segments_aligned_ == kSegmentAlignmentTrue) {
|
||||||
|
adaptation_set.SetStringAttribute(mpd_type_ == MpdBuilder::kStatic
|
||||||
|
? "subSegmentAlignment"
|
||||||
|
: "segmentAlignment",
|
||||||
|
"true");
|
||||||
|
}
|
||||||
|
|
||||||
if (picture_aspect_ratio_.size() == 1)
|
if (picture_aspect_ratio_.size() == 1)
|
||||||
adaptation_set.SetStringAttribute("par", *picture_aspect_ratio_.begin());
|
adaptation_set.SetStringAttribute("par", *picture_aspect_ratio_.begin());
|
||||||
|
|
||||||
|
@ -713,6 +752,19 @@ xml::ScopedXmlPtr<xmlNode>::type AdaptationSet::GetXml() {
|
||||||
return adaptation_set.PassScopedPtr();
|
return adaptation_set.PassScopedPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdaptationSet::ForceSetSegmentAlignment(bool segment_alignment) {
|
||||||
|
segments_aligned_ =
|
||||||
|
segment_alignment ? kSegmentAlignmentTrue : kSegmentAlignmentFalse;
|
||||||
|
force_set_segment_alignment_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
bool AdaptationSet::GetEarliestTimestamp(double* timestamp_seconds) {
|
bool AdaptationSet::GetEarliestTimestamp(double* timestamp_seconds) {
|
||||||
DCHECK(timestamp_seconds);
|
DCHECK(timestamp_seconds);
|
||||||
|
|
||||||
|
@ -734,18 +786,90 @@ bool AdaptationSet::GetEarliestTimestamp(double* timestamp_seconds) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Representation::Representation(const MediaInfo& media_info,
|
// This implementation assumes that each representations' segments' are
|
||||||
|
// contiguous.
|
||||||
|
// Also assumes that all Representations are added before this is called.
|
||||||
|
// This checks whether the first elements of the lists in
|
||||||
|
// representation_segment_start_times_ are aligned.
|
||||||
|
// For example, suppose this method was just called with args rep_id=2
|
||||||
|
// start_time=1.
|
||||||
|
// 1 -> [1, 100, 200]
|
||||||
|
// 2 -> [1]
|
||||||
|
// The timestamps of the first elements match, so this flags
|
||||||
|
// segments_aligned_=true.
|
||||||
|
// Also since the first segment start times match, the first element of all the
|
||||||
|
// lists are removed, so the map of lists becomes:
|
||||||
|
// 1 -> [100, 200]
|
||||||
|
// 2 -> []
|
||||||
|
// Note that there could be false positives.
|
||||||
|
// e.g. just got rep_id=3 start_time=1 duration=300, and the duration of the
|
||||||
|
// whole AdaptationSet is 300.
|
||||||
|
// 1 -> [1, 100, 200]
|
||||||
|
// 2 -> [1, 90, 100]
|
||||||
|
// 3 -> [1]
|
||||||
|
// 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 */) {
|
||||||
|
if (segments_aligned_ == kSegmentAlignmentFalse ||
|
||||||
|
force_set_segment_alignment_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<uint64_t>& representation_start_times =
|
||||||
|
representation_segment_start_times_[representation_id];
|
||||||
|
representation_start_times.push_back(start_time);
|
||||||
|
// There's no way to detemine whether the segments are aligned if some
|
||||||
|
// representations do not have any segments.
|
||||||
|
if (representation_segment_start_times_.size() != representations_.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
DCHECK(!representation_start_times.empty());
|
||||||
|
const uint64_t expected_start_time = representation_start_times.front();
|
||||||
|
for (RepresentationTimeline::const_iterator it =
|
||||||
|
representation_segment_start_times_.begin();
|
||||||
|
it != representation_segment_start_times_.end(); ++it) {
|
||||||
|
// If there are no entries in a list, then there is no way for the
|
||||||
|
// segment alignment status to change.
|
||||||
|
// Note that it can be empty because entries get deleted below.
|
||||||
|
if (it->second.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (expected_start_time != it->second.front()) {
|
||||||
|
// Flag as false and clear the start times data, no need to keep it
|
||||||
|
// around.
|
||||||
|
segments_aligned_ = kSegmentAlignmentFalse;
|
||||||
|
representation_segment_start_times_.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
segments_aligned_ = kSegmentAlignmentTrue;
|
||||||
|
|
||||||
|
for (RepresentationTimeline::iterator it =
|
||||||
|
representation_segment_start_times_.begin();
|
||||||
|
it != representation_segment_start_times_.end(); ++it) {
|
||||||
|
it->second.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Representation::Representation(
|
||||||
|
const MediaInfo& media_info,
|
||||||
const MpdOptions& mpd_options,
|
const MpdOptions& mpd_options,
|
||||||
uint32_t id)
|
uint32_t id,
|
||||||
|
scoped_ptr<RepresentationStateChangeListener> state_change_listener)
|
||||||
: media_info_(media_info),
|
: media_info_(media_info),
|
||||||
id_(id),
|
id_(id),
|
||||||
bandwidth_estimator_(BandwidthEstimator::kUseAllBlocks),
|
bandwidth_estimator_(BandwidthEstimator::kUseAllBlocks),
|
||||||
mpd_options_(mpd_options),
|
mpd_options_(mpd_options),
|
||||||
start_number_(1) {
|
start_number_(1),
|
||||||
}
|
state_change_listener_(state_change_listener.Pass()) {}
|
||||||
|
|
||||||
Representation::~Representation() {
|
Representation::~Representation() {}
|
||||||
}
|
|
||||||
|
|
||||||
bool Representation::Init() {
|
bool Representation::Init() {
|
||||||
codecs_ = GetCodecs(media_info_);
|
codecs_ = GetCodecs(media_info_);
|
||||||
|
@ -801,6 +925,8 @@ void Representation::AddNewSegment(uint64_t start_time,
|
||||||
}
|
}
|
||||||
|
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
|
if (state_change_listener_)
|
||||||
|
state_change_listener_->OnNewSegmentForRepresentation(start_time, duration);
|
||||||
if (IsContiguous(start_time, duration, size)) {
|
if (IsContiguous(start_time, duration, size)) {
|
||||||
++segment_infos_.back().repeat;
|
++segment_infos_.back().repeat;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -200,6 +200,13 @@ class AdaptationSet {
|
||||||
/// NULL ScopedXmlPtr.
|
/// NULL ScopedXmlPtr.
|
||||||
xml::ScopedXmlPtr<xmlNode>::type GetXml();
|
xml::ScopedXmlPtr<xmlNode>::type GetXml();
|
||||||
|
|
||||||
|
/// Forces the (sub)segmentAlignment field to be set to @a segment_alignment.
|
||||||
|
/// Use this if you are certain that the (sub)segments are alinged/unaligned
|
||||||
|
/// for the AdaptationSet.
|
||||||
|
/// @param segment_alignment is the value used for (sub)segmentAlignment
|
||||||
|
/// attribute.
|
||||||
|
void ForceSetSegmentAlignment(bool segment_alignment);
|
||||||
|
|
||||||
/// Sets the AdaptationSet@group attribute.
|
/// Sets the AdaptationSet@group attribute.
|
||||||
/// Passing a negative value to this method will unset the attribute.
|
/// Passing a negative value to this method will unset the attribute.
|
||||||
/// Note that group=0 is a special group, as mentioned in the DASH MPD
|
/// Note that group=0 is a special group, as mentioned in the DASH MPD
|
||||||
|
@ -212,7 +219,43 @@ class AdaptationSet {
|
||||||
// Must be unique in the Period.
|
// Must be unique in the Period.
|
||||||
uint32_t id() const { return id_; }
|
uint32_t id() const { return id_; }
|
||||||
|
|
||||||
|
/// Notifies the AdaptationSet instance that a new (sub)segment was added to
|
||||||
|
/// the Representation with @a representation_id.
|
||||||
|
/// This must be called every time a (sub)segment is added to a
|
||||||
|
/// Representation in this AdaptationSet.
|
||||||
|
/// If a Representation is constructed using AddRepresentation() this
|
||||||
|
/// is called automatically whenever Representation::AddNewSegment() is
|
||||||
|
/// is called.
|
||||||
|
/// @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 OnNewSegmentForRepresentation(uint32_t representation_id,
|
||||||
|
uint64_t start_time,
|
||||||
|
uint64_t duration);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// kSegmentAlignmentUnknown means that it is uncertain if the
|
||||||
|
// (sub)segments are aligned or not.
|
||||||
|
// kSegmentAlignmentTrue means that it is certain that the all the (current)
|
||||||
|
// segments added to the adaptation set are aligned.
|
||||||
|
// kSegmentAlignmentFalse means that it is it is certain that some segments
|
||||||
|
// are not aligned. This is useful to disable the computation for
|
||||||
|
// segment alignment, once it is certain that some segments are not aligned.
|
||||||
|
enum SegmentAligmentStatus {
|
||||||
|
kSegmentAlignmentUnknown,
|
||||||
|
kSegmentAlignmentTrue,
|
||||||
|
kSegmentAlignmentFalse
|
||||||
|
};
|
||||||
|
|
||||||
|
// This maps Representations (IDs) to a list of start times of the segments.
|
||||||
|
// e.g.
|
||||||
|
// If Representation 1 has start time 0, 100, 200 and Representation 2 has
|
||||||
|
// start times 0, 200, 400, then the map contains:
|
||||||
|
// 1 -> [0, 100, 200]
|
||||||
|
// 2 -> [0, 200, 400]
|
||||||
|
typedef std::map<uint32_t, std::list<uint64_t> > RepresentationTimeline;
|
||||||
|
|
||||||
friend class MpdBuilder;
|
friend class MpdBuilder;
|
||||||
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckAdaptationSetId);
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckAdaptationSetId);
|
||||||
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest,
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest,
|
||||||
|
@ -222,19 +265,37 @@ class AdaptationSet {
|
||||||
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest,
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest,
|
||||||
CheckAdaptationSetTextContentType);
|
CheckAdaptationSetTextContentType);
|
||||||
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, SetAdaptationSetGroup);
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, SetAdaptationSetGroup);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(StaticMpdBuilderTest, SubSegmentAlignment);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(StaticMpdBuilderTest, ForceSetSubSegmentAlignment);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(DynamicMpdBuilderTest, SegmentAlignment);
|
||||||
|
|
||||||
/// @param adaptation_set_id is an ID number for this AdaptationSet.
|
/// @param adaptation_set_id is an ID number for this AdaptationSet.
|
||||||
|
/// @param lang is the language of this AdaptationSet. Mainly relevant for
|
||||||
|
/// audio.
|
||||||
|
/// @param mpd_options is the options for this MPD.
|
||||||
|
/// @param mpd_type is the type of this MPD.
|
||||||
/// @param representation_counter is a Counter for assigning ID numbers to
|
/// @param representation_counter is a Counter for assigning ID numbers to
|
||||||
/// Representation. It can not be NULL.
|
/// Representation. It can not be NULL.
|
||||||
AdaptationSet(uint32_t adaptation_set_id,
|
AdaptationSet(uint32_t adaptation_set_id,
|
||||||
const std::string& lang,
|
const std::string& lang,
|
||||||
const MpdOptions& mpd_options,
|
const MpdOptions& mpd_options,
|
||||||
|
MpdBuilder::MpdType mpd_type,
|
||||||
base::AtomicSequenceNumber* representation_counter);
|
base::AtomicSequenceNumber* representation_counter);
|
||||||
|
|
||||||
// Gets the earliest, normalized segment timestamp. Returns true if
|
// Gets the earliest, normalized segment timestamp. Returns true if
|
||||||
// successful, false otherwise.
|
// successful, false otherwise.
|
||||||
bool GetEarliestTimestamp(double* timestamp_seconds);
|
bool GetEarliestTimestamp(double* timestamp_seconds);
|
||||||
|
|
||||||
|
/// Called from OnNewSegmentForRepresentation(). Checks whether the segments
|
||||||
|
/// are aligned. Sets segments_aligned_.
|
||||||
|
/// @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);
|
||||||
|
|
||||||
std::list<ContentProtectionElement> content_protection_elements_;
|
std::list<ContentProtectionElement> content_protection_elements_;
|
||||||
std::list<Representation*> representations_;
|
std::list<Representation*> representations_;
|
||||||
::STLElementDeleter<std::list<Representation*> > representations_deleter_;
|
::STLElementDeleter<std::list<Representation*> > representations_deleter_;
|
||||||
|
@ -246,6 +307,7 @@ class AdaptationSet {
|
||||||
const uint32_t id_;
|
const uint32_t id_;
|
||||||
const std::string lang_;
|
const std::string lang_;
|
||||||
const MpdOptions& mpd_options_;
|
const MpdOptions& mpd_options_;
|
||||||
|
const MpdBuilder::MpdType mpd_type_;
|
||||||
|
|
||||||
// The group attribute for the AdaptationSet. If the value is negative,
|
// The group attribute for the AdaptationSet. If the value is negative,
|
||||||
// no group number is specified.
|
// no group number is specified.
|
||||||
|
@ -283,9 +345,31 @@ class AdaptationSet {
|
||||||
// The roles of this AdaptationSet.
|
// The roles of this AdaptationSet.
|
||||||
std::set<Role> roles_;
|
std::set<Role> roles_;
|
||||||
|
|
||||||
|
// True iff all the segments are aligned.
|
||||||
|
SegmentAligmentStatus segments_aligned_;
|
||||||
|
bool force_set_segment_alignment_;
|
||||||
|
|
||||||
|
// Keeps track of segment start times of Representations.
|
||||||
|
RepresentationTimeline representation_segment_start_times_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(AdaptationSet);
|
DISALLOW_COPY_AND_ASSIGN(AdaptationSet);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(rkuroiwa): OnSetSampleDuration() must also be added to this to notify
|
||||||
|
// sample duration change to AdaptationSet, to set the right frame rate.
|
||||||
|
class RepresentationStateChangeListener {
|
||||||
|
public:
|
||||||
|
RepresentationStateChangeListener() {}
|
||||||
|
virtual ~RepresentationStateChangeListener() {}
|
||||||
|
|
||||||
|
/// Notifies the instance that a new (sub)segment was added to
|
||||||
|
/// the Representation.
|
||||||
|
/// @param start_time is the start time of the new segment.
|
||||||
|
/// @param duration is the duration of the new segment.
|
||||||
|
virtual void OnNewSegmentForRepresentation(uint64_t start_time,
|
||||||
|
uint64_t duration) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
/// Representation class contains references to a single media stream, as
|
/// Representation class contains references to a single media stream, as
|
||||||
/// well as optional ContentProtection elements for that stream.
|
/// well as optional ContentProtection elements for that stream.
|
||||||
class Representation {
|
class Representation {
|
||||||
|
@ -309,8 +393,10 @@ class Representation {
|
||||||
/// then the former is used.
|
/// then the former is used.
|
||||||
void AddContentProtectionElement(const ContentProtectionElement& element);
|
void AddContentProtectionElement(const ContentProtectionElement& element);
|
||||||
|
|
||||||
/// Add a media segment to the representation.
|
/// Add a media (sub)segment to the representation.
|
||||||
/// @param start_time is the start time for the segment, in units of the
|
/// AdaptationSet@{subSegmentAlignment,segmentAlignment} cannot be set
|
||||||
|
/// if this is not called for all Representations.
|
||||||
|
/// @param start_time is the start time for the (sub)segment, in units of the
|
||||||
/// stream's time scale.
|
/// stream's time scale.
|
||||||
/// @param duration is the duration of the segment, in units of the stream's
|
/// @param duration is the duration of the segment, in units of the stream's
|
||||||
/// time scale.
|
/// time scale.
|
||||||
|
@ -339,15 +425,22 @@ class Representation {
|
||||||
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml);
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml);
|
||||||
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckRepresentationId);
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckRepresentationId);
|
||||||
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, SetSampleDuration);
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, SetSampleDuration);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest,
|
||||||
|
RepresentationStateChangeListener);
|
||||||
|
|
||||||
/// @param media_info is a MediaInfo containing information on the media.
|
/// @param media_info is a MediaInfo containing information on the media.
|
||||||
/// @a media_info.bandwidth is required for 'static' profile. If @a
|
/// @a media_info.bandwidth is required for 'static' profile. If @a
|
||||||
/// media_info.bandwidth is not present in 'dynamic' profile, this
|
/// media_info.bandwidth is not present in 'dynamic' profile, this
|
||||||
/// tries to estimate it using the info passed to AddNewSegment().
|
/// tries to estimate it using the info passed to AddNewSegment().
|
||||||
|
/// @param mpd_options is options for the entire MPD.
|
||||||
/// @param representation_id is the numeric ID for the <Representation>.
|
/// @param representation_id is the numeric ID for the <Representation>.
|
||||||
Representation(const MediaInfo& media_info,
|
/// @param state_change_listener is an event handler for state changes to
|
||||||
|
/// the representation. If null, no event handler registered.
|
||||||
|
Representation(
|
||||||
|
const MediaInfo& media_info,
|
||||||
const MpdOptions& mpd_options,
|
const MpdOptions& mpd_options,
|
||||||
uint32_t representation_id);
|
uint32_t representation_id,
|
||||||
|
scoped_ptr<RepresentationStateChangeListener> state_change_listener);
|
||||||
|
|
||||||
bool AddLiveInfo(xml::RepresentationXmlNode* representation);
|
bool AddLiveInfo(xml::RepresentationXmlNode* representation);
|
||||||
|
|
||||||
|
@ -391,6 +484,10 @@ class Representation {
|
||||||
// Starts from 1.
|
// Starts from 1.
|
||||||
uint32_t start_number_;
|
uint32_t start_number_;
|
||||||
|
|
||||||
|
// If this is not null, then Representation is responsible for calling the
|
||||||
|
// right methods at right timings.
|
||||||
|
scoped_ptr<RepresentationStateChangeListener> state_change_listener_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Representation);
|
DISALLOW_COPY_AND_ASSIGN(Representation);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://developers.google.com/open-source/licenses/bsd
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <libxml/xmlstring.h>
|
#include <libxml/xmlstring.h>
|
||||||
|
@ -77,6 +78,16 @@ void ExpectAttributeNotSet(base::StringPiece attribute, xmlNodePtr node) {
|
||||||
xmlGetProp(node, BAD_CAST attribute.data()));
|
xmlGetProp(node, BAD_CAST attribute.data()));
|
||||||
ASSERT_FALSE(attribute_xml_str);
|
ASSERT_FALSE(attribute_xml_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MockRepresentationStateChangeListener
|
||||||
|
: public RepresentationStateChangeListener {
|
||||||
|
public:
|
||||||
|
MockRepresentationStateChangeListener() {}
|
||||||
|
~MockRepresentationStateChangeListener() {}
|
||||||
|
|
||||||
|
MOCK_METHOD2(OnNewSegmentForRepresentation,
|
||||||
|
void(uint64_t start_time, uint64_t duration));
|
||||||
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
template <MpdBuilder::MpdType type>
|
template <MpdBuilder::MpdType type>
|
||||||
|
@ -106,6 +117,12 @@ class MpdBuilderTest: public ::testing::Test {
|
||||||
representation_ = representation;
|
representation_ = representation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to return an empty listener for tests that don't need
|
||||||
|
// it.
|
||||||
|
scoped_ptr<RepresentationStateChangeListener> NoListener() {
|
||||||
|
return scoped_ptr<RepresentationStateChangeListener>();
|
||||||
|
}
|
||||||
|
|
||||||
MpdBuilder mpd_;
|
MpdBuilder mpd_;
|
||||||
|
|
||||||
// We usually need only one representation.
|
// We usually need only one representation.
|
||||||
|
@ -199,6 +216,8 @@ class SegmentTemplateTest : public DynamicMpdBuilderTest {
|
||||||
|
|
||||||
std::string TemplateOutputInsertValues(const std::string& s_elements_string,
|
std::string TemplateOutputInsertValues(const std::string& s_elements_string,
|
||||||
uint64_t bandwidth) {
|
uint64_t bandwidth) {
|
||||||
|
// Note: Since all the tests have 1 Representation, the AdaptationSet
|
||||||
|
// always has segmentAligntment=true.
|
||||||
const char kOutputTemplate[] =
|
const char kOutputTemplate[] =
|
||||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
"<MPD xmlns=\"urn:mpeg:DASH:schema:MPD:2011\" "
|
"<MPD xmlns=\"urn:mpeg:DASH:schema:MPD:2011\" "
|
||||||
|
@ -210,7 +229,7 @@ class SegmentTemplateTest : public DynamicMpdBuilderTest {
|
||||||
" <Period start=\"PT0S\">\n"
|
" <Period start=\"PT0S\">\n"
|
||||||
" <AdaptationSet id=\"0\" width=\"720\" height=\"480\""
|
" <AdaptationSet id=\"0\" width=\"720\" height=\"480\""
|
||||||
" frameRate=\"10/5\" contentType=\"video\""
|
" frameRate=\"10/5\" contentType=\"video\""
|
||||||
" par=\"3:2\">\n"
|
" par=\"3:2\" segmentAlignment=\"true\">\n"
|
||||||
" <Representation id=\"0\" bandwidth=\"%" PRIu64 "\" "
|
" <Representation id=\"0\" bandwidth=\"%" PRIu64 "\" "
|
||||||
"codecs=\"avc1.010101\" mimeType=\"video/mp4\" width=\"720\" "
|
"codecs=\"avc1.010101\" mimeType=\"video/mp4\" width=\"720\" "
|
||||||
"height=\"480\" frameRate=\"10/5\" sar=\"1:1\">\n"
|
"height=\"480\" frameRate=\"10/5\" sar=\"1:1\">\n"
|
||||||
|
@ -282,6 +301,8 @@ class TimeShiftBufferDepthTest : public SegmentTemplateTest {
|
||||||
void CheckTimeShiftBufferDepthResult(const std::string& expected_s_element,
|
void CheckTimeShiftBufferDepthResult(const std::string& expected_s_element,
|
||||||
int expected_time_shift_buffer_depth,
|
int expected_time_shift_buffer_depth,
|
||||||
int expected_start_number) {
|
int expected_start_number) {
|
||||||
|
// Note: Since all the tests have 1 Representation, the AdaptationSet
|
||||||
|
// always has segmentAligntment=true.
|
||||||
const char kOutputTemplate[] =
|
const char kOutputTemplate[] =
|
||||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
"<MPD xmlns=\"urn:mpeg:DASH:schema:MPD:2011\" "
|
"<MPD xmlns=\"urn:mpeg:DASH:schema:MPD:2011\" "
|
||||||
|
@ -294,7 +315,7 @@ class TimeShiftBufferDepthTest : public SegmentTemplateTest {
|
||||||
" <Period start=\"PT0S\">\n"
|
" <Period start=\"PT0S\">\n"
|
||||||
" <AdaptationSet id=\"0\" width=\"720\" height=\"480\""
|
" <AdaptationSet id=\"0\" width=\"720\" height=\"480\""
|
||||||
" frameRate=\"10/2\" contentType=\"video\""
|
" frameRate=\"10/2\" contentType=\"video\""
|
||||||
" par=\"3:2\">\n"
|
" par=\"3:2\" segmentAlignment=\"true\">\n"
|
||||||
" <Representation id=\"0\" bandwidth=\"%" PRIu64 "\" "
|
" <Representation id=\"0\" bandwidth=\"%" PRIu64 "\" "
|
||||||
"codecs=\"avc1.010101\" mimeType=\"video/mp4\" width=\"720\" "
|
"codecs=\"avc1.010101\" mimeType=\"video/mp4\" width=\"720\" "
|
||||||
"height=\"480\" frameRate=\"10/2\" sar=\"1:1\">\n"
|
"height=\"480\" frameRate=\"10/2\" sar=\"1:1\">\n"
|
||||||
|
@ -328,8 +349,8 @@ class TimeShiftBufferDepthTest : public SegmentTemplateTest {
|
||||||
// Verify that AdaptationSet@group can be set and unset.
|
// Verify that AdaptationSet@group can be set and unset.
|
||||||
TEST_F(CommonMpdBuilderTest, SetAdaptationSetGroup) {
|
TEST_F(CommonMpdBuilderTest, SetAdaptationSetGroup) {
|
||||||
base::AtomicSequenceNumber sequence_counter;
|
base::AtomicSequenceNumber sequence_counter;
|
||||||
AdaptationSet adaptation_set(
|
AdaptationSet adaptation_set(kAnyAdaptationSetId, "", MpdOptions(),
|
||||||
kAnyAdaptationSetId, "", MpdOptions(), &sequence_counter);
|
MpdBuilder::kStatic, &sequence_counter);
|
||||||
adaptation_set.set_group(1);
|
adaptation_set.set_group(1);
|
||||||
|
|
||||||
xml::ScopedXmlPtr<xmlNode>::type xml_with_group(adaptation_set.GetXml());
|
xml::ScopedXmlPtr<xmlNode>::type xml_with_group(adaptation_set.GetXml());
|
||||||
|
@ -357,8 +378,9 @@ TEST_F(CommonMpdBuilderTest, ValidMediaInfo) {
|
||||||
" pixel_height: 1\n"
|
" pixel_height: 1\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"container_type: 1\n";
|
"container_type: 1\n";
|
||||||
Representation representation(
|
Representation representation(ConvertToMediaInfo(kTestMediaInfo),
|
||||||
ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), kAnyRepresentationId);
|
MpdOptions(), kAnyRepresentationId,
|
||||||
|
NoListener());
|
||||||
EXPECT_TRUE(representation.Init());
|
EXPECT_TRUE(representation.Init());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,8 +397,9 @@ TEST_F(CommonMpdBuilderTest, InvalidMediaInfo) {
|
||||||
" pixel_height: 1\n"
|
" pixel_height: 1\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"container_type: 1\n";
|
"container_type: 1\n";
|
||||||
Representation representation(
|
Representation representation(ConvertToMediaInfo(kTestMediaInfo),
|
||||||
ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), kAnyRepresentationId);
|
MpdOptions(), kAnyRepresentationId,
|
||||||
|
NoListener());
|
||||||
EXPECT_FALSE(representation.Init());
|
EXPECT_FALSE(representation.Init());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,8 +416,9 @@ TEST_F(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml) {
|
||||||
" pixel_height: 1\n"
|
" pixel_height: 1\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"container_type: 1\n";
|
"container_type: 1\n";
|
||||||
Representation representation(
|
Representation representation(ConvertToMediaInfo(kTestMediaInfo),
|
||||||
ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), kAnyRepresentationId);
|
MpdOptions(), kAnyRepresentationId,
|
||||||
|
NoListener());
|
||||||
EXPECT_TRUE(representation.Init());
|
EXPECT_TRUE(representation.Init());
|
||||||
xml::ScopedXmlPtr<xmlNode>::type node_xml(representation.GetXml());
|
xml::ScopedXmlPtr<xmlNode>::type node_xml(representation.GetXml());
|
||||||
EXPECT_NO_FATAL_FAILURE(
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
@ -409,6 +433,35 @@ TEST_F(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml) {
|
||||||
ExpectAttributeEqString("frameRate", "10/10", node_xml.get()));
|
ExpectAttributeEqString("frameRate", "10/10", node_xml.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure RepresentationStateChangeListener::OnNewSegmentForRepresentation()
|
||||||
|
// is called.
|
||||||
|
TEST_F(CommonMpdBuilderTest, RepresentationStateChangeListener) {
|
||||||
|
const char kTestMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1'\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
|
||||||
|
const uint64_t kStartTime = 199238u;
|
||||||
|
const uint64_t kDuration = 98u;
|
||||||
|
scoped_ptr<MockRepresentationStateChangeListener> listener(
|
||||||
|
new MockRepresentationStateChangeListener());
|
||||||
|
EXPECT_CALL(*listener,
|
||||||
|
OnNewSegmentForRepresentation(kStartTime, kDuration));
|
||||||
|
Representation representation(
|
||||||
|
ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), kAnyRepresentationId,
|
||||||
|
listener.PassAs<RepresentationStateChangeListener>());
|
||||||
|
EXPECT_TRUE(representation.Init());
|
||||||
|
|
||||||
|
representation.AddNewSegment(kStartTime, kDuration, 10 /* any size */);
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that content type is set correctly if video info is present in
|
// Verify that content type is set correctly if video info is present in
|
||||||
// MediaInfo.
|
// MediaInfo.
|
||||||
TEST_F(CommonMpdBuilderTest, CheckAdaptationSetVideoContentType) {
|
TEST_F(CommonMpdBuilderTest, CheckAdaptationSetVideoContentType) {
|
||||||
|
@ -425,8 +478,8 @@ TEST_F(CommonMpdBuilderTest, CheckAdaptationSetVideoContentType) {
|
||||||
"}\n"
|
"}\n"
|
||||||
"container_type: 1\n";
|
"container_type: 1\n";
|
||||||
|
|
||||||
AdaptationSet adaptation_set(
|
AdaptationSet adaptation_set(kAnyAdaptationSetId, "", MpdOptions(),
|
||||||
kAnyAdaptationSetId, "", MpdOptions(), &sequence_counter);
|
MpdBuilder::kStatic, &sequence_counter);
|
||||||
adaptation_set.AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo));
|
adaptation_set.AddRepresentation(ConvertToMediaInfo(kVideoMediaInfo));
|
||||||
|
|
||||||
xml::ScopedXmlPtr<xmlNode>::type node_xml(adaptation_set.GetXml());
|
xml::ScopedXmlPtr<xmlNode>::type node_xml(adaptation_set.GetXml());
|
||||||
|
@ -447,8 +500,8 @@ TEST_F(CommonMpdBuilderTest, CheckAdaptationSetAudioContentType) {
|
||||||
"}\n"
|
"}\n"
|
||||||
"container_type: 1\n";
|
"container_type: 1\n";
|
||||||
|
|
||||||
AdaptationSet adaptation_set(
|
AdaptationSet adaptation_set(kAnyAdaptationSetId, "", MpdOptions(),
|
||||||
kAnyAdaptationSetId, "", MpdOptions(), &sequence_counter);
|
MpdBuilder::kStatic, &sequence_counter);
|
||||||
adaptation_set.AddRepresentation(ConvertToMediaInfo(kAudioMediaInfo));
|
adaptation_set.AddRepresentation(ConvertToMediaInfo(kAudioMediaInfo));
|
||||||
|
|
||||||
xml::ScopedXmlPtr<xmlNode>::type node_xml(adaptation_set.GetXml());
|
xml::ScopedXmlPtr<xmlNode>::type node_xml(adaptation_set.GetXml());
|
||||||
|
@ -470,8 +523,8 @@ TEST_F(CommonMpdBuilderTest, DISABLED_CheckAdaptationSetTextContentType) {
|
||||||
"}\n"
|
"}\n"
|
||||||
"container_type: 1\n";
|
"container_type: 1\n";
|
||||||
|
|
||||||
AdaptationSet adaptation_set(
|
AdaptationSet adaptation_set(kAnyAdaptationSetId, "", MpdOptions(),
|
||||||
kAnyAdaptationSetId, "", MpdOptions(), &sequence_counter);
|
MpdBuilder::kStatic, &sequence_counter);
|
||||||
adaptation_set.AddRepresentation(ConvertToMediaInfo(kTextMediaInfo));
|
adaptation_set.AddRepresentation(ConvertToMediaInfo(kTextMediaInfo));
|
||||||
|
|
||||||
xml::ScopedXmlPtr<xmlNode>::type node_xml(adaptation_set.GetXml());
|
xml::ScopedXmlPtr<xmlNode>::type node_xml(adaptation_set.GetXml());
|
||||||
|
@ -482,8 +535,8 @@ TEST_F(CommonMpdBuilderTest, DISABLED_CheckAdaptationSetTextContentType) {
|
||||||
TEST_F(CommonMpdBuilderTest, CheckAdaptationSetId) {
|
TEST_F(CommonMpdBuilderTest, CheckAdaptationSetId) {
|
||||||
base::AtomicSequenceNumber sequence_counter;
|
base::AtomicSequenceNumber sequence_counter;
|
||||||
const uint32_t kAdaptationSetId = 42;
|
const uint32_t kAdaptationSetId = 42;
|
||||||
AdaptationSet adaptation_set(
|
AdaptationSet adaptation_set(kAdaptationSetId, "", MpdOptions(),
|
||||||
kAdaptationSetId, "", MpdOptions(), &sequence_counter);
|
MpdBuilder::kStatic, &sequence_counter);
|
||||||
ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kAdaptationSetId, &adaptation_set));
|
ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kAdaptationSetId, &adaptation_set));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,6 +802,176 @@ TEST_F(CommonMpdBuilderTest,
|
||||||
ExpectAttributeNotSet("frameRate", adaptation_set_xml.get()));
|
ExpectAttributeNotSet("frameRate", adaptation_set_xml.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that subSegmentAlignment is set to true if all the Representations'
|
||||||
|
// segments are aligned and the MPD type is static.
|
||||||
|
TEST_F(StaticMpdBuilderTest, SubSegmentAlignment) {
|
||||||
|
base::AtomicSequenceNumber sequence_counter;
|
||||||
|
const char k480pMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1'\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 8\n"
|
||||||
|
" pixel_height: 9\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
const char k360pMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1'\n"
|
||||||
|
" width: 640\n"
|
||||||
|
" height: 360\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
AdaptationSet adaptation_set(kAnyAdaptationSetId, "", MpdOptions(),
|
||||||
|
MpdBuilder::kStatic, &sequence_counter);
|
||||||
|
Representation* representation_480p =
|
||||||
|
adaptation_set.AddRepresentation(ConvertToMediaInfo(k480pMediaInfo));
|
||||||
|
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());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
xml::ScopedXmlPtr<xmlNode>::type unaligned(adaptation_set.GetXml());
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeNotSet("subSegmentAlignment", unaligned.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that subSegmentAlignment can be force set to true.
|
||||||
|
TEST_F(StaticMpdBuilderTest, ForceSetSubSegmentAlignment) {
|
||||||
|
base::AtomicSequenceNumber sequence_counter;
|
||||||
|
const char k480pMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1'\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 8\n"
|
||||||
|
" pixel_height: 9\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
const char k360pMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1'\n"
|
||||||
|
" width: 640\n"
|
||||||
|
" height: 360\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
AdaptationSet adaptation_set(kAnyAdaptationSetId, "", MpdOptions(),
|
||||||
|
MpdBuilder::kStatic, &sequence_counter);
|
||||||
|
Representation* representation_480p =
|
||||||
|
adaptation_set.AddRepresentation(ConvertToMediaInfo(k480pMediaInfo));
|
||||||
|
Representation* representation_360p =
|
||||||
|
adaptation_set.AddRepresentation(ConvertToMediaInfo(k360pMediaInfo));
|
||||||
|
|
||||||
|
// Use different starting times to make the segments "not aligned".
|
||||||
|
const uint64_t kStartTime1 = 1u;
|
||||||
|
const uint64_t kStartTime2 = 2u;
|
||||||
|
COMPILE_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);
|
||||||
|
xml::ScopedXmlPtr<xmlNode>::type unaligned(adaptation_set.GetXml());
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeNotSet("subSegmentAlignment", unaligned.get()));
|
||||||
|
|
||||||
|
// Then force set the segment alignment attribute to true.
|
||||||
|
adaptation_set.ForceSetSegmentAlignment(true);
|
||||||
|
xml::ScopedXmlPtr<xmlNode>::type aligned(adaptation_set.GetXml());
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeEqString("subSegmentAlignment", "true", aligned.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that segmentAlignment is set to true if all the Representations
|
||||||
|
// segments' are aligned and the MPD type is dynamic.
|
||||||
|
TEST_F(DynamicMpdBuilderTest, SegmentAlignment) {
|
||||||
|
base::AtomicSequenceNumber sequence_counter;
|
||||||
|
const char k480pMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1'\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 8\n"
|
||||||
|
" pixel_height: 9\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
const char k360pMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1'\n"
|
||||||
|
" width: 640\n"
|
||||||
|
" height: 360\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
AdaptationSet adaptation_set(kAnyAdaptationSetId, "", MpdOptions(),
|
||||||
|
MpdBuilder::kDynamic, &sequence_counter);
|
||||||
|
Representation* representation_480p =
|
||||||
|
adaptation_set.AddRepresentation(ConvertToMediaInfo(k480pMediaInfo));
|
||||||
|
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("segmentAlignment", "true", aligned.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);
|
||||||
|
|
||||||
|
xml::ScopedXmlPtr<xmlNode>::type unaligned(adaptation_set.GetXml());
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeNotSet("subSegmentAlignment", unaligned.get()));
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that the width and height attribute are set if all the video
|
// Verify that the width and height attribute are set if all the video
|
||||||
// representations have the same width and height.
|
// representations have the same width and height.
|
||||||
TEST_F(StaticMpdBuilderTest, AdapatationSetWidthAndHeight) {
|
TEST_F(StaticMpdBuilderTest, AdapatationSetWidthAndHeight) {
|
||||||
|
@ -835,7 +1058,7 @@ TEST_F(CommonMpdBuilderTest, CheckRepresentationId) {
|
||||||
const uint32_t kRepresentationId = 1;
|
const uint32_t kRepresentationId = 1;
|
||||||
|
|
||||||
Representation representation(
|
Representation representation(
|
||||||
video_media_info, MpdOptions(), kRepresentationId);
|
video_media_info, MpdOptions(), kRepresentationId, NoListener());
|
||||||
EXPECT_TRUE(representation.Init());
|
EXPECT_TRUE(representation.Init());
|
||||||
ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kRepresentationId, &representation));
|
ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kRepresentationId, &representation));
|
||||||
}
|
}
|
||||||
|
@ -849,7 +1072,7 @@ TEST_F(CommonMpdBuilderTest, SetSampleDuration) {
|
||||||
const uint32_t kRepresentationId = 1;
|
const uint32_t kRepresentationId = 1;
|
||||||
|
|
||||||
Representation representation(
|
Representation representation(
|
||||||
video_media_info, MpdOptions(), kRepresentationId);
|
video_media_info, MpdOptions(), kRepresentationId, NoListener());
|
||||||
EXPECT_TRUE(representation.Init());
|
EXPECT_TRUE(representation.Init());
|
||||||
representation.SetSampleDuration(2u);
|
representation.SetSampleDuration(2u);
|
||||||
EXPECT_EQ(2u,
|
EXPECT_EQ(2u,
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
'../base/base.gyp:base',
|
'../base/base.gyp:base',
|
||||||
'../media/file/file.gyp:file',
|
'../media/file/file.gyp:file',
|
||||||
'../media/test/media_test.gyp:run_all_unittests',
|
'../media/test/media_test.gyp:run_all_unittests',
|
||||||
|
'../testing/gmock.gyp:gmock',
|
||||||
'../testing/gtest.gyp:gtest',
|
'../testing/gtest.gyp:gtest',
|
||||||
'mpd_builder',
|
'mpd_builder',
|
||||||
'mpd_util',
|
'mpd_util',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" availabilityStartTime="2011-12-25T12:30:00" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011">
|
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" availabilityStartTime="2011-12-25T12:30:00" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011">
|
||||||
<Period start="PT0S">
|
<Period start="PT0S">
|
||||||
<AdaptationSet id="0" width="720" height="480" frameRate="10/5" contentType="video" par="3:2">
|
<AdaptationSet id="0" width="720" height="480" frameRate="10/5" contentType="video" par="3:2" segmentAlignment="true">
|
||||||
<Representation id="0" bandwidth="102400" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480" frameRate="10/5" sar="1:1">
|
<Representation id="0" bandwidth="102400" codecs="avc1.010101" mimeType="video/mp4" width="720" height="480" frameRate="10/5" sar="1:1">
|
||||||
<SegmentTemplate timescale="1000" initialization="init.mp4" media="$Time$.mp4">
|
<SegmentTemplate timescale="1000" initialization="init.mp4" media="$Time$.mp4">
|
||||||
<SegmentTimeline>
|
<SegmentTimeline>
|
||||||
|
|
Loading…
Reference in New Issue