2
* Copyright (C) 2016 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3, as published
6
* by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranties of
10
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
* PURPOSE. See the GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License along
14
* with this program. If not, see <http://www.gnu.org/licenses/>.
18
#include <gmock/gmock.h>
20
#include "mcs/network/stream.h"
22
#include "mcs/streaming/rtpsender.h"
24
using namespace ::testing;
27
static constexpr unsigned int kRTPHeaderSize{12};
28
static constexpr unsigned int kStreamMaxUnitSize = 1472;
29
static constexpr unsigned int kMPEGTSPacketSize{188};
30
static constexpr unsigned int kSourceID = 0xdeadbeef;
31
// See http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml
32
static constexpr unsigned int kRTPPayloadTypeMP2T = 33;
34
class MockNetworkStream : public mcs::network::Stream {
36
MOCK_METHOD2(Connect, bool(const std::string &address, const mcs::network::Port &port));
37
MOCK_METHOD0(WaitUntilReady, bool());
38
MOCK_METHOD2(Write, mcs::network::Stream::Error(const uint8_t*, unsigned int));
39
MOCK_CONST_METHOD0(LocalPort, mcs::network::Port());
40
MOCK_CONST_METHOD0(MaxUnitSize, std::uint32_t());
43
class MockSenderReport : public mcs::video::SenderReport {
45
MOCK_METHOD2(SentPacket, void(const mcs::TimestampUs&, const size_t&));
49
TEST(RTPSender, ForwardsCorrectPort) {
50
auto mock_stream = std::make_shared<MockNetworkStream>();
51
auto mock_report = std::make_shared<MockSenderReport>();
53
EXPECT_CALL(*mock_stream, MaxUnitSize())
54
.WillRepeatedly(Return(kStreamMaxUnitSize));
56
mcs::network::Port local_port = 1234;
58
EXPECT_CALL(*mock_stream, LocalPort())
60
.WillOnce(Return(local_port));
62
auto sender = std::make_shared<mcs::streaming::RTPSender>(mock_stream, mock_report);
64
EXPECT_EQ(local_port, sender->LocalPort());
67
TEST(RTPSender, SpecifiesExecutableName) {
68
auto mock_stream = std::make_shared<MockNetworkStream>();
69
auto mock_report = std::make_shared<MockSenderReport>();
71
EXPECT_CALL(*mock_stream, MaxUnitSize())
72
.WillRepeatedly(Return(kStreamMaxUnitSize));
74
auto sender = std::make_shared<mcs::streaming::RTPSender>(mock_stream, mock_report);
75
EXPECT_NE(0, sender->Name().length());
78
TEST(RTPSender, StartAndStopDoesNotFail) {
79
auto mock_stream = std::make_shared<MockNetworkStream>();
80
auto mock_report = std::make_shared<MockSenderReport>();
82
EXPECT_CALL(*mock_stream, MaxUnitSize())
83
.WillRepeatedly(Return(kStreamMaxUnitSize));
85
auto sender = std::make_shared<mcs::streaming::RTPSender>(mock_stream, mock_report);
87
EXPECT_TRUE(sender->Start());
88
EXPECT_TRUE(sender->Stop());
91
TEST(RTPSender, DoeNotAcceptIncorrectPacketSizes) {
92
auto mock_stream = std::make_shared<MockNetworkStream>();
93
auto mock_report = std::make_shared<MockSenderReport>();
95
EXPECT_CALL(*mock_stream, MaxUnitSize())
96
.WillRepeatedly(Return(kStreamMaxUnitSize));
98
auto sender = std::make_shared<mcs::streaming::RTPSender>(mock_stream, mock_report);
100
auto packets = mcs::video::Buffer::Create(kMPEGTSPacketSize + 1);
101
EXPECT_FALSE(sender->Queue(packets));
103
packets = mcs::video::Buffer::Create(kMPEGTSPacketSize - 1);
104
EXPECT_FALSE(sender->Queue(packets));
107
TEST(RTPSender, ExecutesWithEmptyQueue) {
108
auto mock_stream = std::make_shared<MockNetworkStream>();
109
auto mock_report = std::make_shared<MockSenderReport>();
111
EXPECT_CALL(*mock_stream, MaxUnitSize())
112
.WillRepeatedly(Return(kStreamMaxUnitSize));
114
auto sender = std::make_shared<mcs::streaming::RTPSender>(mock_stream, mock_report);
116
EXPECT_TRUE(sender->Execute());
119
TEST(RTPSender, ExecuteDoesNotFailWhenStreamIsNotReady) {
120
auto mock_stream = std::make_shared<MockNetworkStream>();
121
auto mock_report = std::make_shared<MockSenderReport>();
123
EXPECT_CALL(*mock_stream, MaxUnitSize())
124
.WillRepeatedly(Return(kStreamMaxUnitSize));
126
EXPECT_CALL(*mock_stream, WaitUntilReady())
128
.WillOnce(Return(false));
130
auto sender = std::make_shared<mcs::streaming::RTPSender>(mock_stream, mock_report);
132
auto packets = mcs::video::Buffer::Create(kMPEGTSPacketSize);
134
EXPECT_TRUE(sender->Queue(packets));
135
EXPECT_TRUE(sender->Execute());
138
TEST(RTPSender, QueuesUpPackagesAndSendsAll) {
139
auto mock_stream = std::make_shared<MockNetworkStream>();
140
auto mock_report = std::make_shared<MockSenderReport>();
142
auto now = mcs::Utils::GetNowUs();
144
EXPECT_CALL(*mock_report, SentPacket(now, _))
147
EXPECT_CALL(*mock_stream, MaxUnitSize())
148
.WillRepeatedly(Return(kStreamMaxUnitSize));
150
EXPECT_CALL(*mock_stream, WaitUntilReady())
152
.WillOnce(Return(true));
154
EXPECT_CALL(*mock_stream, Write(_, _))
156
.WillRepeatedly(Return(mcs::network::Stream::Error::kNone));
158
auto sender = std::make_shared<mcs::streaming::RTPSender>(mock_stream, mock_report);
160
auto packets = mcs::video::Buffer::Create(kMPEGTSPacketSize * 15);
161
packets->SetTimestamp(now);
163
EXPECT_TRUE(sender->Queue(packets));
164
EXPECT_TRUE(sender->Execute());
167
TEST(RTPSender, WritePackageFails) {
168
auto mock_stream = std::make_shared<MockNetworkStream>();
169
auto mock_report = std::make_shared<MockSenderReport>();
171
EXPECT_CALL(*mock_stream, MaxUnitSize())
172
.WillRepeatedly(Return(kStreamMaxUnitSize));
174
EXPECT_CALL(*mock_stream, WaitUntilReady())
176
.WillOnce(Return(true));
178
EXPECT_CALL(*mock_stream, Write(_, _))
180
.WillOnce(Return(mcs::network::Stream::Error::kRemoteClosedConnection));
182
auto sender = std::make_shared<mcs::streaming::RTPSender>(mock_stream, mock_report);
184
auto packets = mcs::video::Buffer::Create(kMPEGTSPacketSize);
186
EXPECT_TRUE(sender->Queue(packets));
187
EXPECT_FALSE(sender->Execute());
190
TEST(RTPSender, ConstructsCorrectRTPHeader) {
191
auto mock_stream = std::make_shared<MockNetworkStream>();
192
auto mock_report = std::make_shared<MockSenderReport>();
194
auto packet_timestamp = mcs::Utils::GetNowUs();
196
uint32_t expected_output_size = kMPEGTSPacketSize + kRTPHeaderSize;
198
EXPECT_CALL(*mock_report, SentPacket(packet_timestamp, expected_output_size))
201
EXPECT_CALL(*mock_stream, MaxUnitSize())
202
.WillRepeatedly(Return(kStreamMaxUnitSize));
204
EXPECT_CALL(*mock_stream, WaitUntilReady())
205
.WillOnce(Return(true));
207
std::uint8_t *output_data = nullptr;
209
EXPECT_CALL(*mock_stream, Write(_, expected_output_size))
210
.WillOnce(DoAll(Invoke([&](const uint8_t *data, unsigned int size) {
211
// Need to copy the data here as otherwise its freed
212
// as soon as the write call comes back.
213
output_data = new std::uint8_t[size];
214
::memcpy(output_data, data, size);
216
Return(mcs::network::Stream::Error::kNone)));
218
auto sender = std::make_shared<mcs::streaming::RTPSender>(mock_stream, mock_report);
220
auto packets = mcs::video::Buffer::Create(kMPEGTSPacketSize);
221
packets->SetTimestamp(packet_timestamp);
223
EXPECT_TRUE(sender->Queue(packets));
224
EXPECT_TRUE(sender->Execute());
226
EXPECT_NE(nullptr, output_data);
228
EXPECT_EQ(0x80, output_data[0]);
229
EXPECT_EQ(kRTPPayloadTypeMP2T, output_data[1]);
231
// Sequence should start at 0
232
EXPECT_EQ(0x00, output_data[2]);
233
EXPECT_EQ(0x00, output_data[3]);
235
// We can't compare the actual RTP time here but we can make sure
236
// its between our start time and now.
237
std::uint32_t rtp_time = 0;
238
rtp_time |= (output_data[4] << 24);
239
rtp_time |= (output_data[5] << 16);
240
rtp_time |= (output_data[6] << 8);
241
rtp_time |= (output_data[7] << 0);
243
// Convert back from a 90 kHz clock
247
auto packet_timestamp_ms = packet_timestamp / 1000ll;
248
EXPECT_LE(packet_timestamp_ms, rtp_time);
249
EXPECT_GE(mcs::Utils::GetNowUs(), rtp_time);
251
std::uint32_t source_id = 0;
252
source_id |= (output_data[8] << 24);
253
source_id |= (output_data[9] << 16);
254
source_id |= (output_data[10] << 8);
255
source_id |= (output_data[11] << 0);
257
EXPECT_EQ(kSourceID, source_id);