Commit b6e2be31 authored by hubbe@chromium.org's avatar hubbe@chromium.org

Cast: Only ACK decododable frames

Also, add an optimization to not decode frames if we are behind and multiple frames are currently decodable.

Review URL: https://codereview.chromium.org/289483003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272709 0039d316-1c4b-4281-b951-d872f2087c98
parent 8304f61a
......@@ -169,19 +169,29 @@ void AudioReceiver::EmitAvailableEncodedFrames() {
scoped_ptr<transport::EncodedFrame> encoded_frame(
new transport::EncodedFrame());
bool is_consecutively_next_frame = false;
if (!framer_.GetEncodedAudioFrame(encoded_frame.get(),
&is_consecutively_next_frame)) {
bool have_multiple_complete_frames = false;
if (!framer_.GetEncodedFrame(encoded_frame.get(),
&is_consecutively_next_frame,
&have_multiple_complete_frames)) {
VLOG(1) << "Wait for more audio packets to produce a completed frame.";
return; // OnReceivedPayloadData() will invoke this method in the future.
}
const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
const base::TimeTicks playout_time =
GetPlayoutTime(now, encoded_frame->rtp_timestamp);
// If we have multiple decodable frames, and the current frame is
// too old, then skip it and decode the next frame instead.
if (have_multiple_complete_frames && now > playout_time) {
framer_.ReleaseFrame(encoded_frame->frame_id);
continue;
}
// If |framer_| has a frame ready that is out of sequence, examine the
// playout time to determine whether it's acceptable to continue, thereby
// skipping one or more frames. Skip if the missing frame wouldn't complete
// playing before the start of playback of the available frame.
const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
const base::TimeTicks playout_time =
GetPlayoutTime(now, encoded_frame->rtp_timestamp);
if (!is_consecutively_next_frame) {
// TODO(miu): Also account for expected decode time here?
const base::TimeTicks earliest_possible_end_time_of_missing_frame =
......
......@@ -23,7 +23,6 @@ CastMessageBuilder::CastMessageBuilder(
decoder_faster_than_max_frame_rate_(decoder_faster_than_max_frame_rate),
max_unacked_frames_(max_unacked_frames),
cast_msg_(media_ssrc),
waiting_for_key_frame_(true),
slowing_down_ack_(false),
acked_last_frame_(true),
last_acked_frame_id_(kStartFrameId) {
......@@ -32,67 +31,61 @@ CastMessageBuilder::CastMessageBuilder(
CastMessageBuilder::~CastMessageBuilder() {}
void CastMessageBuilder::CompleteFrameReceived(uint32 frame_id,
bool is_key_frame) {
void CastMessageBuilder::CompleteFrameReceived(uint32 frame_id) {
DCHECK_GE(static_cast<int32>(frame_id - last_acked_frame_id_), 0);
VLOG(2) << "CompleteFrameReceived: " << frame_id;
if (last_update_time_.is_null()) {
// Our first update.
last_update_time_ = clock_->NowTicks();
}
if (waiting_for_key_frame_) {
if (!is_key_frame) {
// Ignore that we have received this complete frame since we are
// waiting on a key frame.
return;
}
waiting_for_key_frame_ = false;
cast_msg_.missing_frames_and_packets_.clear();
cast_msg_.ack_frame_id_ = frame_id;
last_update_time_ = clock_->NowTicks();
// We might have other complete frames waiting after we receive the last
// packet in the key-frame.
UpdateAckMessage();
} else {
if (!UpdateAckMessage())
return;
BuildPacketList();
if (!UpdateAckMessage(frame_id)) {
return;
}
BuildPacketList();
// Send cast message.
VLOG(2) << "Send cast message Ack:" << static_cast<int>(frame_id);
cast_feedback_->CastFeedback(cast_msg_);
}
bool CastMessageBuilder::UpdateAckMessage() {
bool CastMessageBuilder::UpdateAckMessage(uint32 frame_id) {
if (!decoder_faster_than_max_frame_rate_) {
int complete_frame_count = frame_id_map_->NumberOfCompleteFrames();
if (complete_frame_count > max_unacked_frames_) {
// We have too many frames pending in our framer; slow down ACK.
slowing_down_ack_ = true;
if (!slowing_down_ack_) {
slowing_down_ack_ = true;
ack_queue_.push_back(last_acked_frame_id_);
}
} else if (complete_frame_count <= 1) {
// We are down to one or less frames in our framer; ACK normally.
slowing_down_ack_ = false;
ack_queue_.clear();
}
}
if (slowing_down_ack_) {
// We are slowing down acknowledgment by acknowledging every other frame.
if (acked_last_frame_) {
acked_last_frame_ = false;
} else {
acked_last_frame_ = true;
last_acked_frame_id_++;
// Note: frame skipping and slowdown ACK is not supported at the same
// time; and it's not needed since we can skip frames to catch up.
}
} else {
uint32 frame_id = frame_id_map_->LastContinuousFrame();
// Is it a new frame?
if (last_acked_frame_id_ == frame_id)
// Note: frame skipping and slowdown ACK is not supported at the same
// time; and it's not needed since we can skip frames to catch up.
if (!ack_queue_.empty() && ack_queue_.back() == frame_id) {
return false;
}
ack_queue_.push_back(frame_id);
if (!acked_last_frame_) {
ack_queue_.pop_front();
}
frame_id = ack_queue_.front();
}
last_acked_frame_id_ = frame_id;
acked_last_frame_ = true;
acked_last_frame_ = false;
// Is it a new frame?
if (last_acked_frame_id_ == frame_id) {
return false;
}
acked_last_frame_ = true;
last_acked_frame_id_ = frame_id;
cast_msg_.ack_frame_id_ = last_acked_frame_id_;
cast_msg_.missing_frames_and_packets_.clear();
last_update_time_ = clock_->NowTicks();
......@@ -120,7 +113,6 @@ void CastMessageBuilder::UpdateCastMessage() {
}
void CastMessageBuilder::Reset() {
waiting_for_key_frame_ = true;
cast_msg_.ack_frame_id_ = kStartFrameId;
cast_msg_.missing_frames_and_packets_.clear();
time_last_nacked_map_.clear();
......@@ -142,7 +134,8 @@ bool CastMessageBuilder::UpdateCastMessageInternal(RtcpCastMessage* message) {
}
last_update_time_ = now;
UpdateAckMessage(); // Needed to cover when a frame is skipped.
// Needed to cover when a frame is skipped.
UpdateAckMessage(last_acked_frame_id_);
BuildPacketList();
message->Copy(cast_msg_);
return true;
......
......@@ -7,6 +7,7 @@
#ifndef MEDIA_CAST_FRAMER_CAST_MESSAGE_BUILDER_H_
#define MEDIA_CAST_FRAMER_CAST_MESSAGE_BUILDER_H_
#include <deque>
#include <map>
#include "media/cast/framer/frame_id_map.h"
......@@ -30,13 +31,13 @@ class CastMessageBuilder {
int max_unacked_frames);
~CastMessageBuilder();
void CompleteFrameReceived(uint32 frame_id, bool is_key_frame);
void CompleteFrameReceived(uint32 frame_id);
bool TimeToSendNextCastMessage(base::TimeTicks* time_to_send);
void UpdateCastMessage();
void Reset();
private:
bool UpdateAckMessage();
bool UpdateAckMessage(uint32 frame_id);
void BuildPacketList();
bool UpdateCastMessageInternal(RtcpCastMessage* message);
......@@ -51,13 +52,13 @@ class CastMessageBuilder {
RtcpCastMessage cast_msg_;
base::TimeTicks last_update_time_;
bool waiting_for_key_frame_;
TimeLastNackMap time_last_nacked_map_;
bool slowing_down_ack_;
bool acked_last_frame_;
uint32 last_acked_frame_id_;
std::deque<uint32> ack_queue_;
DISALLOW_COPY_AND_ASSIGN(CastMessageBuilder);
};
......
......@@ -112,8 +112,7 @@ class CastMessageBuilderTest : public ::testing::Test {
void InsertPacket() {
PacketType packet_type = frame_id_map_.InsertPacket(rtp_header_);
if (packet_type == kNewPacketCompletingFrame) {
cast_msg_builder_->CompleteFrameReceived(rtp_header_.frame_id,
rtp_header_.is_key_frame);
cast_msg_builder_->CompleteFrameReceived(rtp_header_.frame_id);
}
cast_msg_builder_->UpdateCastMessage();
}
......@@ -136,26 +135,6 @@ class CastMessageBuilderTest : public ::testing::Test {
DISALLOW_COPY_AND_ASSIGN(CastMessageBuilderTest);
};
TEST_F(CastMessageBuilderTest, StartWithAKeyFrame) {
SetFrameIds(3, 2);
SetPacketId(0);
SetMaxPacketId(0);
InsertPacket();
// Should not trigger ack.
EXPECT_FALSE(feedback_.triggered());
SetFrameIds(5, 5);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(true);
InsertPacket();
frame_id_map_.RemoveOldFrames(5); // Simulate 5 being pulled for rendering.
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
cast_msg_builder_->UpdateCastMessage();
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(5u, feedback_.last_frame_acked());
}
TEST_F(CastMessageBuilderTest, OneFrameNackList) {
SetFrameIds(0, 0);
SetPacketId(4);
......@@ -187,31 +166,6 @@ TEST_F(CastMessageBuilderTest, CompleteFrameMissing) {
EXPECT_EQ(kRtcpCastAllPacketsLost, feedback_.num_missing_packets(1));
}
TEST_F(CastMessageBuilderTest, FastForwardAck) {
SetFrameIds(1, 0);
SetPacketId(0);
SetMaxPacketId(0);
InsertPacket();
EXPECT_FALSE(feedback_.triggered());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
SetFrameIds(2, 1);
SetPacketId(0);
SetMaxPacketId(0);
InsertPacket();
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(kStartFrameId, feedback_.last_frame_acked());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
SetFrameIds(0, 0);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(true);
InsertPacket();
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(2u, feedback_.last_frame_acked());
}
TEST_F(CastMessageBuilderTest, RemoveOldFrames) {
SetFrameIds(1, 0);
SetPacketId(0);
......@@ -232,7 +186,7 @@ TEST_F(CastMessageBuilderTest, RemoveOldFrames) {
SetMaxPacketId(5);
InsertPacket();
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(kStartFrameId, feedback_.last_frame_acked());
EXPECT_EQ(2u, feedback_.last_frame_acked());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
SetFrameIds(5, 5);
......@@ -260,42 +214,6 @@ TEST_F(CastMessageBuilderTest, RemoveOldFrames) {
EXPECT_EQ(5u, feedback_.last_frame_acked());
}
TEST_F(CastMessageBuilderTest, WrapFastForward) {
SetFrameIds(254, 254);
SetPacketId(0);
SetMaxPacketId(1);
SetKeyFrame(true);
InsertPacket();
EXPECT_FALSE(feedback_.triggered());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
SetFrameIds(255, 254);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(false);
InsertPacket();
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(253u, feedback_.last_frame_acked());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
SetFrameIds(256, 255);
SetPacketId(0);
SetMaxPacketId(0);
SetKeyFrame(false);
InsertPacket();
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(253u, feedback_.last_frame_acked());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
SetFrameIds(254, 254);
SetPacketId(1);
SetMaxPacketId(1);
SetKeyFrame(true);
InsertPacket();
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(256u, feedback_.last_frame_acked());
}
TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacket) {
SetFrameIds(0, 0);
SetPacketId(0);
......@@ -404,7 +322,7 @@ TEST_F(CastMessageBuilderTest, BasicRps) {
SetKeyFrame(false);
InsertPacket();
EXPECT_TRUE(feedback_.triggered());
EXPECT_EQ(0u, feedback_.last_frame_acked());
EXPECT_EQ(3u, feedback_.last_frame_acked());
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
frame_id_map_.RemoveOldFrames(3); // Simulate 3 being pulled for rendering.
......@@ -480,16 +398,19 @@ TEST_F(CastMessageBuilderTest, SlowDownAck) {
// We should now have entered the slowdown ACK state.
uint32 expected_frame_id = 1;
for (; frame_id < 10; ++frame_id) {
if (frame_id % 2)
if (frame_id % 2) {
++expected_frame_id;
EXPECT_TRUE(feedback_.triggered());
EXPECT_TRUE(feedback_.triggered());
} else {
EXPECT_FALSE(feedback_.triggered());
}
EXPECT_EQ(expected_frame_id, feedback_.last_frame_acked());
SetFrameIds(frame_id, frame_id - 1);
InsertPacket();
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
}
EXPECT_TRUE(feedback_.triggered());
EXPECT_FALSE(feedback_.triggered());
EXPECT_EQ(expected_frame_id, feedback_.last_frame_acked());
// Simulate frame_id being pulled for rendering.
......
......@@ -137,6 +137,22 @@ bool FrameIdMap::NextContinuousFrame(uint32* frame_id) const {
return false;
}
bool FrameIdMap::HaveMultipleDecodableFrames() const {
// Find the oldest decodable frame.
FrameMap::const_iterator it;
bool found_one = false;
for (it = frame_map_.begin(); it != frame_map_.end(); ++it) {
if (it->second->Complete() && DecodableFrame(it->second.get())) {
if (found_one) {
return true;
} else {
found_one = true;
}
}
}
return false;
}
uint32 FrameIdMap::LastContinuousFrame() const {
uint32 last_continuous_frame_id = last_released_frame_;
uint32 next_expected_frame = last_released_frame_;
......@@ -157,43 +173,16 @@ uint32 FrameIdMap::LastContinuousFrame() const {
return last_continuous_frame_id;
}
bool FrameIdMap::NextAudioFrameAllowingMissingFrames(uint32* frame_id) const {
// First check if we have continuous frames.
if (NextContinuousFrame(frame_id))
return true;
// Find the oldest frame.
FrameMap::const_iterator it_best_match = frame_map_.end();
FrameMap::const_iterator it;
// Find first complete frame.
for (it = frame_map_.begin(); it != frame_map_.end(); ++it) {
if (it->second->Complete()) {
it_best_match = it;
break;
}
}
if (it_best_match == frame_map_.end())
return false; // No complete frame.
++it;
for (; it != frame_map_.end(); ++it) {
if (it->second->Complete() &&
IsOlderFrameId(it->first, it_best_match->first)) {
it_best_match = it;
}
}
*frame_id = it_best_match->first;
return true;
}
bool FrameIdMap::NextVideoFrameAllowingSkippingFrames(uint32* frame_id) const {
bool FrameIdMap::NextFrameAllowingSkippingFrames(uint32* frame_id) const {
// Find the oldest decodable frame.
FrameMap::const_iterator it_best_match = frame_map_.end();
FrameMap::const_iterator it;
for (it = frame_map_.begin(); it != frame_map_.end(); ++it) {
if (it->second->Complete() && DecodableVideoFrame(it->second.get())) {
it_best_match = it;
if (it->second->Complete() && DecodableFrame(it->second.get())) {
if (it_best_match == frame_map_.end() ||
IsOlderFrameId(it->first, it_best_match->first)) {
it_best_match = it;
}
}
}
if (it_best_match == frame_map_.end())
......@@ -237,11 +226,14 @@ bool FrameIdMap::ContinuousFrame(FrameInfo* frame) const {
return static_cast<uint32>(last_released_frame_ + 1) == frame->frame_id();
}
bool FrameIdMap::DecodableVideoFrame(FrameInfo* frame) const {
bool FrameIdMap::DecodableFrame(FrameInfo* frame) const {
if (frame->is_key_frame())
return true;
if (waiting_for_key_ && !frame->is_key_frame())
return false;
// Self-reference?
if (frame->referenced_frame_id() == frame->frame_id())
return true;
// Current frame is not necessarily referencing the last frame.
// Do we have the reference frame?
......
......@@ -64,8 +64,8 @@ class FrameIdMap {
bool NextContinuousFrame(uint32* frame_id) const;
uint32 LastContinuousFrame() const;
bool NextAudioFrameAllowingMissingFrames(uint32* frame_id) const;
bool NextVideoFrameAllowingSkippingFrames(uint32* frame_id) const;
bool NextFrameAllowingSkippingFrames(uint32* frame_id) const;
bool HaveMultipleDecodableFrames() const;
int NumberOfCompleteFrames() const;
void GetMissingPackets(uint32 frame_id,
......@@ -74,7 +74,7 @@ class FrameIdMap {
private:
bool ContinuousFrame(FrameInfo* frame) const;
bool DecodableVideoFrame(FrameInfo* frame) const;
bool DecodableFrame(FrameInfo* frame) const;
FrameMap frame_map_;
bool waiting_for_key_;
......
......@@ -58,42 +58,15 @@ bool Framer::InsertPacket(const uint8* payload_data,
it->second->InsertPacket(payload_data, payload_size, rtp_header);
}
bool complete = (packet_type == kNewPacketCompletingFrame);
if (complete) {
// ACK as soon as possible.
VLOG(2) << "Complete frame " << static_cast<int>(rtp_header.frame_id);
cast_msg_builder_->CompleteFrameReceived(rtp_header.frame_id,
rtp_header.is_key_frame);
}
return complete;
return packet_type == kNewPacketCompletingFrame;
}
// This does not release the frame.
bool Framer::GetEncodedAudioFrame(transport::EncodedFrame* audio_frame,
bool* next_frame) {
uint32 frame_id;
// Find frame id.
if (frame_id_map_.NextContinuousFrame(&frame_id)) {
// We have our next frame.
*next_frame = true;
} else {
if (!frame_id_map_.NextAudioFrameAllowingMissingFrames(&frame_id)) {
return false;
}
*next_frame = false;
}
bool Framer::GetEncodedFrame(transport::EncodedFrame* frame,
bool* next_frame,
bool* have_multiple_decodable_frames) {
*have_multiple_decodable_frames = frame_id_map_.HaveMultipleDecodableFrames();
ConstFrameIterator it = frames_.find(frame_id);
DCHECK(it != frames_.end());
if (it == frames_.end())
return false;
return it->second->AssembleEncodedFrame(audio_frame);
}
// This does not release the frame.
bool Framer::GetEncodedVideoFrame(transport::EncodedFrame* video_frame,
bool* next_frame) {
uint32 frame_id;
// Find frame id.
if (frame_id_map_.NextContinuousFrame(&frame_id)) {
......@@ -104,18 +77,23 @@ bool Framer::GetEncodedVideoFrame(transport::EncodedFrame* video_frame,
if (!decoder_faster_than_max_frame_rate_)
return false;
if (!frame_id_map_.NextVideoFrameAllowingSkippingFrames(&frame_id)) {
if (!frame_id_map_.NextFrameAllowingSkippingFrames(&frame_id)) {
return false;
}
*next_frame = false;
}
if (*next_frame) {
VLOG(2) << "ACK frame " << frame_id;
cast_msg_builder_->CompleteFrameReceived(frame_id);
}
ConstFrameIterator it = frames_.find(frame_id);
DCHECK(it != frames_.end());
if (it == frames_.end())
return false;
return it->second->AssembleEncodedFrame(video_frame);
return it->second->AssembleEncodedFrame(frame);
}
void Framer::Reset() {
......
......@@ -40,15 +40,14 @@ class Framer {
const RtpCastHeader& rtp_header,
bool* duplicate);
// Extracts a complete encoded frame - will only return a complete continuous
// frame.
// Returns false if the frame does not exist or if the frame is not complete
// within the given time frame.
bool GetEncodedVideoFrame(transport::EncodedFrame* video_frame,
bool* next_frame);
bool GetEncodedAudioFrame(transport::EncodedFrame* audio_frame,
bool* next_frame);
// Extracts a complete encoded frame - will only return a complete and
// decodable frame. Returns false if no such frames exist.
// |next_frame| will be set to true if the returned frame is the very
// next frame. |have_multiple_complete_frames| will be set to true
// if there are more decodadble frames available.
bool GetEncodedFrame(transport::EncodedFrame* video_frame,
bool* next_frame,
bool* have_multiple_complete_frames);
void ReleaseFrame(uint32 frame_id);
......
This diff is collapsed.
......@@ -155,19 +155,30 @@ void VideoReceiver::EmitAvailableEncodedFrames() {
scoped_ptr<transport::EncodedFrame> encoded_frame(
new transport::EncodedFrame());
bool is_consecutively_next_frame = false;
if (!framer_.GetEncodedVideoFrame(encoded_frame.get(),
&is_consecutively_next_frame)) {
bool have_multiple_complete_frames = false;
if (!framer_.GetEncodedFrame(encoded_frame.get(),
&is_consecutively_next_frame,
&have_multiple_complete_frames)) {
VLOG(1) << "Wait for more video packets to produce a completed frame.";
return; // OnReceivedPayloadData() will invoke this method in the future.
}
const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
const base::TimeTicks playout_time =
GetPlayoutTime(now, encoded_frame->rtp_timestamp);
// If we have multiple decodable frames, and the current frame is
// too old, then skip it and decode the next frame instead.
if (have_multiple_complete_frames && now > playout_time) {
framer_.ReleaseFrame(encoded_frame->frame_id);
continue;
}
// If |framer_| has a frame ready that is out of sequence, examine the
// playout time to determine whether it's acceptable to continue, thereby
// skipping one or more frames. Skip if the missing frame wouldn't complete
// playing before the start of playback of the available frame.
const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
const base::TimeTicks playout_time =
GetPlayoutTime(now, encoded_frame->rtp_timestamp);
if (!is_consecutively_next_frame) {
// TODO(miu): Also account for expected decode time here?
const base::TimeTicks earliest_possible_end_time_of_missing_frame =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment