Wire up Representation::SetSampleDuration() to update AdaptationSet frame rate
- Representation::SetSampleDuration() calls AdaptatoinSet::OnSetFrameRateForRepresentation() via RepresentationStateChangeListener. Change-Id: I4a76c57798c9b6dc1e84ba47336ed1c78fbabfd6
This commit is contained in:
parent
336435fcca
commit
ab8f64ea2c
|
@ -347,6 +347,12 @@ class RepresentationStateChangeListenerImpl
|
||||||
start_time, duration);
|
start_time, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void OnSetFrameRateForRepresentation(uint32_t frame_duration,
|
||||||
|
uint32_t timescale) OVERRIDE {
|
||||||
|
adaptation_set_->OnSetFrameRateForRepresentation(representation_id_,
|
||||||
|
frame_duration, timescale);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const uint32_t representation_id_;
|
const uint32_t representation_id_;
|
||||||
AdaptationSet* const adaptation_set_;
|
AdaptationSet* const adaptation_set_;
|
||||||
|
@ -653,12 +659,8 @@ Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) {
|
||||||
video_widths_.insert(video_info.width());
|
video_widths_.insert(video_info.width());
|
||||||
video_heights_.insert(video_info.height());
|
video_heights_.insert(video_info.height());
|
||||||
|
|
||||||
if (video_info.has_time_scale() && video_info.has_frame_duration()) {
|
if (video_info.has_time_scale() && video_info.has_frame_duration())
|
||||||
video_frame_rates_[static_cast<double>(video_info.time_scale()) /
|
RecordFrameRate(video_info.frame_duration(), video_info.time_scale());
|
||||||
video_info.frame_duration()] =
|
|
||||||
base::IntToString(video_info.time_scale()) + "/" +
|
|
||||||
base::IntToString(video_info.frame_duration());
|
|
||||||
}
|
|
||||||
|
|
||||||
AddPictureAspectRatio(video_info, &picture_aspect_ratio_);
|
AddPictureAspectRatio(video_info, &picture_aspect_ratio_);
|
||||||
}
|
}
|
||||||
|
@ -772,6 +774,14 @@ void AdaptationSet::OnNewSegmentForRepresentation(uint32_t representation_id,
|
||||||
CheckSegmentAlignment(representation_id, start_time, duration);
|
CheckSegmentAlignment(representation_id, start_time, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdaptationSet::OnSetFrameRateForRepresentation(
|
||||||
|
uint32_t /* representation_id */,
|
||||||
|
uint32_t frame_duration,
|
||||||
|
uint32_t timescale) {
|
||||||
|
base::AutoLock scoped_lock(lock_);
|
||||||
|
RecordFrameRate(frame_duration, timescale);
|
||||||
|
}
|
||||||
|
|
||||||
bool AdaptationSet::GetEarliestTimestamp(double* timestamp_seconds) {
|
bool AdaptationSet::GetEarliestTimestamp(double* timestamp_seconds) {
|
||||||
DCHECK(timestamp_seconds);
|
DCHECK(timestamp_seconds);
|
||||||
|
|
||||||
|
@ -864,6 +874,18 @@ void AdaptationSet::CheckSegmentAlignment(uint32_t representation_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since all AdaptationSet cares about is the maxFrameRate, representation_id
|
||||||
|
// is not passed to this method.
|
||||||
|
void AdaptationSet::RecordFrameRate(uint32_t frame_duration,
|
||||||
|
uint32_t timescale) {
|
||||||
|
if (frame_duration == 0) {
|
||||||
|
LOG(ERROR) << "Frame duration is 0 and cannot be set.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
video_frame_rates_[static_cast<double>(timescale) / frame_duration] =
|
||||||
|
base::IntToString(timescale) + "/" + base::IntToString(frame_duration);
|
||||||
|
}
|
||||||
|
|
||||||
Representation::Representation(
|
Representation::Representation(
|
||||||
const MediaInfo& media_info,
|
const MediaInfo& media_info,
|
||||||
const MpdOptions& mpd_options,
|
const MpdOptions& mpd_options,
|
||||||
|
@ -949,9 +971,15 @@ void Representation::AddNewSegment(uint64_t start_time,
|
||||||
}
|
}
|
||||||
|
|
||||||
void Representation::SetSampleDuration(uint32_t sample_duration) {
|
void Representation::SetSampleDuration(uint32_t sample_duration) {
|
||||||
// Assume single video info.
|
base::AutoLock scoped_lock(lock_);
|
||||||
if (media_info_.has_video_info())
|
|
||||||
|
if (media_info_.has_video_info()) {
|
||||||
media_info_.mutable_video_info()->set_frame_duration(sample_duration);
|
media_info_.mutable_video_info()->set_frame_duration(sample_duration);
|
||||||
|
if (state_change_listener_) {
|
||||||
|
state_change_listener_->OnSetFrameRateForRepresentation(
|
||||||
|
sample_duration, media_info_.video_info().time_scale());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses info in |media_info_| and |content_protection_elements_| to create a
|
// Uses info in |media_info_| and |content_protection_elements_| to create a
|
||||||
|
|
|
@ -237,6 +237,22 @@ class AdaptationSet {
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration);
|
uint64_t duration);
|
||||||
|
|
||||||
|
/// Notifies the AdaptationSet instance that the sample duration for the
|
||||||
|
/// Representation was set.
|
||||||
|
/// The frame duration for a video Representation might not be specified when
|
||||||
|
/// a Representation is created (by calling AddRepresentation()).
|
||||||
|
/// This should be used to notify this instance that the frame rate for a
|
||||||
|
/// Represenatation has been set.
|
||||||
|
/// This method is called automatically when
|
||||||
|
/// Represenatation::SetSampleDuration() is called if the Represenatation
|
||||||
|
/// instance was created using AddRepresentation().
|
||||||
|
/// @param representation_id is the id of the Representation.
|
||||||
|
/// @frame_duration is the duration of a frame in the Representation.
|
||||||
|
/// @param timescale is the timescale of the Representation.
|
||||||
|
void OnSetFrameRateForRepresentation(uint32_t representation_id,
|
||||||
|
uint32_t frame_duration,
|
||||||
|
uint32_t timescale);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// @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
|
/// @param lang is the language of this AdaptationSet. Mainly relevant for
|
||||||
|
@ -286,6 +302,9 @@ class AdaptationSet {
|
||||||
FRIEND_TEST_ALL_PREFIXES(StaticMpdBuilderTest, SubSegmentAlignment);
|
FRIEND_TEST_ALL_PREFIXES(StaticMpdBuilderTest, SubSegmentAlignment);
|
||||||
FRIEND_TEST_ALL_PREFIXES(StaticMpdBuilderTest, ForceSetSubSegmentAlignment);
|
FRIEND_TEST_ALL_PREFIXES(StaticMpdBuilderTest, ForceSetSubSegmentAlignment);
|
||||||
FRIEND_TEST_ALL_PREFIXES(DynamicMpdBuilderTest, SegmentAlignment);
|
FRIEND_TEST_ALL_PREFIXES(DynamicMpdBuilderTest, SegmentAlignment);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(
|
||||||
|
CommonMpdBuilderTest,
|
||||||
|
SetAdaptationFrameRateUsingRepresentationSetSampleDuration);
|
||||||
|
|
||||||
// Gets the earliest, normalized segment timestamp. Returns true if
|
// Gets the earliest, normalized segment timestamp. Returns true if
|
||||||
// successful, false otherwise.
|
// successful, false otherwise.
|
||||||
|
@ -301,6 +320,9 @@ class AdaptationSet {
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration);
|
uint64_t duration);
|
||||||
|
|
||||||
|
// Records the framerate of a Representation.
|
||||||
|
void RecordFrameRate(uint32_t frame_duration, uint32_t timescale);
|
||||||
|
|
||||||
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_;
|
||||||
|
@ -360,8 +382,6 @@ class AdaptationSet {
|
||||||
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 {
|
class RepresentationStateChangeListener {
|
||||||
public:
|
public:
|
||||||
RepresentationStateChangeListener() {}
|
RepresentationStateChangeListener() {}
|
||||||
|
@ -373,6 +393,13 @@ class RepresentationStateChangeListener {
|
||||||
/// @param duration is the duration of the new segment.
|
/// @param duration is the duration of the new segment.
|
||||||
virtual void OnNewSegmentForRepresentation(uint64_t start_time,
|
virtual void OnNewSegmentForRepresentation(uint64_t start_time,
|
||||||
uint64_t duration) = 0;
|
uint64_t duration) = 0;
|
||||||
|
|
||||||
|
/// Notifies the instance that the frame rate was set for the
|
||||||
|
/// Representation.
|
||||||
|
/// @param frame_duration is the duration of a frame.
|
||||||
|
/// @param timescale is the timescale of the Representation.
|
||||||
|
virtual void OnSetFrameRateForRepresentation(uint32_t frame_duration,
|
||||||
|
uint32_t timescale) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Representation class contains references to a single media stream, as
|
/// Representation class contains references to a single media stream, as
|
||||||
|
@ -412,7 +439,7 @@ class Representation {
|
||||||
uint64_t size);
|
uint64_t size);
|
||||||
|
|
||||||
/// Set the sample duration of this Representation.
|
/// Set the sample duration of this Representation.
|
||||||
/// In most cases, the sample duration is not available right away. This
|
/// Sample duration is not available right away especially for live. This
|
||||||
/// allows setting the sample duration after the Representation has been
|
/// allows setting the sample duration after the Representation has been
|
||||||
/// initialized.
|
/// initialized.
|
||||||
/// @param sample_duration is the duration of a sample.
|
/// @param sample_duration is the duration of a sample.
|
||||||
|
@ -449,8 +476,12 @@ 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,
|
FRIEND_TEST_ALL_PREFIXES(
|
||||||
RepresentationStateChangeListener);
|
CommonMpdBuilderTest,
|
||||||
|
RepresentationStateChangeListenerOnNewSegmentForRepresentation);
|
||||||
|
FRIEND_TEST_ALL_PREFIXES(
|
||||||
|
CommonMpdBuilderTest,
|
||||||
|
RepresentationStateChangeListenerOnSetFrameRateForRepresentation);
|
||||||
|
|
||||||
bool AddLiveInfo(xml::RepresentationXmlNode* representation);
|
bool AddLiveInfo(xml::RepresentationXmlNode* representation);
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,9 @@ class MockRepresentationStateChangeListener
|
||||||
|
|
||||||
MOCK_METHOD2(OnNewSegmentForRepresentation,
|
MOCK_METHOD2(OnNewSegmentForRepresentation,
|
||||||
void(uint64_t start_time, uint64_t duration));
|
void(uint64_t start_time, uint64_t duration));
|
||||||
|
|
||||||
|
MOCK_METHOD2(OnSetFrameRateForRepresentation,
|
||||||
|
void(uint32_t frame_duration, uint32_t timescale));
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -435,7 +438,8 @@ TEST_F(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml) {
|
||||||
|
|
||||||
// Make sure RepresentationStateChangeListener::OnNewSegmentForRepresentation()
|
// Make sure RepresentationStateChangeListener::OnNewSegmentForRepresentation()
|
||||||
// is called.
|
// is called.
|
||||||
TEST_F(CommonMpdBuilderTest, RepresentationStateChangeListener) {
|
TEST_F(CommonMpdBuilderTest,
|
||||||
|
RepresentationStateChangeListenerOnNewSegmentForRepresentation) {
|
||||||
const char kTestMediaInfo[] =
|
const char kTestMediaInfo[] =
|
||||||
"video_info {\n"
|
"video_info {\n"
|
||||||
" codec: 'avc1'\n"
|
" codec: 'avc1'\n"
|
||||||
|
@ -462,6 +466,37 @@ TEST_F(CommonMpdBuilderTest, RepresentationStateChangeListener) {
|
||||||
representation.AddNewSegment(kStartTime, kDuration, 10 /* any size */);
|
representation.AddNewSegment(kStartTime, kDuration, 10 /* any size */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure
|
||||||
|
// RepresentationStateChangeListener::OnSetFrameRateForRepresentation()
|
||||||
|
// is called.
|
||||||
|
TEST_F(CommonMpdBuilderTest,
|
||||||
|
RepresentationStateChangeListenerOnSetFrameRateForRepresentation) {
|
||||||
|
const char kTestMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1'\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 1000\n"
|
||||||
|
" frame_duration: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
|
||||||
|
const uint64_t kTimeScale = 1000u;
|
||||||
|
const uint64_t kFrameDuration = 33u;
|
||||||
|
scoped_ptr<MockRepresentationStateChangeListener> listener(
|
||||||
|
new MockRepresentationStateChangeListener());
|
||||||
|
EXPECT_CALL(*listener,
|
||||||
|
OnSetFrameRateForRepresentation(kFrameDuration, kTimeScale));
|
||||||
|
Representation representation(
|
||||||
|
ConvertToMediaInfo(kTestMediaInfo), MpdOptions(), kAnyRepresentationId,
|
||||||
|
listener.PassAs<RepresentationStateChangeListener>());
|
||||||
|
EXPECT_TRUE(representation.Init());
|
||||||
|
|
||||||
|
representation.SetSampleDuration(kFrameDuration);
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
@ -643,6 +678,77 @@ TEST_F(CommonMpdBuilderTest, AdapatationSetMaxFrameRate) {
|
||||||
ExpectAttributeNotSet("frameRate", adaptation_set_xml.get()));
|
ExpectAttributeNotSet("frameRate", adaptation_set_xml.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that (max)FrameRate can be set by calling
|
||||||
|
// Representation::SetSampleDuration().
|
||||||
|
TEST_F(CommonMpdBuilderTest,
|
||||||
|
SetAdaptationFrameRateUsingRepresentationSetSampleDuration) {
|
||||||
|
base::AtomicSequenceNumber sequence_counter;
|
||||||
|
// Note that frame duration is not set in the MediaInfos. It could be there
|
||||||
|
// and should not affect the behavior of the program.
|
||||||
|
// But to make it closer to a real live-profile use case,
|
||||||
|
// the frame duration is not set in the MediaInfo, instead it is set using
|
||||||
|
// SetSampleDuration().
|
||||||
|
const char k480pMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1'\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 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"
|
||||||
|
" 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, make sure that maxFrameRate nor frameRate are set because
|
||||||
|
// frame durations were not provided in the MediaInfo.
|
||||||
|
xml::ScopedXmlPtr<xmlNode>::type no_frame_rate(adaptation_set.GetXml());
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeNotSet("maxFrameRate", no_frame_rate.get()));
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeNotSet("frameRate", no_frame_rate.get()));
|
||||||
|
|
||||||
|
// Then set same frame duration for the representations. (Given that the
|
||||||
|
// time scales match).
|
||||||
|
const uint32_t kSameFrameDuration = 3u;
|
||||||
|
representation_480p->SetSampleDuration(kSameFrameDuration);
|
||||||
|
representation_360p->SetSampleDuration(kSameFrameDuration);
|
||||||
|
|
||||||
|
xml::ScopedXmlPtr<xmlNode>::type same_frame_rate(adaptation_set.GetXml());
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeNotSet("maxFrameRate", same_frame_rate.get()));
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeEqString("frameRate", "10/3", same_frame_rate.get()));
|
||||||
|
|
||||||
|
// Then set 480p to be 5fps (10/2) so that maxFrameRate is set.
|
||||||
|
const uint32_t k5FPSFrameDuration = 2;
|
||||||
|
COMPILE_ASSERT(k5FPSFrameDuration < kSameFrameDuration,
|
||||||
|
frame_duration_must_be_shorter_for_max_frame_rate);
|
||||||
|
representation_480p->SetSampleDuration(k5FPSFrameDuration);
|
||||||
|
|
||||||
|
xml::ScopedXmlPtr<xmlNode>::type max_frame_rate(adaptation_set.GetXml());
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeEqString("maxFrameRate", "10/2", max_frame_rate.get()));
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
ExpectAttributeNotSet("frameRate", max_frame_rate.get()));
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that if the picture aspect ratio of all the Representations are the
|
// Verify that if the picture aspect ratio of all the Representations are the
|
||||||
// same, @par attribute is present.
|
// same, @par attribute is present.
|
||||||
TEST_F(CommonMpdBuilderTest, AdaptationSetParAllSame) {
|
TEST_F(CommonMpdBuilderTest, AdaptationSetParAllSame) {
|
||||||
|
|
Loading…
Reference in New Issue