Commit b836b712 authored by hubbe's avatar hubbe Committed by Commit bot

Cast: Make vp8 3-buffer mode work

Cast was originally intended to tell vp8 to only reference buffers which have been ACKed.
However, that mode was buggy and disabled. This CL fixes it again. The three-buffer mode
lets the receiver drop frames and continue, which should be quite useful if we want lower
latency.

BUG=351596

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

Cr-Commit-Position: refs/heads/master@{#293243}
parent 2551612c
...@@ -83,11 +83,6 @@ bool Framer::GetEncodedFrame(EncodedFrame* frame, ...@@ -83,11 +83,6 @@ bool Framer::GetEncodedFrame(EncodedFrame* frame,
*next_frame = 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); ConstFrameIterator it = frames_.find(frame_id);
DCHECK(it != frames_.end()); DCHECK(it != frames_.end());
if (it == frames_.end()) if (it == frames_.end())
...@@ -96,6 +91,11 @@ bool Framer::GetEncodedFrame(EncodedFrame* frame, ...@@ -96,6 +91,11 @@ bool Framer::GetEncodedFrame(EncodedFrame* frame,
return it->second->AssembleEncodedFrame(frame); return it->second->AssembleEncodedFrame(frame);
} }
void Framer::AckFrame(uint32 frame_id) {
VLOG(2) << "ACK frame " << frame_id;
cast_msg_builder_->CompleteFrameReceived(frame_id);
}
void Framer::Reset() { void Framer::Reset() {
frame_id_map_.Clear(); frame_id_map_.Clear();
frames_.clear(); frames_.clear();
......
...@@ -49,6 +49,9 @@ class Framer { ...@@ -49,6 +49,9 @@ class Framer {
bool* next_frame, bool* next_frame,
bool* have_multiple_complete_frames); bool* have_multiple_complete_frames);
// TODO(hubbe): Move this elsewhere.
void AckFrame(uint32 frame_id);
void ReleaseFrame(uint32 frame_id); void ReleaseFrame(uint32 frame_id);
// Reset framer state to original state and flush all pending buffers. // Reset framer state to original state and flush all pending buffers.
......
...@@ -216,9 +216,10 @@ void FrameReceiver::EmitAvailableEncodedFrames() { ...@@ -216,9 +216,10 @@ void FrameReceiver::EmitAvailableEncodedFrames() {
// skipping one or more frames. Skip if the missing frame wouldn't complete // skipping one or more frames. Skip if the missing frame wouldn't complete
// playing before the start of playback of the available frame. // playing before the start of playback of the available frame.
if (!is_consecutively_next_frame) { if (!is_consecutively_next_frame) {
// TODO(miu): Also account for expected decode time here? // This assumes that decoding takes as long as playing, which might
// not be true.
const base::TimeTicks earliest_possible_end_time_of_missing_frame = const base::TimeTicks earliest_possible_end_time_of_missing_frame =
now + expected_frame_duration_; now + expected_frame_duration_ * 2;
if (earliest_possible_end_time_of_missing_frame < playout_time) { if (earliest_possible_end_time_of_missing_frame < playout_time) {
VLOG(1) << "Wait for next consecutive frame instead of skipping."; VLOG(1) << "Wait for next consecutive frame instead of skipping.";
if (!is_waiting_for_consecutive_frame_) { if (!is_waiting_for_consecutive_frame_) {
...@@ -234,6 +235,11 @@ void FrameReceiver::EmitAvailableEncodedFrames() { ...@@ -234,6 +235,11 @@ void FrameReceiver::EmitAvailableEncodedFrames() {
} }
} }
// At this point, we have the complete next frame, or a decodable
// frame from somewhere later in the stream, AND we have given up
// on waiting for any frames in between, so now we can ACK the frame.
framer_.AckFrame(encoded_frame->frame_id);
// Decrypt the payload data in the frame, if crypto is being used. // Decrypt the payload data in the frame, if crypto is being used.
if (decryptor_.is_activated()) { if (decryptor_.is_activated()) {
std::string decrypted_data; std::string decrypted_data;
......
...@@ -36,15 +36,14 @@ class TestVideoEncoderCallback ...@@ -36,15 +36,14 @@ class TestVideoEncoderCallback
void DeliverEncodedVideoFrame( void DeliverEncodedVideoFrame(
scoped_ptr<EncodedFrame> encoded_frame) { scoped_ptr<EncodedFrame> encoded_frame) {
if (expected_frame_id_ == expected_last_referenced_frame_id_) { if (expected_frame_id_ != expected_last_referenced_frame_id_) {
EXPECT_EQ(EncodedFrame::KEY, encoded_frame->dependency);
} else {
EXPECT_EQ(EncodedFrame::DEPENDENT, EXPECT_EQ(EncodedFrame::DEPENDENT,
encoded_frame->dependency); encoded_frame->dependency);
} }
EXPECT_EQ(expected_frame_id_, encoded_frame->frame_id); EXPECT_EQ(expected_frame_id_, encoded_frame->frame_id);
EXPECT_EQ(expected_last_referenced_frame_id_, EXPECT_EQ(expected_last_referenced_frame_id_,
encoded_frame->referenced_frame_id); encoded_frame->referenced_frame_id)
<< "frame id: " << expected_frame_id_;
EXPECT_EQ(expected_capture_time_, encoded_frame->reference_time); EXPECT_EQ(expected_capture_time_, encoded_frame->reference_time);
} }
......
...@@ -17,32 +17,18 @@ namespace cast { ...@@ -17,32 +17,18 @@ namespace cast {
static const uint32 kMinIntra = 300; static const uint32 kMinIntra = 300;
static int ComputeMaxNumOfRepeatedBuffers(int max_unacked_frames) {
if (max_unacked_frames > kNumberOfVp8VideoBuffers)
return (max_unacked_frames - 1) / kNumberOfVp8VideoBuffers;
return 0;
}
Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config, Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config,
int max_unacked_frames) int max_unacked_frames)
: cast_config_(video_config), : cast_config_(video_config),
use_multiple_video_buffers_( use_multiple_video_buffers_(
cast_config_.max_number_of_video_buffers_used == cast_config_.max_number_of_video_buffers_used ==
kNumberOfVp8VideoBuffers), kNumberOfVp8VideoBuffers),
max_number_of_repeated_buffers_in_a_row_(
ComputeMaxNumOfRepeatedBuffers(max_unacked_frames)),
key_frame_requested_(true), key_frame_requested_(true),
first_frame_received_(false), first_frame_received_(false),
last_encoded_frame_id_(kStartFrameId), last_encoded_frame_id_(kStartFrameId),
number_of_repeated_buffers_(0) { last_acked_frame_id_(kStartFrameId),
// TODO(pwestin): we need to figure out how to synchronize the acking with the frame_id_to_reference_(kStartFrameId - 1),
// internal state of the encoder, ideally the encoder will tell if we can undroppable_frames_(0) {
// send another frame.
DCHECK(!use_multiple_video_buffers_ ||
max_number_of_repeated_buffers_in_a_row_ == 0)
<< "Invalid config";
// VP8 have 3 buffers available for prediction, with // VP8 have 3 buffers available for prediction, with
// max_number_of_video_buffers_used set to 1 we maximize the coding efficiency // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency
// however in this mode we can not skip frames in the receiver to catch up // however in this mode we can not skip frames in the receiver to catch up
...@@ -74,8 +60,8 @@ void Vp8Encoder::Initialize() { ...@@ -74,8 +60,8 @@ void Vp8Encoder::Initialize() {
NULL, IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL); NULL, IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL);
for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
acked_frame_buffers_[i] = true; buffer_state_[i].frame_id = kStartFrameId;
used_buffers_frame_id_[i] = kStartFrameId; buffer_state_[i].state = kBufferStartState;
} }
InitEncode(cast_config_.number_of_encode_threads); InitEncode(cast_config_.number_of_encode_threads);
} }
...@@ -160,8 +146,7 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, ...@@ -160,8 +146,7 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
buffer_to_update = kLastBuffer; buffer_to_update = kLastBuffer;
} else { } else {
// Reference all acked frames (buffers). // Reference all acked frames (buffers).
latest_frame_id_to_reference = GetLatestFrameIdToReference(); latest_frame_id_to_reference = GetCodecReferenceFlags(&flags);
GetCodecReferenceFlags(&flags);
buffer_to_update = GetNextBufferToUpdate(); buffer_to_update = GetNextBufferToUpdate();
GetCodecUpdateFlags(buffer_to_update, &flags); GetCodecUpdateFlags(buffer_to_update, &flags);
} }
...@@ -214,6 +199,7 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, ...@@ -214,6 +199,7 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
// Populate the encoded frame. // Populate the encoded frame.
encoded_image->frame_id = ++last_encoded_frame_id_; encoded_image->frame_id = ++last_encoded_frame_id_;
if (is_key_frame) { if (is_key_frame) {
// TODO(Hubbe): Replace "dependency" with a "bool is_key_frame".
encoded_image->dependency = EncodedFrame::KEY; encoded_image->dependency = EncodedFrame::KEY;
encoded_image->referenced_frame_id = encoded_image->frame_id; encoded_image->referenced_frame_id = encoded_image->frame_id;
} else { } else {
...@@ -228,108 +214,130 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, ...@@ -228,108 +214,130 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
key_frame_requested_ = false; key_frame_requested_ = false;
for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
used_buffers_frame_id_[i] = encoded_image->frame_id; buffer_state_[i].state = kBufferSent;
buffer_state_[i].frame_id = encoded_image->frame_id;
} }
// We can pick any buffer as last_used_vp8_buffer_ since we update
// them all.
last_used_vp8_buffer_ = buffer_to_update;
} else { } else {
if (buffer_to_update != kNoBuffer) { if (buffer_to_update != kNoBuffer) {
acked_frame_buffers_[buffer_to_update] = false; buffer_state_[buffer_to_update].state = kBufferSent;
used_buffers_frame_id_[buffer_to_update] = encoded_image->frame_id; buffer_state_[buffer_to_update].frame_id = encoded_image->frame_id;
last_used_vp8_buffer_ = buffer_to_update;
} }
} }
return true; return true;
} }
void Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) { uint32 Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) {
if (!use_multiple_video_buffers_) if (!use_multiple_video_buffers_)
return; return last_encoded_frame_id_ + 1;
// We need to reference something. const uint32 kMagicFrameOffset = 512;
DCHECK(acked_frame_buffers_[kAltRefBuffer] || // We set latest_frame_to_reference to an old frame so that
acked_frame_buffers_[kGoldenBuffer] || // IsNewerFrameId will work correctly.
acked_frame_buffers_[kLastBuffer]) uint32 latest_frame_to_reference =
<< "Invalid state"; last_encoded_frame_id_ - kMagicFrameOffset;
if (!acked_frame_buffers_[kAltRefBuffer]) { // Reference all acked frames.
*flags |= VP8_EFLAG_NO_REF_ARF; // TODO(hubbe): We may also want to allow references to the
} // last encoded frame, if that frame was assigned to a buffer.
if (!acked_frame_buffers_[kGoldenBuffer]) { for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
*flags |= VP8_EFLAG_NO_REF_GF; if (buffer_state_[i].state == kBufferAcked) {
if (IsNewerFrameId(buffer_state_[i].frame_id,
latest_frame_to_reference)) {
latest_frame_to_reference = buffer_state_[i].frame_id;
}
} else {
switch (i) {
case kAltRefBuffer:
*flags |= VP8_EFLAG_NO_REF_ARF;
break;
case kGoldenBuffer:
*flags |= VP8_EFLAG_NO_REF_GF;
break;
case kLastBuffer:
*flags |= VP8_EFLAG_NO_REF_LAST;
break;
}
}
} }
if (!acked_frame_buffers_[kLastBuffer]) {
*flags |= VP8_EFLAG_NO_REF_LAST; if (latest_frame_to_reference ==
last_encoded_frame_id_ - kMagicFrameOffset) {
// We have nothing to reference, it's kind of like a key frame,
// but doesn't reset buffers.
latest_frame_to_reference = last_encoded_frame_id_ + 1;
} }
return latest_frame_to_reference;
} }
uint32 Vp8Encoder::GetLatestFrameIdToReference() { Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() {
if (!use_multiple_video_buffers_) if (!use_multiple_video_buffers_)
return last_encoded_frame_id_; return kNoBuffer;
int latest_frame_id_to_reference = -1; // The goal here is to make sure that we always keep one ACKed
if (acked_frame_buffers_[kAltRefBuffer]) { // buffer while trying to get an ACK for a newer buffer as we go.
latest_frame_id_to_reference = used_buffers_frame_id_[kAltRefBuffer]; // Here are the rules for which buffer to select for update:
} // 1. If there is a buffer in state kStartState, use it.
if (acked_frame_buffers_[kGoldenBuffer]) { // 2. If there is a buffer other than the oldest buffer
if (latest_frame_id_to_reference == -1) { // which is Acked, use the oldest buffer.
latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer]; // 3. If there are Sent buffers which are older than
} else { // latest_acked_frame_, use the oldest one.
if (IsNewerFrameId(used_buffers_frame_id_[kGoldenBuffer], // 4. If all else fails, just overwrite the newest buffer,
latest_frame_id_to_reference)) { // but no more than 3 times in a row.
latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer]; // TODO(hubbe): Figure out if 3 is optimal.
} // Note, rule 1-3 describe cases where there is a "free" buffer
// that we can use. Rule 4 describes what happens when there is
// no free buffer available.
// Buffers, sorted from oldest frame to newest.
Vp8Encoder::Vp8Buffers buffers[kNumberOfVp8VideoBuffers];
for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
Vp8Encoder::Vp8Buffers buffer = static_cast<Vp8Encoder::Vp8Buffers>(i);
// Rule 1
if (buffer_state_[buffer].state == kBufferStartState) {
undroppable_frames_ = 0;
return buffer;
} }
buffers[buffer] = buffer;
} }
if (acked_frame_buffers_[kLastBuffer]) {
if (latest_frame_id_to_reference == -1) { // Sorting three elements with selection sort.
latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer]; for (int i = 0; i < kNumberOfVp8VideoBuffers - 1; i++) {
} else { for (int j = i + 1; j < kNumberOfVp8VideoBuffers; j++) {
if (IsNewerFrameId(used_buffers_frame_id_[kLastBuffer], if (IsOlderFrameId(buffer_state_[buffers[j]].frame_id,
latest_frame_id_to_reference)) { buffer_state_[buffers[i]].frame_id)) {
latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer]; std::swap(buffers[i], buffers[j]);
} }
} }
} }
DCHECK(latest_frame_id_to_reference != -1) << "Invalid state";
return static_cast<uint32>(latest_frame_id_to_reference);
}
Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() { // Rule 2
if (!use_multiple_video_buffers_) if (buffer_state_[buffers[1]].state == kBufferAcked ||
return kNoBuffer; buffer_state_[buffers[2]].state == kBufferAcked) {
undroppable_frames_ = 0;
return buffers[0];
}
// Update at most one buffer, except for key-frames. // Rule 3
for (int i = 0; i < kNumberOfVp8VideoBuffers; i++) {
if (buffer_state_[buffers[i]].state == kBufferSent &&
IsOlderFrameId(buffer_state_[buffers[i]].frame_id,
last_acked_frame_id_)) {
undroppable_frames_ = 0;
return buffers[i];
}
}
Vp8Buffers buffer_to_update = kNoBuffer; // Rule 4
if (number_of_repeated_buffers_ < max_number_of_repeated_buffers_in_a_row_) { if (undroppable_frames_ >= 3) {
// TODO(pwestin): experiment with this. The issue with only this change is undroppable_frames_ = 0;
// that we can end up with only 4 frames in flight when we expect 6. return kNoBuffer;
// buffer_to_update = last_used_vp8_buffer_;
buffer_to_update = kNoBuffer;
++number_of_repeated_buffers_;
} else { } else {
number_of_repeated_buffers_ = 0; undroppable_frames_++;
switch (last_used_vp8_buffer_) { return buffers[kNumberOfVp8VideoBuffers - 1];
case kAltRefBuffer:
buffer_to_update = kLastBuffer;
VLOG(1) << "VP8 update last buffer";
break;
case kLastBuffer:
buffer_to_update = kGoldenBuffer;
VLOG(1) << "VP8 update golden buffer";
break;
case kGoldenBuffer:
buffer_to_update = kAltRefBuffer;
VLOG(1) << "VP8 update alt-ref buffer";
break;
case kNoBuffer:
DCHECK(false) << "Invalid state";
break;
}
} }
return buffer_to_update;
} }
void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update, void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update,
...@@ -381,10 +389,14 @@ void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) { ...@@ -381,10 +389,14 @@ void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) {
VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id); VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id);
for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
if (frame_id == used_buffers_frame_id_[i]) { if (frame_id == buffer_state_[i].frame_id) {
acked_frame_buffers_[i] = true; buffer_state_[i].state = kBufferAcked;
break;
} }
} }
if (IsOlderFrameId(last_acked_frame_id_, frame_id)) {
last_acked_frame_id_ = frame_id;
}
} }
void Vp8Encoder::GenerateKeyFrame() { void Vp8Encoder::GenerateKeyFrame() {
......
...@@ -55,6 +55,16 @@ class Vp8Encoder : public SoftwareVideoEncoder { ...@@ -55,6 +55,16 @@ class Vp8Encoder : public SoftwareVideoEncoder {
kNoBuffer = 3 // Note: must be last. kNoBuffer = 3 // Note: must be last.
}; };
enum Vp8BufferState {
kBufferStartState,
kBufferSent,
kBufferAcked
};
struct BufferState {
uint32 frame_id;
Vp8BufferState state;
};
void InitEncode(int number_of_cores); void InitEncode(int number_of_cores);
// Calculate the max target in % for a keyframe. // Calculate the max target in % for a keyframe.
...@@ -63,11 +73,9 @@ class Vp8Encoder : public SoftwareVideoEncoder { ...@@ -63,11 +73,9 @@ class Vp8Encoder : public SoftwareVideoEncoder {
// Calculate which next Vp8 buffers to update with the next frame. // Calculate which next Vp8 buffers to update with the next frame.
Vp8Buffers GetNextBufferToUpdate(); Vp8Buffers GetNextBufferToUpdate();
// Calculate which previous frame to reference.
uint32 GetLatestFrameIdToReference();
// Get encoder flags for our referenced encoder buffers. // Get encoder flags for our referenced encoder buffers.
void GetCodecReferenceFlags(vpx_codec_flags_t* flags); // Return which previous frame to reference.
uint32 GetCodecReferenceFlags(vpx_codec_flags_t* flags);
// Get encoder flags for our encoder buffers to update with next frame. // Get encoder flags for our encoder buffers to update with next frame.
void GetCodecUpdateFlags(Vp8Buffers buffer_to_update, void GetCodecUpdateFlags(Vp8Buffers buffer_to_update,
...@@ -75,7 +83,6 @@ class Vp8Encoder : public SoftwareVideoEncoder { ...@@ -75,7 +83,6 @@ class Vp8Encoder : public SoftwareVideoEncoder {
const VideoSenderConfig cast_config_; const VideoSenderConfig cast_config_;
const bool use_multiple_video_buffers_; const bool use_multiple_video_buffers_;
const int max_number_of_repeated_buffers_in_a_row_;
// VP8 internal objects. // VP8 internal objects.
scoped_ptr<vpx_codec_enc_cfg_t> config_; scoped_ptr<vpx_codec_enc_cfg_t> config_;
...@@ -86,10 +93,10 @@ class Vp8Encoder : public SoftwareVideoEncoder { ...@@ -86,10 +93,10 @@ class Vp8Encoder : public SoftwareVideoEncoder {
bool first_frame_received_; bool first_frame_received_;
base::TimeDelta first_frame_timestamp_; base::TimeDelta first_frame_timestamp_;
uint32 last_encoded_frame_id_; uint32 last_encoded_frame_id_;
uint32 used_buffers_frame_id_[kNumberOfVp8VideoBuffers]; uint32 last_acked_frame_id_;
bool acked_frame_buffers_[kNumberOfVp8VideoBuffers]; uint32 frame_id_to_reference_;
Vp8Buffers last_used_vp8_buffer_; uint32 undroppable_frames_;
int number_of_repeated_buffers_; BufferState buffer_state_[kNumberOfVp8VideoBuffers];
// This is bound to the thread where Initialize() is called. // This is bound to the thread where Initialize() is called.
base::ThreadChecker thread_checker_; base::ThreadChecker thread_checker_;
......
...@@ -210,7 +210,7 @@ class LoopBackTransport : public PacketSender { ...@@ -210,7 +210,7 @@ class LoopBackTransport : public PacketSender {
const base::Closure& cb) OVERRIDE { const base::Closure& cb) OVERRIDE {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
if (!send_packets_) if (!send_packets_)
return false; return true;
bytes_sent_ += packet->data.size(); bytes_sent_ += packet->data.size();
if (drop_packets_belonging_to_odd_frames_) { if (drop_packets_belonging_to_odd_frames_) {
...@@ -406,7 +406,9 @@ class TestReceiverVideoCallback ...@@ -406,7 +406,9 @@ class TestReceiverVideoCallback
PopulateVideoFrame(expected_I420_frame.get(), PopulateVideoFrame(expected_I420_frame.get(),
expected_video_frame.start_value); expected_video_frame.start_value);
EXPECT_GE(I420PSNR(expected_I420_frame, video_frame), kVideoAcceptedPSNR); if (expected_video_frame.should_be_continuous) {
EXPECT_GE(I420PSNR(expected_I420_frame, video_frame), kVideoAcceptedPSNR);
}
EXPECT_NEAR( EXPECT_NEAR(
(playout_time - expected_video_frame.playout_time).InMillisecondsF(), (playout_time - expected_video_frame.playout_time).InMillisecondsF(),
...@@ -988,79 +990,14 @@ TEST_F(End2EndTest, DISABLED_StartSenderBeforeReceiver) { ...@@ -988,79 +990,14 @@ TEST_F(End2EndTest, DISABLED_StartSenderBeforeReceiver) {
EXPECT_EQ(10, test_receiver_video_callback_->number_times_called()); EXPECT_EQ(10, test_receiver_video_callback_->number_times_called());
} }
// This tests a network glitch lasting for 10 video frames. TEST_F(End2EndTest, DropEveryOtherFrame3Buffers) {
// Flaky. See crbug.com/351596. Configure(CODEC_VIDEO_VP8, CODEC_AUDIO_OPUS, kDefaultAudioSamplingRate, 3);
TEST_F(End2EndTest, DISABLED_GlitchWith3Buffers) { int target_delay = 300;
Configure(CODEC_VIDEO_VP8, CODEC_AUDIO_OPUS,
kDefaultAudioSamplingRate, 3);
video_sender_config_.target_playout_delay =
base::TimeDelta::FromMilliseconds(67);
video_receiver_config_.rtp_max_delay_ms = 67;
Create();
int video_start = kVideoStart;
base::TimeTicks capture_time;
// Frames will rendered on completion until the render time stabilizes, i.e.
// we got enough data.
const int frames_before_glitch = 20;
for (int i = 0; i < frames_before_glitch; ++i) {
capture_time = testing_clock_sender_->NowTicks();
SendVideoFrame(video_start, capture_time);
test_receiver_video_callback_->AddExpectedResult(
video_start,
video_sender_config_.width,
video_sender_config_.height,
capture_time + base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs),
true);
cast_receiver_->RequestDecodedVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
RunTasks(kFrameTimerMs);
video_start++;
}
// Introduce a glitch lasting for 10 frames.
sender_to_receiver_.SetSendPackets(false);
for (int i = 0; i < 10; ++i) {
capture_time = testing_clock_sender_->NowTicks();
// First 3 will be sent and lost.
SendVideoFrame(video_start, capture_time);
RunTasks(kFrameTimerMs);
video_start++;
}
sender_to_receiver_.SetSendPackets(true);
RunTasks(100);
capture_time = testing_clock_sender_->NowTicks();
// Frame 1 should be acked by now and we should have an opening to send 4.
SendVideoFrame(video_start, capture_time);
RunTasks(kFrameTimerMs);
// Frames 1-3 are old frames by now, and therefore should be decoded, but
// not rendered. The next frame we expect to render is frame #4.
test_receiver_video_callback_->AddExpectedResult(
video_start,
video_sender_config_.width,
video_sender_config_.height,
capture_time + base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs),
true);
cast_receiver_->RequestDecodedVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
RunTasks(2 * kFrameTimerMs + 1); // Empty the receiver pipeline.
EXPECT_EQ(frames_before_glitch + 1,
test_receiver_video_callback_->number_times_called());
}
// Disabled due to flakiness and crashiness. http://crbug.com/360951
TEST_F(End2EndTest, DISABLED_DropEveryOtherFrame3Buffers) {
Configure(CODEC_VIDEO_VP8, CODEC_AUDIO_OPUS,
kDefaultAudioSamplingRate, 3);
video_sender_config_.target_playout_delay = video_sender_config_.target_playout_delay =
base::TimeDelta::FromMilliseconds(67); base::TimeDelta::FromMilliseconds(target_delay);
video_receiver_config_.rtp_max_delay_ms = 67; audio_sender_config_.target_playout_delay =
base::TimeDelta::FromMilliseconds(target_delay);
video_receiver_config_.rtp_max_delay_ms = target_delay;
Create(); Create();
sender_to_receiver_.DropAllPacketsBelongingToOddFrames(); sender_to_receiver_.DropAllPacketsBelongingToOddFrames();
...@@ -1078,7 +1015,7 @@ TEST_F(End2EndTest, DISABLED_DropEveryOtherFrame3Buffers) { ...@@ -1078,7 +1015,7 @@ TEST_F(End2EndTest, DISABLED_DropEveryOtherFrame3Buffers) {
video_sender_config_.width, video_sender_config_.width,
video_sender_config_.height, video_sender_config_.height,
capture_time + capture_time +
base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs), base::TimeDelta::FromMilliseconds(target_delay),
i == 0); i == 0);
// GetRawVideoFrame will not return the frame until we are close in // GetRawVideoFrame will not return the frame until we are close in
...@@ -1091,7 +1028,7 @@ TEST_F(End2EndTest, DISABLED_DropEveryOtherFrame3Buffers) { ...@@ -1091,7 +1028,7 @@ TEST_F(End2EndTest, DISABLED_DropEveryOtherFrame3Buffers) {
video_start++; video_start++;
} }
RunTasks(2 * kFrameTimerMs + 1); // Empty the pipeline. RunTasks(2 * kFrameTimerMs + target_delay); // Empty the pipeline.
EXPECT_EQ(i / 2, test_receiver_video_callback_->number_times_called()); EXPECT_EQ(i / 2, test_receiver_video_callback_->number_times_called());
} }
......
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