1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00
srs/trunk/src/utest/srs_utest_rtc.cpp

1007 lines
35 KiB
C++

/*
The MIT License (MIT)
Copyright (c) 2013-2020 Winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_utest_rtc.hpp>
#include <srs_kernel_error.hpp>
#include <srs_core_autofree.hpp>
#include <srs_app_rtc_queue.hpp>
#include <srs_app_utility.hpp>
#include <srs_kernel_rtc_rtp.hpp>
#include <srs_app_rtc_source.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_kernel_codec.hpp>
#include <vector>
using namespace std;
extern SSL_CTX* srs_build_dtls_ctx(SrsDtlsVersion version);
class MockDtls
{
public:
SSL_CTX* dtls_ctx;
SSL* dtls;
BIO* bio_in;
BIO* bio_out;
ISrsDtlsCallback* callback_;
bool handshake_done_for_us;
SrsDtlsRole role_;
SrsDtlsVersion version_;
public:
MockDtls(ISrsDtlsCallback* callback);
virtual ~MockDtls();
srs_error_t initialize(std::string role, std::string version);
srs_error_t start_active_handshake();
srs_error_t on_dtls(char* data, int nb_data);
srs_error_t do_handshake();
};
MockDtls::MockDtls(ISrsDtlsCallback* callback)
{
dtls_ctx = NULL;
dtls = NULL;
callback_ = callback;
handshake_done_for_us = false;
role_ = SrsDtlsRoleServer;
version_ = SrsDtlsVersionAuto;
}
MockDtls::~MockDtls()
{
if (dtls_ctx) {
SSL_CTX_free(dtls_ctx);
dtls_ctx = NULL;
}
if (dtls) {
SSL_free(dtls);
dtls = NULL;
}
}
srs_error_t MockDtls::initialize(std::string role, std::string version)
{
role_ = SrsDtlsRoleServer;
if (role == "active") {
role_ = SrsDtlsRoleClient;
}
if (version == "dtls1.0") {
version_ = SrsDtlsVersion1_0;
} else if (version == "dtls1.2") {
version_ = SrsDtlsVersion1_2;
} else {
version_ = SrsDtlsVersionAuto;
}
dtls_ctx = srs_build_dtls_ctx(version_);
dtls = SSL_new(dtls_ctx);
srs_assert(dtls);
if (role_ == SrsDtlsRoleClient) {
SSL_set_connect_state(dtls);
SSL_set_max_send_fragment(dtls, kRtpPacketSize);
} else {
SSL_set_accept_state(dtls);
}
bio_in = BIO_new(BIO_s_mem());
srs_assert(bio_in);
bio_out = BIO_new(BIO_s_mem());
srs_assert(bio_out);
SSL_set_bio(dtls, bio_in, bio_out);
return srs_success;
}
srs_error_t MockDtls::start_active_handshake()
{
if (role_ == SrsDtlsRoleClient) {
return do_handshake();
}
return srs_success;
}
srs_error_t MockDtls::on_dtls(char* data, int nb_data)
{
srs_error_t err = srs_success;
srs_assert(BIO_reset(bio_in) == 1);
srs_assert(BIO_reset(bio_out) == 1);
srs_assert(BIO_write(bio_in, data, nb_data) > 0);
if ((err = do_handshake()) != srs_success) {
return srs_error_wrap(err, "do handshake");
}
while (BIO_ctrl_pending(bio_in) > 0) {
char buf[8092];
int nb = SSL_read(dtls, buf, sizeof(buf));
if (nb <= 0) {
continue;
}
if ((err = callback_->on_dtls_application_data(buf, nb)) != srs_success) {
return srs_error_wrap(err, "on DTLS data, size=%u", nb);
}
}
return err;
}
srs_error_t MockDtls::do_handshake()
{
srs_error_t err = srs_success;
int r0 = SSL_do_handshake(dtls);
int r1 = SSL_get_error(dtls, r0);
if (r0 < 0 && (r1 != SSL_ERROR_NONE && r1 != SSL_ERROR_WANT_READ && r1 != SSL_ERROR_WANT_WRITE)) {
return srs_error_new(ERROR_RTC_DTLS, "handshake r0=%d, r1=%d", r0, r1);
}
if (r1 == SSL_ERROR_NONE) {
handshake_done_for_us = true;
}
uint8_t* data = NULL;
int size = BIO_get_mem_data(bio_out, &data);
if (size > 0 && (err = callback_->write_dtls_data(data, size)) != srs_success) {
return srs_error_wrap(err, "dtls send size=%u", size);
}
if (handshake_done_for_us) {
return callback_->on_dtls_handshake_done();
}
return err;
}
class MockDtlsCallback : virtual public ISrsDtlsCallback, virtual public ISrsCoroutineHandler
{
public:
SrsDtls* peer;
MockDtls* peer2;
SrsCoroutine* trd;
srs_error_t r0;
bool done;
std::vector<SrsSample> samples;
public:
int nn_client_hello_lost;
int nn_server_hello_lost;
int nn_certificate_lost;
int nn_new_session_lost;
int nn_change_cipher_lost;
public:
// client -> server
int nn_client_hello;
// server -> client
int nn_server_hello;
// client -> server
int nn_certificate;
// server -> client
int nn_new_session;
int nn_change_cipher;
public:
MockDtlsCallback();
virtual ~MockDtlsCallback();
virtual srs_error_t on_dtls_handshake_done();
virtual srs_error_t on_dtls_application_data(const char* data, const int len);
virtual srs_error_t write_dtls_data(void* data, int size);
virtual srs_error_t cycle();
};
MockDtlsCallback::MockDtlsCallback()
{
peer = NULL;
peer2 = NULL;
r0 = srs_success;
done = false;
trd = new SrsSTCoroutine("mock", this);
srs_assert(trd->start() == srs_success);
nn_client_hello_lost = 0;
nn_server_hello_lost = 0;
nn_certificate_lost = 0;
nn_new_session_lost = 0;
nn_change_cipher_lost = 0;
nn_client_hello = 0;
nn_server_hello = 0;
nn_certificate = 0;
nn_new_session = 0;
nn_change_cipher = 0;
}
MockDtlsCallback::~MockDtlsCallback()
{
srs_freep(trd);
srs_freep(r0);
for (vector<SrsSample>::iterator it = samples.begin(); it != samples.end(); ++it) {
delete[] it->bytes;
}
}
srs_error_t MockDtlsCallback::on_dtls_handshake_done()
{
done = true;
return srs_success;
}
srs_error_t MockDtlsCallback::on_dtls_application_data(const char* data, const int len)
{
return srs_success;
}
srs_error_t MockDtlsCallback::write_dtls_data(void* data, int size)
{
int nn_lost = 0;
if (true) {
uint8_t content_type = 0;
if (size >= 1) {
content_type = (uint8_t)((uint8_t*)data)[0];
}
uint8_t handshake_type = 0;
if (size >= 14) {
handshake_type = (uint8_t)((uint8_t*)data)[13];
}
if (content_type == 22) {
if (handshake_type == 1) {
nn_lost = nn_client_hello_lost--;
nn_client_hello++;
} else if (handshake_type == 2) {
nn_lost = nn_server_hello_lost--;
nn_server_hello++;
} else if (handshake_type == 11) {
nn_lost = nn_certificate_lost--;
nn_certificate++;
} else if (handshake_type == 4) {
nn_lost = nn_new_session_lost--;
nn_new_session++;
}
} else if (content_type == 20) {
nn_lost = nn_change_cipher_lost--;
nn_change_cipher++;
}
}
// Simulate to drop packet.
if (nn_lost > 0) {
return srs_success;
}
// Send out it.
char* cp = new char[size];
memcpy(cp, data, size);
samples.push_back(SrsSample((char*)cp, size));
return srs_success;
}
srs_error_t MockDtlsCallback::cycle()
{
srs_error_t err = srs_success;
while (err == srs_success) {
if ((err = trd->pull()) != srs_success) {
break;
}
if (samples.empty()) {
srs_usleep(0);
continue;
}
SrsSample p = samples.at(0);
samples.erase(samples.begin());
if (peer) {
err = peer->on_dtls((char*)p.bytes, p.size);
} else if (peer2) {
err = peer2->on_dtls((char*)p.bytes, p.size);
}
srs_freepa(p.bytes);
}
// Copy it for utest to check it.
r0 = srs_error_copy(err);
return err;
}
// Wait for mock io to done, try to switch to coroutine many times.
void mock_wait_dtls_io_done(int count = 100, int interval = 0)
{
for (int i = 0; i < count; i++) {
srs_usleep(interval * SRS_UTIME_MILLISECONDS);
}
}
// To avoid the crash when peer or peer2 is freed before io.
class MockBridgeDtlsIO
{
private:
MockDtlsCallback* io_;
public:
MockBridgeDtlsIO(MockDtlsCallback* io, SrsDtls* peer, MockDtls* peer2) {
io_ = io;
io->peer = peer;
io->peer2 = peer2;
}
virtual ~MockBridgeDtlsIO() {
io_->peer = NULL;
io_->peer2 = NULL;
}
};
struct DTLSServerFlowCase
{
int id;
string ClientVersion;
string ServerVersion;
bool ClientDone;
bool ServerDone;
bool ClientError;
bool ServerError;
};
std::ostream& operator<< (std::ostream& stream, const DTLSServerFlowCase& c)
{
stream << "Case #" << c.id
<< ", client(" << c.ClientVersion << ",done=" << c.ClientDone << ",err=" << c.ClientError << ")"
<< ", server(" << c.ServerVersion << ",done=" << c.ServerDone << ",err=" << c.ServerError << ")";
return stream;
}
VOID TEST(KernelRTCTest, DTLSClientARQTest)
{
srs_error_t err = srs_success;
// No ARQ, check the number of packets.
if (true) {
MockDtlsCallback cio; SrsDtls client(&cio);
MockDtlsCallback sio; SrsDtls server(&sio);
cio.peer = &server; sio.peer = &client;
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
mock_wait_dtls_io_done(30, 1);
EXPECT_TRUE(sio.r0 == srs_success);
EXPECT_TRUE(cio.r0 == srs_success);
EXPECT_TRUE(cio.done);
EXPECT_TRUE(sio.done);
EXPECT_EQ(1, cio.nn_client_hello);
EXPECT_EQ(1, sio.nn_server_hello);
EXPECT_EQ(1, cio.nn_certificate);
EXPECT_EQ(1, sio.nn_new_session);
EXPECT_EQ(0, sio.nn_change_cipher);
}
// ClientHello lost, client retransmit the ClientHello.
if (true) {
MockDtlsCallback cio; SrsDtls client(&cio);
MockDtlsCallback sio; SrsDtls server(&sio);
MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL);
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
// Use very short interval for utest.
dynamic_cast<SrsDtlsClientImpl*>(client.impl)->arq_first = 1 * SRS_UTIME_MILLISECONDS;
dynamic_cast<SrsDtlsClientImpl*>(client.impl)->arq_interval = 1 * SRS_UTIME_MILLISECONDS;
// Lost 2 packets, total packets should be 3.
// Note that only one server hello.
cio.nn_client_hello_lost = 2;
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
mock_wait_dtls_io_done(10, 3);
EXPECT_TRUE(sio.r0 == srs_success);
EXPECT_TRUE(cio.r0 == srs_success);
EXPECT_TRUE(cio.done);
EXPECT_TRUE(sio.done);
EXPECT_EQ(3, cio.nn_client_hello);
EXPECT_EQ(1, sio.nn_server_hello);
EXPECT_EQ(1, cio.nn_certificate);
EXPECT_EQ(1, sio.nn_new_session);
EXPECT_EQ(0, sio.nn_change_cipher);
}
// Certificate lost, client retransmit the Certificate.
if (true) {
MockDtlsCallback cio; SrsDtls client(&cio);
MockDtlsCallback sio; SrsDtls server(&sio);
MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL);
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
// Use very short interval for utest.
dynamic_cast<SrsDtlsClientImpl*>(client.impl)->arq_first = 1 * SRS_UTIME_MILLISECONDS;
dynamic_cast<SrsDtlsClientImpl*>(client.impl)->arq_interval = 1 * SRS_UTIME_MILLISECONDS;
// Lost 2 packets, total packets should be 3.
// Note that only one server NewSessionTicket.
cio.nn_certificate_lost = 2;
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
mock_wait_dtls_io_done(10, 3);
EXPECT_TRUE(sio.r0 == srs_success);
EXPECT_TRUE(cio.r0 == srs_success);
EXPECT_TRUE(cio.done);
EXPECT_TRUE(sio.done);
EXPECT_EQ(1, cio.nn_client_hello);
EXPECT_EQ(1, sio.nn_server_hello);
EXPECT_EQ(3, cio.nn_certificate);
EXPECT_EQ(1, sio.nn_new_session);
EXPECT_EQ(0, sio.nn_change_cipher);
}
}
VOID TEST(KernelRTCTest, DTLSServerARQTest)
{
srs_error_t err = srs_success;
// No ARQ, check the number of packets.
if (true) {
MockDtlsCallback cio; SrsDtls client(&cio);
MockDtlsCallback sio; SrsDtls server(&sio);
cio.peer = &server; sio.peer = &client;
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
mock_wait_dtls_io_done(30, 1);
EXPECT_TRUE(sio.r0 == srs_success);
EXPECT_TRUE(cio.r0 == srs_success);
EXPECT_TRUE(cio.done);
EXPECT_TRUE(sio.done);
EXPECT_EQ(1, cio.nn_client_hello);
EXPECT_EQ(1, sio.nn_server_hello);
EXPECT_EQ(1, cio.nn_certificate);
EXPECT_EQ(1, sio.nn_new_session);
EXPECT_EQ(0, sio.nn_change_cipher);
}
// ServerHello lost, client retransmit the ClientHello.
if (true) {
MockDtlsCallback cio; SrsDtls client(&cio);
MockDtlsCallback sio; SrsDtls server(&sio);
MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL);
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
// Use very short interval for utest.
dynamic_cast<SrsDtlsClientImpl*>(client.impl)->arq_first = 1 * SRS_UTIME_MILLISECONDS;
dynamic_cast<SrsDtlsClientImpl*>(client.impl)->arq_interval = 1 * SRS_UTIME_MILLISECONDS;
// Lost 2 packets, total packets should be 3.
sio.nn_server_hello_lost = 2;
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
mock_wait_dtls_io_done(10, 3);
EXPECT_TRUE(sio.r0 == srs_success);
EXPECT_TRUE(cio.r0 == srs_success);
EXPECT_TRUE(cio.done);
EXPECT_TRUE(sio.done);
EXPECT_EQ(3, cio.nn_client_hello);
EXPECT_EQ(3, sio.nn_server_hello);
EXPECT_EQ(1, cio.nn_certificate);
EXPECT_EQ(1, sio.nn_new_session);
EXPECT_EQ(0, sio.nn_change_cipher);
}
// NewSessionTicket lost, client retransmit the Certificate.
if (true) {
MockDtlsCallback cio; SrsDtls client(&cio);
MockDtlsCallback sio; SrsDtls server(&sio);
MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL);
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
// Use very short interval for utest.
dynamic_cast<SrsDtlsClientImpl*>(client.impl)->arq_first = 1 * SRS_UTIME_MILLISECONDS;
dynamic_cast<SrsDtlsClientImpl*>(client.impl)->arq_interval = 1 * SRS_UTIME_MILLISECONDS;
// Lost 2 packets, total packets should be 3.
sio.nn_new_session_lost = 2;
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
mock_wait_dtls_io_done(10, 3);
EXPECT_TRUE(sio.r0 == srs_success);
EXPECT_TRUE(cio.r0 == srs_success);
EXPECT_TRUE(cio.done);
EXPECT_TRUE(sio.done);
EXPECT_EQ(1, cio.nn_client_hello);
EXPECT_EQ(1, sio.nn_server_hello);
EXPECT_EQ(3, cio.nn_certificate);
EXPECT_EQ(3, sio.nn_new_session);
EXPECT_EQ(0, sio.nn_change_cipher);
}
}
VOID TEST(KernelRTCTest, DTLSClientFlowTest)
{
srs_error_t err = srs_success;
DTLSServerFlowCase cases[] = {
// OK, Client, Server: DTLS v1.0
{0, "dtls1.0", "dtls1.0", true, true, false, false},
// OK, Client, Server: DTLS v1.2
{1, "dtls1.2", "dtls1.2", true, true, false, false},
// OK, Client: DTLS v1.0, Server: DTLS auto(v1.0 or v1.2).
{2, "dtls1.0", "auto", true, true, false, false},
// OK, Client: DTLS v1.2, Server: DTLS auto(v1.0 or v1.2).
{3, "dtls1.2", "auto", true, true, false, false},
// OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0
{4, "auto", "dtls1.0", true, true, false, false},
// OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0
{5, "auto", "dtls1.2", true, true, false, false},
// Fail, Client: DTLS v1.0, Server: DTLS v1.2
{6, "dtls1.0", "dtls1.2", false, false, false, true},
// Fail, Client: DTLS v1.2, Server: DTLS v1.0
{7, "dtls1.2", "dtls1.0", false, false, true, false},
};
for (int i = 0; i < (int)(sizeof(cases) / sizeof(DTLSServerFlowCase)); i++) {
DTLSServerFlowCase c = cases[i];
MockDtlsCallback cio; SrsDtls client(&cio);
MockDtlsCallback sio; MockDtls server(&sio);
MockBridgeDtlsIO b0(&cio, NULL, &server); MockBridgeDtlsIO b1(&sio, &client, NULL);
HELPER_EXPECT_SUCCESS(client.initialize("active", c.ClientVersion)) << c;
HELPER_EXPECT_SUCCESS(server.initialize("passive", c.ServerVersion)) << c;
HELPER_EXPECT_SUCCESS(client.start_active_handshake()) << c;
mock_wait_dtls_io_done();
// Note that the cio error is generated from server, vice versa.
EXPECT_EQ(c.ClientError, sio.r0 != srs_success) << c;
EXPECT_EQ(c.ServerError, cio.r0 != srs_success) << c;
EXPECT_EQ(c.ClientDone, cio.done) << c;
EXPECT_EQ(c.ServerDone, sio.done) << c;
}
}
VOID TEST(KernelRTCTest, DTLSServerFlowTest)
{
srs_error_t err = srs_success;
DTLSServerFlowCase cases[] = {
// OK, Client, Server: DTLS v1.0
{0, "dtls1.0", "dtls1.0", true, true, false, false},
// OK, Client, Server: DTLS v1.2
{1, "dtls1.2", "dtls1.2", true, true, false, false},
// OK, Client: DTLS v1.0, Server: DTLS auto(v1.0 or v1.2).
{2, "dtls1.0", "auto", true, true, false, false},
// OK, Client: DTLS v1.2, Server: DTLS auto(v1.0 or v1.2).
{3, "dtls1.2", "auto", true, true, false, false},
// OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0
{4, "auto", "dtls1.0", true, true, false, false},
// OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0
{5, "auto", "dtls1.2", true, true, false, false},
// Fail, Client: DTLS v1.0, Server: DTLS v1.2
{6, "dtls1.0", "dtls1.2", false, false, false, true},
// Fail, Client: DTLS v1.2, Server: DTLS v1.0
{7, "dtls1.2", "dtls1.0", false, false, true, false},
};
for (int i = 0; i < (int)(sizeof(cases) / sizeof(DTLSServerFlowCase)); i++) {
DTLSServerFlowCase c = cases[i];
MockDtlsCallback cio; MockDtls client(&cio);
MockDtlsCallback sio; SrsDtls server(&sio);
MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, NULL, &client);
HELPER_EXPECT_SUCCESS(client.initialize("active", c.ClientVersion)) << c;
HELPER_EXPECT_SUCCESS(server.initialize("passive", c.ServerVersion)) << c;
HELPER_EXPECT_SUCCESS(client.start_active_handshake()) << c;
mock_wait_dtls_io_done();
// Note that the cio error is generated from server, vice versa.
EXPECT_EQ(c.ClientError, sio.r0 != srs_success) << c;
EXPECT_EQ(c.ServerError, cio.r0 != srs_success) << c;
EXPECT_EQ(c.ClientDone, cio.done) << c;
EXPECT_EQ(c.ServerDone, sio.done) << c;
}
}
VOID TEST(KernelRTCTest, SequenceCompare)
{
if (true) {
EXPECT_EQ(0, srs_rtp_seq_distance(0, 0));
EXPECT_EQ(0, srs_rtp_seq_distance(1, 1));
EXPECT_EQ(0, srs_rtp_seq_distance(3, 3));
EXPECT_EQ(1, srs_rtp_seq_distance(0, 1));
EXPECT_EQ(-1, srs_rtp_seq_distance(1, 0));
EXPECT_EQ(1, srs_rtp_seq_distance(65535, 0));
}
if (true) {
EXPECT_FALSE(srs_rtp_seq_distance(1, 1) > 0);
EXPECT_TRUE(srs_rtp_seq_distance(65534, 65535) > 0);
EXPECT_TRUE(srs_rtp_seq_distance(0, 1) > 0);
EXPECT_TRUE(srs_rtp_seq_distance(255, 256) > 0);
EXPECT_TRUE(srs_rtp_seq_distance(65535, 0) > 0);
EXPECT_TRUE(srs_rtp_seq_distance(65280, 0) > 0);
EXPECT_TRUE(srs_rtp_seq_distance(65535, 255) > 0);
EXPECT_TRUE(srs_rtp_seq_distance(65280, 255) > 0);
EXPECT_FALSE(srs_rtp_seq_distance(0, 65535) > 0);
EXPECT_FALSE(srs_rtp_seq_distance(0, 65280) > 0);
EXPECT_FALSE(srs_rtp_seq_distance(255, 65535) > 0);
EXPECT_FALSE(srs_rtp_seq_distance(255, 65280) > 0);
// Note that srs_rtp_seq_distance(0, 32768)>0 is TRUE by https://mp.weixin.qq.com/s/JZTInmlB9FUWXBQw_7NYqg
// but for WebRTC jitter buffer it's FALSE and we follow it.
EXPECT_FALSE(srs_rtp_seq_distance(0, 32768) > 0);
// It's FALSE definitely.
EXPECT_FALSE(srs_rtp_seq_distance(32768, 0) > 0);
}
if (true) {
EXPECT_FALSE(srs_seq_is_newer(1, 1));
EXPECT_TRUE(srs_seq_is_newer(65535, 65534));
EXPECT_TRUE(srs_seq_is_newer(1, 0));
EXPECT_TRUE(srs_seq_is_newer(256, 255));
EXPECT_TRUE(srs_seq_is_newer(0, 65535));
EXPECT_TRUE(srs_seq_is_newer(0, 65280));
EXPECT_TRUE(srs_seq_is_newer(255, 65535));
EXPECT_TRUE(srs_seq_is_newer(255, 65280));
EXPECT_FALSE(srs_seq_is_newer(65535, 0));
EXPECT_FALSE(srs_seq_is_newer(65280, 0));
EXPECT_FALSE(srs_seq_is_newer(65535, 255));
EXPECT_FALSE(srs_seq_is_newer(65280, 255));
EXPECT_FALSE(srs_seq_is_newer(32768, 0));
EXPECT_FALSE(srs_seq_is_newer(0, 32768));
}
if (true) {
EXPECT_FALSE(srs_seq_distance(1, 1) > 0);
EXPECT_TRUE(srs_seq_distance(65535, 65534) > 0);
EXPECT_TRUE(srs_seq_distance(1, 0) > 0);
EXPECT_TRUE(srs_seq_distance(256, 255) > 0);
EXPECT_TRUE(srs_seq_distance(0, 65535) > 0);
EXPECT_TRUE(srs_seq_distance(0, 65280) > 0);
EXPECT_TRUE(srs_seq_distance(255, 65535) > 0);
EXPECT_TRUE(srs_seq_distance(255, 65280) > 0);
EXPECT_FALSE(srs_seq_distance(65535, 0) > 0);
EXPECT_FALSE(srs_seq_distance(65280, 0) > 0);
EXPECT_FALSE(srs_seq_distance(65535, 255) > 0);
EXPECT_FALSE(srs_seq_distance(65280, 255) > 0);
EXPECT_FALSE(srs_seq_distance(32768, 0) > 0);
EXPECT_FALSE(srs_seq_distance(0, 32768) > 0);
}
if (true) {
EXPECT_FALSE(srs_seq_is_rollback(1, 1));
EXPECT_FALSE(srs_seq_is_rollback(65535, 65534));
EXPECT_FALSE(srs_seq_is_rollback(1, 0));
EXPECT_FALSE(srs_seq_is_rollback(256, 255));
EXPECT_TRUE(srs_seq_is_rollback(0, 65535));
EXPECT_TRUE(srs_seq_is_rollback(0, 65280));
EXPECT_TRUE(srs_seq_is_rollback(255, 65535));
EXPECT_TRUE(srs_seq_is_rollback(255, 65280));
EXPECT_FALSE(srs_seq_is_rollback(65535, 0));
EXPECT_FALSE(srs_seq_is_rollback(65280, 0));
EXPECT_FALSE(srs_seq_is_rollback(65535, 255));
EXPECT_FALSE(srs_seq_is_rollback(65280, 255));
EXPECT_FALSE(srs_seq_is_rollback(32768, 0));
EXPECT_FALSE(srs_seq_is_rollback(0, 32768));
}
}
VOID TEST(KernelRTCTest, DecodeHeaderWithPadding)
{
srs_error_t err = srs_success;
// RTP packet cipher with padding.
uint8_t data[] = {
0xb0, 0x66, 0x0a, 0x97, 0x7e, 0x32, 0x10, 0xee, 0x7d, 0xe6, 0xd0, 0xe6, 0xbe, 0xde, 0x00, 0x01, 0x31, 0x00, 0x16, 0x00, 0x25, 0xcd, 0xef, 0xce, 0xd7, 0x24, 0x57, 0xd9, 0x3c, 0xfd, 0x0f, 0x77, 0xea, 0x89, 0x61, 0xcb, 0x67, 0xa1, 0x65, 0x4a, 0x7d, 0x1f, 0x10, 0x4e, 0xed, 0x5e, 0x74, 0xe8, 0x7e, 0xce, 0x4d, 0xcf, 0xd5, 0x58, 0xd1, 0x2c, 0x30, 0xf1, 0x26, 0x62, 0xd3, 0x0c, 0x6a, 0x48,
0x29, 0x83, 0xd2, 0x3d, 0x30, 0xa1, 0x7c, 0x6f, 0xa1, 0x5c, 0x9f, 0x08, 0x43, 0x50, 0x34, 0x2b, 0x3c, 0xa1, 0xf0, 0xb0, 0xe2, 0x0e, 0xc8, 0xf9, 0x79, 0x06, 0x51, 0xfe, 0xbb, 0x13, 0x54, 0x3e, 0xb4, 0x37, 0x91, 0x96, 0x94, 0xb7, 0x61, 0x2e, 0x97, 0x09, 0xb8, 0x27, 0x10, 0x6a, 0x2e, 0xe0, 0x62, 0xe4, 0x37, 0x41, 0xab, 0x4f, 0xbf, 0x06, 0x0a, 0x89, 0x80, 0x18, 0x0d, 0x6e, 0x0a, 0xd1,
0x9f, 0xf1, 0xdd, 0x12, 0xbd, 0x1a, 0x70, 0x72, 0x33, 0xcc, 0xaa, 0x82, 0xdf, 0x92, 0x90, 0x45, 0xda, 0x3e, 0x88, 0x1c, 0x63, 0x83, 0xbc, 0xc8, 0xff, 0xfd, 0x64, 0xe3, 0xd4, 0x68, 0xe6, 0xc8, 0xdc, 0x81, 0x72, 0x5f, 0x38, 0x5b, 0xab, 0x63, 0x7b, 0x96, 0x03, 0x03, 0x54, 0xc5, 0xe6, 0x35, 0xf6, 0x86, 0xcc, 0xac, 0x74, 0xb0, 0xf4, 0x07, 0x9e, 0x19, 0x30, 0x4f, 0x90, 0xd6, 0xdb, 0x8b,
0x0d, 0xcb, 0x76, 0x71, 0x55, 0xc7, 0x4a, 0x6e, 0x1b, 0xb4, 0x42, 0xf4, 0xae, 0x81, 0x17, 0x08, 0xb7, 0x50, 0x61, 0x5a, 0x42, 0xde, 0x1f, 0xf3, 0xfd, 0xe2, 0x30, 0xff, 0xb7, 0x07, 0xdd, 0x4b, 0xb1, 0x00, 0xd9, 0x6c, 0x43, 0xa0, 0x9a, 0xfa, 0xbb, 0xec, 0xdf, 0x51, 0xce, 0x33, 0x79, 0x4b, 0xa7, 0x02, 0xf3, 0x96, 0x62, 0x42, 0x25, 0x28, 0x85, 0xa7, 0xe7, 0xd1, 0xd3, 0xf3,
};
// If not plaintext, the padding in body is invalid,
// so it will fail if decoding the header with padding.
if (true) {
SrsBuffer b((char*)data, sizeof(data)); SrsRtpHeader h;
HELPER_EXPECT_FAILED(h.decode(&b));
}
// Should ok if ignore padding.
if (true) {
SrsBuffer b((char*)data, sizeof(data)); SrsRtpHeader h;
h.ignore_padding(true);
HELPER_EXPECT_SUCCESS(h.decode(&b));
}
}
VOID TEST(KernelRTCTest, DumpsHexToString)
{
if (true) {
EXPECT_STREQ("", srs_string_dumps_hex(NULL, 0).c_str());
}
if (true) {
uint8_t data[] = {0, 0, 0, 0};
EXPECT_STREQ("00 00 00 00", srs_string_dumps_hex((const char*)data, sizeof(data)).c_str());
}
if (true) {
uint8_t data[] = {0, 1, 2, 3};
EXPECT_STREQ("00 01 02 03", srs_string_dumps_hex((const char*)data, sizeof(data)).c_str());
}
if (true) {
uint8_t data[] = {0xa, 3, 0xf, 3};
EXPECT_STREQ("0a 03 0f 03", srs_string_dumps_hex((const char*)data, sizeof(data)).c_str());
}
if (true) {
uint8_t data[] = {0xa, 3, 0xf, 3};
EXPECT_STREQ("0a,03,0f,03", srs_string_dumps_hex((const char*)data, sizeof(data), INT_MAX, ',', 0, 0).c_str());
EXPECT_STREQ("0a030f03", srs_string_dumps_hex((const char*)data, sizeof(data), INT_MAX, '\0', 0, 0).c_str());
EXPECT_STREQ("0a,03,\n0f,03", srs_string_dumps_hex((const char*)data, sizeof(data), INT_MAX, ',', 2, '\n').c_str());
EXPECT_STREQ("0a,03,0f,03", srs_string_dumps_hex((const char*)data, sizeof(data), INT_MAX, ',', 2,'\0').c_str());
}
if (true) {
uint8_t data[] = {0xa, 3, 0xf};
EXPECT_STREQ("0a 03", srs_string_dumps_hex((const char*)data, sizeof(data), 2).c_str());
}
}
VOID TEST(KernelRTCTest, NACKFetchRTPPacket)
{
SrsRtcConnection s(NULL, SrsContextId());
SrsRtcPlayStream play(&s, SrsContextId());
SrsRtcTrackDescription ds;
SrsRtcVideoSendTrack *track = new SrsRtcVideoSendTrack(&s, &ds);
// The RTP queue will free the packet.
if (true) {
SrsRtpPacket2* pkt = new SrsRtpPacket2();
pkt->header.set_sequence(100);
track->rtp_queue_->set(pkt->header.get_sequence(), pkt);
}
// If sequence not match, packet not found.
if (true) {
SrsRtpPacket2* pkt = track->fetch_rtp_packet(10);
EXPECT_TRUE(pkt == NULL);
}
// The sequence matched, we got the packet.
if (true) {
SrsRtpPacket2* pkt = track->fetch_rtp_packet(100);
EXPECT_TRUE(pkt != NULL);
}
// NACK special case.
if (true) {
// The sequence is the "same", 1100%1000 is 100,
// so we can also get it from the RTP queue.
SrsRtpPacket2* pkt = track->rtp_queue_->at(1100);
EXPECT_TRUE(pkt != NULL);
// But the track requires exactly match, so it returns NULL.
pkt = track->fetch_rtp_packet(1100);
EXPECT_TRUE(pkt == NULL);
}
}
extern bool srs_is_stun(const uint8_t* data, size_t size);
extern bool srs_is_dtls(const uint8_t* data, size_t len);
extern bool srs_is_rtp_or_rtcp(const uint8_t* data, size_t len);
extern bool srs_is_rtcp(const uint8_t* data, size_t len);
#define mock_arr_push(arr, elem) arr.push_back(vector<uint8_t>(elem, elem + sizeof(elem)))
VOID TEST(KernelRTCTest, TestPacketType)
{
// DTLS packet.
vector< vector<uint8_t> > dtlss;
if (true) { uint8_t data[13] = {20}; mock_arr_push(dtlss, data); } // change_cipher_spec(20)
if (true) { uint8_t data[13] = {21}; mock_arr_push(dtlss, data); } // alert(21)
if (true) { uint8_t data[13] = {22}; mock_arr_push(dtlss, data); } // handshake(22)
if (true) { uint8_t data[13] = {23}; mock_arr_push(dtlss, data); } // application_data(23)
for (int i = 0; i < (int)dtlss.size(); i++) {
vector<uint8_t> elem = dtlss.at(i);
EXPECT_TRUE(srs_is_dtls(&elem[0], (size_t)elem.size()));
}
for (int i = 0; i < (int)dtlss.size(); i++) {
vector<uint8_t> elem = dtlss.at(i);
EXPECT_FALSE(srs_is_dtls(&elem[0], 1));
// All DTLS should not be other packets.
EXPECT_FALSE(srs_is_stun(&elem[0], (size_t)elem.size()));
EXPECT_TRUE(srs_is_dtls(&elem[0], (size_t)elem.size()));
EXPECT_FALSE(srs_is_rtp_or_rtcp(&elem[0], (size_t)elem.size()));
EXPECT_FALSE(srs_is_rtcp(&elem[0], (size_t)elem.size()));
}
// STUN packet.
vector< vector<uint8_t> > stuns;
if (true) { uint8_t data[1] = {0}; mock_arr_push(stuns, data); } // binding request.
if (true) { uint8_t data[1] = {1}; mock_arr_push(stuns, data); } // binding success response.
for (int i = 0; i < (int)stuns.size(); i++) {
vector<uint8_t> elem = stuns.at(i);
EXPECT_TRUE(srs_is_stun(&elem[0], (size_t)elem.size()));
}
for (int i = 0; i < (int)stuns.size(); i++) {
vector<uint8_t> elem = stuns.at(i);
EXPECT_FALSE(srs_is_stun(&elem[0], 0));
// All STUN should not be other packets.
EXPECT_TRUE(srs_is_stun(&elem[0], (size_t)elem.size()));
EXPECT_FALSE(srs_is_dtls(&elem[0], (size_t)elem.size()));
EXPECT_FALSE(srs_is_rtp_or_rtcp(&elem[0], (size_t)elem.size()));
EXPECT_FALSE(srs_is_rtcp(&elem[0], (size_t)elem.size()));
}
// RTCP packet.
vector< vector<uint8_t> > rtcps;
if (true) { uint8_t data[12] = {0x80, 192}; mock_arr_push(rtcps, data); }
if (true) { uint8_t data[12] = {0x80, 200}; mock_arr_push(rtcps, data); } // SR
if (true) { uint8_t data[12] = {0x80, 201}; mock_arr_push(rtcps, data); } // RR
if (true) { uint8_t data[12] = {0x80, 202}; mock_arr_push(rtcps, data); } // SDES
if (true) { uint8_t data[12] = {0x80, 203}; mock_arr_push(rtcps, data); } // BYE
if (true) { uint8_t data[12] = {0x80, 204}; mock_arr_push(rtcps, data); } // APP
if (true) { uint8_t data[12] = {0x80, 223}; mock_arr_push(rtcps, data); }
for (int i = 0; i < (int)rtcps.size(); i++) {
vector<uint8_t> elem = rtcps.at(i);
EXPECT_TRUE(srs_is_rtcp(&elem[0], (size_t)elem.size()));
}
for (int i = 0; i < (int)rtcps.size(); i++) {
vector<uint8_t> elem = rtcps.at(i);
EXPECT_FALSE(srs_is_rtcp(&elem[0], 2));
// All RTCP should not be other packets.
EXPECT_FALSE(srs_is_stun(&elem[0], (size_t)elem.size()));
EXPECT_FALSE(srs_is_dtls(&elem[0], (size_t)elem.size()));
EXPECT_TRUE(srs_is_rtp_or_rtcp(&elem[0], (size_t)elem.size()));
EXPECT_TRUE(srs_is_rtcp(&elem[0], (size_t)elem.size()));
}
// RTP packet.
vector< vector<uint8_t> > rtps;
if (true) { uint8_t data[12] = {0x80, 96}; mock_arr_push(rtps, data); }
if (true) { uint8_t data[12] = {0x80, 127}; mock_arr_push(rtps, data); }
if (true) { uint8_t data[12] = {0x80, 224}; mock_arr_push(rtps, data); }
if (true) { uint8_t data[12] = {0x80, 255}; mock_arr_push(rtps, data); }
for (int i = 0; i < (int)rtps.size(); i++) {
vector<uint8_t> elem = rtps.at(i);
EXPECT_TRUE(srs_is_rtp_or_rtcp(&elem[0], (size_t)elem.size()));
EXPECT_FALSE(srs_is_rtcp(&elem[0], (size_t)elem.size()));
}
for (int i = 0; i < (int)rtps.size(); i++) {
vector<uint8_t> elem = rtps.at(i);
EXPECT_FALSE(srs_is_rtp_or_rtcp(&elem[0], 2));
// All RTP should not be other packets.
EXPECT_FALSE(srs_is_stun(&elem[0], (size_t)elem.size()));
EXPECT_FALSE(srs_is_dtls(&elem[0], (size_t)elem.size()));
EXPECT_TRUE(srs_is_rtp_or_rtcp(&elem[0], (size_t)elem.size()));
EXPECT_FALSE(srs_is_rtcp(&elem[0], (size_t)elem.size()));
}
}
VOID TEST(KernelRTCTest, DefaultTrackStatus)
{
// By default, track is disabled.
if (true) {
SrsRtcTrackDescription td;
// The track must default to disable, that is, the active is false.
EXPECT_FALSE(td.is_active_);
}
// Enable it by player.
if (true) {
SrsRtcConnection s(NULL, SrsContextId()); SrsRtcPlayStream play(&s, SrsContextId());
SrsRtcAudioSendTrack* audio; SrsRtcVideoSendTrack *video;
if (true) {
SrsRtcTrackDescription ds; ds.type_ = "audio"; ds.id_ = "NSNWOn19NDn12o8nNeji2"; ds.ssrc_ = 100;
play.audio_tracks_[ds.ssrc_] = audio = new SrsRtcAudioSendTrack(&s, &ds);
}
if (true) {
SrsRtcTrackDescription ds; ds.type_ = "video"; ds.id_ = "VMo22nfLDn122nfnDNL2"; ds.ssrc_ = 200;
play.video_tracks_[ds.ssrc_] = video = new SrsRtcVideoSendTrack(&s, &ds);
}
EXPECT_FALSE(audio->get_track_status());
EXPECT_FALSE(video->get_track_status());
play.set_all_tracks_status(true);
EXPECT_TRUE(audio->get_track_status());
EXPECT_TRUE(video->get_track_status());
}
// Enable it by publisher.
if (true) {
SrsRtcConnection s(NULL, SrsContextId()); SrsRtcPublishStream publish(&s);
SrsRtcAudioRecvTrack* audio; SrsRtcVideoRecvTrack *video;
if (true) {
SrsRtcTrackDescription ds; ds.type_ = "audio"; ds.id_ = "NSNWOn19NDn12o8nNeji2"; ds.ssrc_ = 100;
audio = new SrsRtcAudioRecvTrack(&s, &ds); publish.audio_tracks_.push_back(audio);
}
if (true) {
SrsRtcTrackDescription ds; ds.type_ = "video"; ds.id_ = "VMo22nfLDn122nfnDNL2"; ds.ssrc_ = 200;
video = new SrsRtcVideoRecvTrack(&s, &ds); publish.video_tracks_.push_back(video);
}
EXPECT_FALSE(audio->get_track_status());
EXPECT_FALSE(video->get_track_status());
publish.set_all_tracks_status(true);
EXPECT_TRUE(audio->get_track_status());
EXPECT_TRUE(video->get_track_status());
}
}