Wire up Representation::SetSampleDuration() to update AdaptationSet frame rate

- Representation::SetSampleDuration() calls
  AdaptatoinSet::OnSetFrameRateForRepresentation() via
  RepresentationStateChangeListener.

Change-Id: I4a76c57798c9b6dc1e84ba47336ed1c78fbabfd6
This commit is contained in:
Rintaro Kuroiwa 2015-07-31 12:38:50 -07:00 committed by Gerrit Code Review
parent 336435fcca
commit ab8f64ea2c
3 changed files with 179 additions and 14 deletions

View File

@ -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

View File

@ -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);

View File

@ -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) {