blob: 8016d8513a78257eae942ede8c06b95dd5928076 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/cast/receiver/frame_receiver.h"
#include <deque>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/test/simple_test_tick_clock.h"
#include "media/base/fake_single_thread_task_runner.h"
#include "media/cast/cast_environment.h"
#include "media/cast/logging/simple_event_subscriber.h"
#include "media/cast/net/cast_transport_impl.h"
#include "media/cast/net/mock_cast_transport.h"
#include "media/cast/net/rtcp/rtcp_utility.h"
#include "media/cast/net/rtcp/test_rtcp_packet_builder.h"
#include "media/cast/test/utility/default_config.h"
#include "starboard/types.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
namespace cobalt {
namespace media {
namespace cast {
namespace {
const int kPacketSize = 1500;
const int kPlayoutDelayMillis = 100;
FrameId GetFirstTestFrameId() { return FrameId::first() + 1234; }
class FakeFrameClient {
public:
FakeFrameClient() : num_called_(0) {}
virtual ~FakeFrameClient() {}
void AddExpectedResult(FrameId expected_frame_id,
const base::TimeTicks& expected_playout_time) {
expected_results_.push_back(
std::make_pair(expected_frame_id, expected_playout_time));
}
void DeliverEncodedFrame(std::unique_ptr<EncodedFrame> frame) {
SCOPED_TRACE(::testing::Message() << "num_called_ is " << num_called_);
ASSERT_TRUE(frame)
<< "If at shutdown: There were unsatisfied requests enqueued.";
ASSERT_FALSE(expected_results_.empty());
EXPECT_EQ(expected_results_.front().first, frame->frame_id);
EXPECT_EQ(expected_results_.front().second, frame->reference_time);
expected_results_.pop_front();
++num_called_;
}
int number_times_called() const { return num_called_; }
private:
std::deque<std::pair<FrameId, base::TimeTicks>> expected_results_;
int num_called_;
DISALLOW_COPY_AND_ASSIGN(FakeFrameClient);
};
} // namespace
class FrameReceiverTest : public ::testing::Test {
protected:
FrameReceiverTest() {
testing_clock_ = new base::SimpleTestTickClock();
testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
start_time_ = testing_clock_->NowTicks();
task_runner_ = new FakeSingleThreadTaskRunner(testing_clock_);
cast_environment_ =
new CastEnvironment(std::unique_ptr<base::TickClock>(testing_clock_),
task_runner_, task_runner_, task_runner_);
}
~FrameReceiverTest() override {}
void SetUp() final {
payload_.assign(kPacketSize, 0);
// Always start with a key frame.
rtp_header_.is_key_frame = true;
rtp_header_.frame_id = GetFirstTestFrameId();
rtp_header_.packet_id = 0;
rtp_header_.max_packet_id = 0;
rtp_header_.reference_frame_id = rtp_header_.frame_id;
rtp_header_.rtp_timestamp = RtpTimeTicks();
}
void CreateFrameReceiverOfAudio() {
config_ = GetDefaultAudioReceiverConfig();
config_.rtp_max_delay_ms = kPlayoutDelayMillis;
receiver_.reset(new FrameReceiver(cast_environment_, config_, AUDIO_EVENT,
&mock_transport_));
}
void CreateFrameReceiverOfVideo() {
config_ = GetDefaultVideoReceiverConfig();
config_.rtp_max_delay_ms = kPlayoutDelayMillis;
// Note: Frame rate must divide 1000 without remainder so the test code
// doesn't have to account for rounding errors.
config_.target_frame_rate = 25;
receiver_.reset(new FrameReceiver(cast_environment_, config_, VIDEO_EVENT,
&mock_transport_));
}
void FeedOneFrameIntoReceiver() {
// Note: For testing purposes, a frame consists of only a single packet.
receiver_->ProcessParsedPacket(rtp_header_, &payload_[0], payload_.size());
}
void FeedLipSyncInfoIntoReceiver() {
const base::TimeTicks now = testing_clock_->NowTicks();
const RtpTimeTicks rtp_timestamp =
RtpTimeTicks::FromTimeDelta(now - start_time_, config_.rtp_timebase);
CHECK_LE(RtpTimeTicks(), rtp_timestamp);
uint32_t ntp_seconds;
uint32_t ntp_fraction;
ConvertTimeTicksToNtp(now, &ntp_seconds, &ntp_fraction);
TestRtcpPacketBuilder rtcp_packet;
rtcp_packet.AddSrWithNtp(config_.sender_ssrc, ntp_seconds, ntp_fraction,
rtp_timestamp.lower_32_bits());
ASSERT_TRUE(receiver_->ProcessPacket(rtcp_packet.GetPacket()));
}
FrameReceiverConfig config_;
std::vector<uint8_t> payload_;
RtpCastHeader rtp_header_;
base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
base::TimeTicks start_time_;
MockCastTransport mock_transport_;
scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
scoped_refptr<CastEnvironment> cast_environment_;
FakeFrameClient frame_client_;
// Important for the FrameReceiver to be declared last, since its dependencies
// must remain alive until after its destruction.
std::unique_ptr<FrameReceiver> receiver_;
private:
DISALLOW_COPY_AND_ASSIGN(FrameReceiverTest);
};
TEST_F(FrameReceiverTest, RejectsUnparsablePackets) {
EXPECT_CALL(mock_transport_, AddValidRtpReceiver(_, _))
.WillRepeatedly(testing::Return());
CreateFrameReceiverOfVideo();
SimpleEventSubscriber event_subscriber;
cast_environment_->logger()->Subscribe(&event_subscriber);
const bool success = receiver_->ProcessPacket(
std::unique_ptr<Packet>(new Packet(kPacketSize, 0xff)));
EXPECT_FALSE(success);
// Confirm no log events.
std::vector<FrameEvent> frame_events;
event_subscriber.GetFrameEventsAndReset(&frame_events);
EXPECT_TRUE(frame_events.empty());
cast_environment_->logger()->Unsubscribe(&event_subscriber);
}
TEST_F(FrameReceiverTest, ReceivesOneFrame) {
EXPECT_CALL(mock_transport_, AddValidRtpReceiver(_, _))
.WillRepeatedly(testing::Return());
CreateFrameReceiverOfAudio();
SimpleEventSubscriber event_subscriber;
cast_environment_->logger()->Subscribe(&event_subscriber);
EXPECT_CALL(mock_transport_, InitializeRtpReceiverRtcpBuilder(_, _))
.WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, AddCastFeedback(_, _))
.WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, AddPli(_)).WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, AddRtcpEvents(_))
.WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, SendRtcpFromRtpReceiver())
.WillRepeatedly(testing::Return());
FeedLipSyncInfoIntoReceiver();
task_runner_->RunTasks();
// Enqueue a request for a frame.
receiver_->RequestEncodedFrame(base::Bind(
&FakeFrameClient::DeliverEncodedFrame, base::Unretained(&frame_client_)));
// The request should not be satisfied since no packets have been received.
task_runner_->RunTasks();
EXPECT_EQ(0, frame_client_.number_times_called());
// Deliver one frame to the receiver and expect to get one frame back.
const base::TimeDelta target_playout_delay =
base::TimeDelta::FromMilliseconds(kPlayoutDelayMillis);
frame_client_.AddExpectedResult(
GetFirstTestFrameId(), testing_clock_->NowTicks() + target_playout_delay);
FeedOneFrameIntoReceiver();
task_runner_->RunTasks();
EXPECT_EQ(1, frame_client_.number_times_called());
// Was the frame logged?
std::vector<FrameEvent> frame_events;
event_subscriber.GetFrameEventsAndReset(&frame_events);
ASSERT_TRUE(!frame_events.empty());
EXPECT_EQ(FRAME_ACK_SENT, frame_events.begin()->type);
EXPECT_EQ(AUDIO_EVENT, frame_events.begin()->media_type);
EXPECT_EQ(rtp_header_.frame_id, frame_events.begin()->frame_id);
EXPECT_EQ(rtp_header_.rtp_timestamp, frame_events.begin()->rtp_timestamp);
cast_environment_->logger()->Unsubscribe(&event_subscriber);
}
TEST_F(FrameReceiverTest, ReceivesFramesSkippingWhenAppropriate) {
EXPECT_CALL(mock_transport_, AddValidRtpReceiver(_, _))
.WillRepeatedly(testing::Return());
CreateFrameReceiverOfAudio();
SimpleEventSubscriber event_subscriber;
cast_environment_->logger()->Subscribe(&event_subscriber);
EXPECT_CALL(mock_transport_, InitializeRtpReceiverRtcpBuilder(_, _))
.WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, AddCastFeedback(_, _))
.WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, AddPli(_)).WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, AddRtcpEvents(_))
.WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, SendRtcpFromRtpReceiver())
.WillRepeatedly(testing::Return());
const base::TimeDelta time_advance_per_frame =
base::TimeDelta::FromSeconds(1) / config_.target_frame_rate;
const RtpTimeDelta rtp_advance_per_frame =
RtpTimeDelta::FromTimeDelta(time_advance_per_frame, config_.rtp_timebase);
// Feed and process lip sync in receiver.
FeedLipSyncInfoIntoReceiver();
task_runner_->RunTasks();
const base::TimeTicks first_frame_capture_time = testing_clock_->NowTicks();
// Enqueue a request for a frame.
const ReceiveEncodedFrameCallback frame_encoded_callback = base::Bind(
&FakeFrameClient::DeliverEncodedFrame, base::Unretained(&frame_client_));
receiver_->RequestEncodedFrame(frame_encoded_callback);
task_runner_->RunTasks();
EXPECT_EQ(0, frame_client_.number_times_called());
// Receive one frame and expect to see the first request satisfied.
const base::TimeDelta target_playout_delay =
base::TimeDelta::FromMilliseconds(kPlayoutDelayMillis);
frame_client_.AddExpectedResult(
GetFirstTestFrameId(), first_frame_capture_time + target_playout_delay);
rtp_header_.rtp_timestamp = RtpTimeTicks();
FeedOneFrameIntoReceiver(); // Frame 1
task_runner_->RunTasks();
EXPECT_EQ(1, frame_client_.number_times_called());
// Enqueue a second request for a frame, but it should not be fulfilled yet.
receiver_->RequestEncodedFrame(frame_encoded_callback);
task_runner_->RunTasks();
EXPECT_EQ(1, frame_client_.number_times_called());
// Receive one frame out-of-order: Make sure that we are not continuous and
// that the RTP timestamp represents a time in the future.
rtp_header_.frame_id = GetFirstTestFrameId() + 2; // "Frame 3"
rtp_header_.reference_frame_id = rtp_header_.frame_id;
rtp_header_.rtp_timestamp += rtp_advance_per_frame * 2;
frame_client_.AddExpectedResult(rtp_header_.frame_id,
first_frame_capture_time +
2 * time_advance_per_frame +
target_playout_delay);
FeedOneFrameIntoReceiver(); // Frame 3
// Frame 2 should not come out at this point in time.
task_runner_->RunTasks();
EXPECT_EQ(1, frame_client_.number_times_called());
// Enqueue a third request for a frame.
receiver_->RequestEncodedFrame(frame_encoded_callback);
task_runner_->RunTasks();
EXPECT_EQ(1, frame_client_.number_times_called());
// Now, advance time forward such that the receiver is convinced it should
// skip Frame 2. Frame 3 is emitted (to satisfy the second request) because a
// decision was made to skip over the no-show Frame 2.
testing_clock_->Advance(2 * time_advance_per_frame + target_playout_delay);
task_runner_->RunTasks();
EXPECT_EQ(2, frame_client_.number_times_called());
// Receive Frame 4 and expect it to fulfill the third request immediately.
rtp_header_.frame_id = GetFirstTestFrameId() + 3; // "Frame 4"
rtp_header_.reference_frame_id = rtp_header_.frame_id;
rtp_header_.rtp_timestamp += rtp_advance_per_frame;
frame_client_.AddExpectedResult(rtp_header_.frame_id,
first_frame_capture_time +
3 * time_advance_per_frame +
target_playout_delay);
FeedOneFrameIntoReceiver(); // Frame 4
task_runner_->RunTasks();
EXPECT_EQ(3, frame_client_.number_times_called());
// Move forward to the playout time of an unreceived Frame 5. Expect no
// additional frames were emitted.
testing_clock_->Advance(3 * time_advance_per_frame);
task_runner_->RunTasks();
EXPECT_EQ(3, frame_client_.number_times_called());
// Were only non-skipped frames logged?
std::vector<FrameEvent> frame_events;
event_subscriber.GetFrameEventsAndReset(&frame_events);
ASSERT_TRUE(!frame_events.empty());
for (size_t i = 0; i < frame_events.size(); ++i) {
EXPECT_EQ(FRAME_ACK_SENT, frame_events[i].type);
EXPECT_EQ(AUDIO_EVENT, frame_events[i].media_type);
EXPECT_LE(GetFirstTestFrameId(), frame_events[i].frame_id);
EXPECT_GE(GetFirstTestFrameId() + 4, frame_events[i].frame_id);
const int frame_offset = frame_events[i].frame_id - GetFirstTestFrameId();
EXPECT_NE(frame_offset, 1); // Frame 2 never received.
EXPECT_EQ(RtpTimeTicks() + (rtp_advance_per_frame * frame_offset),
frame_events[i].rtp_timestamp);
}
cast_environment_->logger()->Unsubscribe(&event_subscriber);
}
TEST_F(FrameReceiverTest, ReceivesFramesRefusingToSkipAny) {
EXPECT_CALL(mock_transport_, AddValidRtpReceiver(_, _))
.WillRepeatedly(testing::Return());
CreateFrameReceiverOfVideo();
SimpleEventSubscriber event_subscriber;
cast_environment_->logger()->Subscribe(&event_subscriber);
EXPECT_CALL(mock_transport_, InitializeRtpReceiverRtcpBuilder(_, _))
.WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, AddCastFeedback(_, _))
.WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, AddPli(_)).WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, AddRtcpEvents(_))
.WillRepeatedly(testing::Return());
EXPECT_CALL(mock_transport_, SendRtcpFromRtpReceiver())
.WillRepeatedly(testing::Return());
const base::TimeDelta time_advance_per_frame =
base::TimeDelta::FromSeconds(1) / config_.target_frame_rate;
const RtpTimeDelta rtp_advance_per_frame =
RtpTimeDelta::FromTimeDelta(time_advance_per_frame, config_.rtp_timebase);
// Feed and process lip sync in receiver.
FeedLipSyncInfoIntoReceiver();
task_runner_->RunTasks();
const base::TimeTicks first_frame_capture_time = testing_clock_->NowTicks();
// Enqueue a request for a frame.
const ReceiveEncodedFrameCallback frame_encoded_callback = base::Bind(
&FakeFrameClient::DeliverEncodedFrame, base::Unretained(&frame_client_));
receiver_->RequestEncodedFrame(frame_encoded_callback);
task_runner_->RunTasks();
EXPECT_EQ(0, frame_client_.number_times_called());
// Receive one frame and expect to see the first request satisfied.
const base::TimeDelta target_playout_delay =
base::TimeDelta::FromMilliseconds(kPlayoutDelayMillis);
frame_client_.AddExpectedResult(
GetFirstTestFrameId(), first_frame_capture_time + target_playout_delay);
rtp_header_.rtp_timestamp = RtpTimeTicks();
FeedOneFrameIntoReceiver(); // Frame 1
task_runner_->RunTasks();
EXPECT_EQ(1, frame_client_.number_times_called());
// Enqueue a second request for a frame, but it should not be fulfilled yet.
receiver_->RequestEncodedFrame(frame_encoded_callback);
task_runner_->RunTasks();
EXPECT_EQ(1, frame_client_.number_times_called());
// Receive one frame out-of-order: Make sure that we are not continuous and
// that the RTP timestamp represents a time in the future.
rtp_header_.is_key_frame = false;
rtp_header_.frame_id = GetFirstTestFrameId() + 2; // "Frame 3"
rtp_header_.reference_frame_id = GetFirstTestFrameId() + 1; // "Frame 2"
rtp_header_.rtp_timestamp += rtp_advance_per_frame * 2;
FeedOneFrameIntoReceiver(); // Frame 3
// Frame 2 should not come out at this point in time.
task_runner_->RunTasks();
EXPECT_EQ(1, frame_client_.number_times_called());
// Enqueue a third request for a frame.
receiver_->RequestEncodedFrame(frame_encoded_callback);
task_runner_->RunTasks();
EXPECT_EQ(1, frame_client_.number_times_called());
// Now, advance time forward such that Frame 2 is now too late for playback.
// Regardless, the receiver must NOT emit Frame 3 yet because it is not
// allowed to skip frames when dependencies are not satisfied. In other
// words, Frame 3 is not decodable without Frame 2.
testing_clock_->Advance(2 * time_advance_per_frame + target_playout_delay);
task_runner_->RunTasks();
EXPECT_EQ(1, frame_client_.number_times_called());
// Now receive Frame 2 and expect both the second and third requests to be
// fulfilled immediately.
frame_client_.AddExpectedResult(GetFirstTestFrameId() + 1, // "Frame 2"
first_frame_capture_time +
1 * time_advance_per_frame +
target_playout_delay);
frame_client_.AddExpectedResult(GetFirstTestFrameId() + 2, // "Frame 3"
first_frame_capture_time +
2 * time_advance_per_frame +
target_playout_delay);
--rtp_header_.frame_id; // "Frame 2"
--rtp_header_.reference_frame_id; // "Frame 1"
rtp_header_.rtp_timestamp -= rtp_advance_per_frame;
FeedOneFrameIntoReceiver(); // Frame 2
task_runner_->RunTasks();
EXPECT_EQ(3, frame_client_.number_times_called());
// Move forward to the playout time of an unreceived Frame 5. Expect no
// additional frames were emitted.
testing_clock_->Advance(3 * time_advance_per_frame);
task_runner_->RunTasks();
EXPECT_EQ(3, frame_client_.number_times_called());
// Sanity-check logging results.
std::vector<FrameEvent> frame_events;
event_subscriber.GetFrameEventsAndReset(&frame_events);
ASSERT_TRUE(!frame_events.empty());
for (size_t i = 0; i < frame_events.size(); ++i) {
EXPECT_EQ(FRAME_ACK_SENT, frame_events[i].type);
EXPECT_EQ(VIDEO_EVENT, frame_events[i].media_type);
EXPECT_LE(GetFirstTestFrameId(), frame_events[i].frame_id);
EXPECT_GE(GetFirstTestFrameId() + 3, frame_events[i].frame_id);
const int frame_offset = frame_events[i].frame_id - GetFirstTestFrameId();
EXPECT_EQ(RtpTimeTicks() + (rtp_advance_per_frame * frame_offset),
frame_events[i].rtp_timestamp);
}
cast_environment_->logger()->Unsubscribe(&event_subscriber);
}
} // namespace cast
} // namespace media
} // namespace cobalt