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);
|
||||
}
|
||||
|
||||
virtual void OnSetFrameRateForRepresentation(uint32_t frame_duration,
|
||||
uint32_t timescale) OVERRIDE {
|
||||
adaptation_set_->OnSetFrameRateForRepresentation(representation_id_,
|
||||
frame_duration, timescale);
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t representation_id_;
|
||||
AdaptationSet* const adaptation_set_;
|
||||
|
@ -653,12 +659,8 @@ Representation* AdaptationSet::AddRepresentation(const MediaInfo& media_info) {
|
|||
video_widths_.insert(video_info.width());
|
||||
video_heights_.insert(video_info.height());
|
||||
|
||||
if (video_info.has_time_scale() && video_info.has_frame_duration()) {
|
||||
video_frame_rates_[static_cast<double>(video_info.time_scale()) /
|
||||
video_info.frame_duration()] =
|
||||
base::IntToString(video_info.time_scale()) + "/" +
|
||||
base::IntToString(video_info.frame_duration());
|
||||
}
|
||||
if (video_info.has_time_scale() && video_info.has_frame_duration())
|
||||
RecordFrameRate(video_info.frame_duration(), video_info.time_scale());
|
||||
|
||||
AddPictureAspectRatio(video_info, &picture_aspect_ratio_);
|
||||
}
|
||||
|
@ -772,6 +774,14 @@ void AdaptationSet::OnNewSegmentForRepresentation(uint32_t representation_id,
|
|||
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) {
|
||||
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(
|
||||
const MediaInfo& media_info,
|
||||
const MpdOptions& mpd_options,
|
||||
|
@ -949,9 +971,15 @@ void Representation::AddNewSegment(uint64_t start_time,
|
|||
}
|
||||
|
||||
void Representation::SetSampleDuration(uint32_t sample_duration) {
|
||||
// Assume single video info.
|
||||
if (media_info_.has_video_info())
|
||||
base::AutoLock scoped_lock(lock_);
|
||||
|
||||
if (media_info_.has_video_info()) {
|
||||
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
|
||||
|
|
|
@ -237,6 +237,22 @@ class AdaptationSet {
|
|||
uint64_t start_time,
|
||||
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:
|
||||
/// @param adaptation_set_id is an ID number for this AdaptationSet.
|
||||
/// @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, ForceSetSubSegmentAlignment);
|
||||
FRIEND_TEST_ALL_PREFIXES(DynamicMpdBuilderTest, SegmentAlignment);
|
||||
FRIEND_TEST_ALL_PREFIXES(
|
||||
CommonMpdBuilderTest,
|
||||
SetAdaptationFrameRateUsingRepresentationSetSampleDuration);
|
||||
|
||||
// Gets the earliest, normalized segment timestamp. Returns true if
|
||||
// successful, false otherwise.
|
||||
|
@ -301,6 +320,9 @@ class AdaptationSet {
|
|||
uint64_t start_time,
|
||||
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<Representation*> representations_;
|
||||
::STLElementDeleter<std::list<Representation*> > representations_deleter_;
|
||||
|
@ -360,8 +382,6 @@ class 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() {}
|
||||
|
@ -373,6 +393,13 @@ class RepresentationStateChangeListener {
|
|||
/// @param duration is the duration of the new segment.
|
||||
virtual void OnNewSegmentForRepresentation(uint64_t start_time,
|
||||
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
|
||||
|
@ -412,7 +439,7 @@ class Representation {
|
|||
uint64_t size);
|
||||
|
||||
/// 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
|
||||
/// initialized.
|
||||
/// @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, CheckRepresentationId);
|
||||
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, SetSampleDuration);
|
||||
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest,
|
||||
RepresentationStateChangeListener);
|
||||
FRIEND_TEST_ALL_PREFIXES(
|
||||
CommonMpdBuilderTest,
|
||||
RepresentationStateChangeListenerOnNewSegmentForRepresentation);
|
||||
FRIEND_TEST_ALL_PREFIXES(
|
||||
CommonMpdBuilderTest,
|
||||
RepresentationStateChangeListenerOnSetFrameRateForRepresentation);
|
||||
|
||||
bool AddLiveInfo(xml::RepresentationXmlNode* representation);
|
||||
|
||||
|
|
|
@ -87,6 +87,9 @@ class MockRepresentationStateChangeListener
|
|||
|
||||
MOCK_METHOD2(OnNewSegmentForRepresentation,
|
||||
void(uint64_t start_time, uint64_t duration));
|
||||
|
||||
MOCK_METHOD2(OnSetFrameRateForRepresentation,
|
||||
void(uint32_t frame_duration, uint32_t timescale));
|
||||
};
|
||||
} // namespace
|
||||
|
||||
|
@ -435,7 +438,8 @@ TEST_F(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml) {
|
|||
|
||||
// Make sure RepresentationStateChangeListener::OnNewSegmentForRepresentation()
|
||||
// is called.
|
||||
TEST_F(CommonMpdBuilderTest, RepresentationStateChangeListener) {
|
||||
TEST_F(CommonMpdBuilderTest,
|
||||
RepresentationStateChangeListenerOnNewSegmentForRepresentation) {
|
||||
const char kTestMediaInfo[] =
|
||||
"video_info {\n"
|
||||
" codec: 'avc1'\n"
|
||||
|
@ -462,6 +466,37 @@ TEST_F(CommonMpdBuilderTest, RepresentationStateChangeListener) {
|
|||
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
|
||||
// MediaInfo.
|
||||
TEST_F(CommonMpdBuilderTest, CheckAdaptationSetVideoContentType) {
|
||||
|
@ -643,6 +678,77 @@ TEST_F(CommonMpdBuilderTest, AdapatationSetMaxFrameRate) {
|
|||
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
|
||||
// same, @par attribute is present.
|
||||
TEST_F(CommonMpdBuilderTest, AdaptationSetParAllSame) {
|
||||
|
|
Loading…
Reference in New Issue