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

Merge branch 'develop' into 4.0release

This commit is contained in:
winlin 2020-08-18 19:56:53 +08:00
commit e73e7e47b1
26 changed files with 879 additions and 384 deletions

View file

@ -159,6 +159,7 @@ For previous versions, please read:
## V4 changes
* v4.0, 2020-08-06, RTC: Refine error check. 4.0.37
* v4.0, 2020-07-25, RTC: Support multiple address for client. 4.0.36
* v4.0, 2020-07-11, Refine log context with random string. 4.0.35
* v4.0, 2020-07-04, Fix some bugs for RTC. 4.0.34

View file

@ -666,14 +666,15 @@ if [ $SRS_FFMPEG_TOOL = YES ]; then
if [[ -f ${SRS_OBJS}/${SRS_PLATFORM}/ffmpeg/bin/ffmpeg ]]; then
echo "ffmpeg-4.1 is ok.";
else
echo "Error: no FFmpeg found at /usr/local/bin/ffmpeg";
echo "Warning: No FFmpeg found at /usr/local/bin/ffmpeg";
echo " please copy it from srs-docker";
echo " or download from http://ffmpeg.org/download.html";
echo " or disable it by --without-ffmpeg";
exit -1;
fi
# Always update the links.
(cd ${SRS_OBJS} && rm -rf ffmpeg && ln -sf ${SRS_PLATFORM}/ffmpeg)
if [[ -f ${SRS_OBJS}/${SRS_PLATFORM}/ffmpeg ]]; then
(cd ${SRS_OBJS} && rm -rf ffmpeg && ln -sf ${SRS_PLATFORM}/ffmpeg)
fi
fi
#####################################################################################

View file

@ -482,24 +482,24 @@ function apply_detail_options() {
# if specified export single file, export project first.
if [ $SRS_EXPORT_LIBRTMP_SINGLE != NO ]; then
echo "Not support --export-librtmp-single"
exit -1
echo "Warning: Ingore --export-librtmp-single"
SRS_EXPORT_LIBRTMP_SINGLE=NO
fi
# disable almost all features for export srs-librtmp.
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
echo "Not support --export-librtmp-project"
exit -1
echo "Warning: Ingore --export-librtmp-project"
SRS_EXPORT_LIBRTMP_PROJECT=NO
fi
if [[ $SRS_LIBRTMP != NO ]]; then
echo "Not support --librtmp"
exit -1
echo "Warning: Ingore --librtmp"
SRS_LIBRTMP=NO
fi
if [[ $SRS_RESEARCH != NO ]]; then
echo "Not support --research"
exit -1
echo "Warning: Ingore --research"
SRS_RESEARCH=NO
fi
if [[ $SRS_SRTP_ASM == YES && $SRS_RTC == NO ]]; then
@ -583,11 +583,13 @@ function check_option_conflicts() {
fi
if [[ $SRS_CROSS_BUILD == YES && ($SRS_TOOL_CC == 'gcc' || $SRS_TOOL_CXX == 'g++' || $SRS_TOOL_AR == 'ar') ]]; then
echo "For crossbuild, must not use default toolchain, cc: $SRS_TOOL_CC, cxx: $SRS_TOOL_CXX, ar: $SRS_TOOL_AR"; exit -1
echo "Warning: For crossbuild, must not use default toolchain, cc: $SRS_TOOL_CC, cxx: $SRS_TOOL_CXX, ar: $SRS_TOOL_AR"
SRS_CROSS_BUILD=NO
fi
if [[ $SRS_NGINX == YES ]]; then
echo "Don't support building NGINX, please use docker https://github.com/ossrs/srs-docker"; exit -1;
echo "Warning: Don't support building NGINX, please use docker https://github.com/ossrs/srs-docker"
SRS_NGINX=NO
fi
# For OSX, recommend to use DTrace, https://blog.csdn.net/win_lin/article/details/53503869

View file

@ -49,7 +49,7 @@ USER_DIR = .
CPPFLAGS += -I\$(GTEST_DIR)/include
# Flags passed to the C++ compiler.
CXXFLAGS += ${CXXFLAGS} -Wextra ${UTEST_EXTRA_DEFINES}
CXXFLAGS += ${CXXFLAGS} ${UTEST_EXTRA_DEFINES} -Wno-unused-private-field -Wno-unused-command-line-argument
# All tests produced by this Makefile. Remember to add new tests you
# created to the list.

View file

@ -42,7 +42,6 @@ using namespace std;
#include <srs_app_utility.hpp>
#include <srs_kernel_utility.hpp>
// set the max packet size.
#define SRS_UDP_MAX_PACKET_SIZE 65535
@ -506,8 +505,8 @@ srs_error_t SrsUdpMuxListener::cycle()
uint64_t nn_loop = 0;
srs_utime_t time_last = srs_get_system_time();
SrsErrorPithyPrint* epp = new SrsErrorPithyPrint();
SrsAutoFree(SrsErrorPithyPrint, epp);
SrsErrorPithyPrint* pp_pkt_handler_err = new SrsErrorPithyPrint();
SrsAutoFree(SrsErrorPithyPrint, pp_pkt_handler_err);
set_socket_buffer();
@ -541,9 +540,15 @@ srs_error_t SrsUdpMuxListener::cycle()
SrsContextRestore(cid);
err = handler->on_udp_packet(&skt);
}
// Use pithy print to show more smart information.
if (err != srs_success) {
if (epp->can_print(err)) {
srs_warn("handle udp pkt err: %s", srs_error_desc(err).c_str());
if (pp_pkt_handler_err->can_print(err)) {
// Append more information.
if (true) {
char* data = skt.data(); int size = skt.size();
err = srs_error_wrap(err, "size=%u, data=[%s]", size, srs_string_dumps_hex(data, size, 8).c_str());
}
srs_warn("handle udp pkt, count=%u, err: %s", pp_pkt_handler_err->nn_count, srs_error_desc(err).c_str());
}
srs_freep(err);
}

View file

@ -116,6 +116,7 @@ SrsStageInfo* SrsStageManager::fetch_or_create(int stage_id, bool* pnew)
SrsErrorPithyPrint::SrsErrorPithyPrint()
{
nn_count = 0;
}
SrsErrorPithyPrint::~SrsErrorPithyPrint()
@ -125,6 +126,12 @@ SrsErrorPithyPrint::~SrsErrorPithyPrint()
bool SrsErrorPithyPrint::can_print(srs_error_t err)
{
int error_code = srs_error_code(err);
return can_print(error_code);
}
bool SrsErrorPithyPrint::can_print(int error_code)
{
nn_count++;
bool new_stage = false;
SrsStageInfo* stage = stages.fetch_or_create(error_code, &new_stage);

View file

@ -68,6 +68,9 @@ public:
// For example, we use it for error pithy print for each UDP packet processing.
class SrsErrorPithyPrint
{
public:
// The number of call of can_print().
uint32_t nn_count;
private:
SrsStageManager stages;
std::map<int, srs_utime_t> ticks;
@ -77,6 +80,8 @@ public:
public:
// Whether specified stage is ready for print.
bool can_print(srs_error_t err);
// We also support int error code.
bool can_print(int err);
};
// The stage is used for a collection of object to do print,

View file

@ -195,6 +195,8 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
}
string local_sdp_str = os.str();
// Filter the \r\n to \\r\\n for JSON.
local_sdp_str = srs_string_replace(local_sdp_str.c_str(), "\r\n", "\\r\\n");
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
res->set("server", SrsJsonAny::integer(SrsStatistic::instance()->server_id()));
@ -207,7 +209,7 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
srs_trace("RTC username=%s, offer=%dB, answer=%dB", session->username().c_str(),
remote_sdp_str.length(), local_sdp_str.length());
srs_trace("RTC remote offer: %s", srs_string_replace(remote_sdp_str.c_str(), "\r\n", "\\r\\n").c_str());
srs_trace("RTC local answer: %s", srs_string_replace(local_sdp_str.c_str(), "\r\n", "\\r\\n").c_str());
srs_trace("RTC local answer: %s", local_sdp_str.c_str());
return err;
}
@ -545,6 +547,8 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHtt
}
string local_sdp_str = os.str();
// Filter the \r\n to \\r\\n for JSON.
local_sdp_str = srs_string_replace(local_sdp_str.c_str(), "\r\n", "\\r\\n");
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
res->set("server", SrsJsonAny::integer(SrsStatistic::instance()->server_id()));
@ -557,7 +561,7 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHtt
srs_trace("RTC username=%s, offer=%dB, answer=%dB", session->username().c_str(),
remote_sdp_str.length(), local_sdp_str.length());
srs_trace("RTC remote offer: %s", srs_string_replace(remote_sdp_str.c_str(), "\r\n", "\\r\\n").c_str());
srs_trace("RTC local answer: %s", srs_string_replace(local_sdp_str.c_str(), "\r\n", "\\r\\n").c_str());
srs_trace("RTC local answer: %s", local_sdp_str.c_str());
return err;
}

View file

@ -58,6 +58,8 @@ using namespace std;
#include <srs_app_rtc_source.hpp>
#include <srs_protocol_utility.hpp>
#define SRS_TICKID_RTCP 0
SrsSecurityTransport::SrsSecurityTransport(SrsRtcConnection* s)
{
session_ = s;
@ -87,10 +89,13 @@ srs_error_t SrsSecurityTransport::start_active_handshake()
srs_error_t SrsSecurityTransport::write_dtls_data(void* data, int size)
{
srs_error_t err = srs_success;
if (size) {
if ((err = session_->sendonly_skt->sendto(data, size, 0)) != srs_success) {
return srs_error_wrap(err, "send dtls packet");
}
if (!size) {
return err;
}
if ((err = session_->sendonly_skt->sendto(data, size, 0)) != srs_success) {
return srs_error_wrap(err, "send dtls packet");
}
if (_srs_blackhole->blackhole) {
@ -112,13 +117,13 @@ srs_error_t SrsSecurityTransport::on_dtls_handshake_done()
if (handshake_done) {
return err;
}
handshake_done = true;
// TODO: FIXME: Add cost for DTLS.
srs_trace("RTC: DTLS handshake done.");
handshake_done = true;
if ((err = srtp_initialize()) != srs_success) {
return srs_error_wrap(err, "srtp init failed");
return srs_error_wrap(err, "srtp init");
}
return session_->on_connection_established();
@ -145,7 +150,7 @@ srs_error_t SrsSecurityTransport::srtp_initialize()
}
if ((err = srtp_->initialize(recv_key, send_key)) != srs_success) {
return srs_error_wrap(err, "srtp init failed");
return srs_error_wrap(err, "srtp init");
}
return err;
@ -153,56 +158,32 @@ srs_error_t SrsSecurityTransport::srtp_initialize()
srs_error_t SrsSecurityTransport::protect_rtp(const char* plaintext, char* cipher, int& nb_cipher)
{
if (!srtp_) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect failed");
}
return srtp_->protect_rtp(plaintext, cipher, nb_cipher);
}
srs_error_t SrsSecurityTransport::protect_rtcp(const char* plaintext, char* cipher, int& nb_cipher)
{
if (!srtp_) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtcp protect failed");
}
return srtp_->protect_rtcp(plaintext, cipher, nb_cipher);
}
// TODO: FIXME: Merge with protect_rtp.
srs_error_t SrsSecurityTransport::protect_rtp2(void* rtp_hdr, int* len_ptr)
{
if (!srtp_) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect");
}
return srtp_->protect_rtp2(rtp_hdr, len_ptr);
}
srs_error_t SrsSecurityTransport::unprotect_rtp(const char* cipher, char* plaintext, int& nb_plaintext)
{
if (!srtp_) {
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "rtp unprotect failed");
}
return srtp_->unprotect_rtp(cipher, plaintext, nb_plaintext);
}
srs_error_t SrsSecurityTransport::unprotect_rtcp(const char* cipher, char* plaintext, int& nb_plaintext)
{
if (!srtp_) {
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "rtcp unprotect failed");
}
return srtp_->unprotect_rtcp(cipher, plaintext, nb_plaintext);
}
SrsRtcPlayStreamStatistic::SrsRtcPlayStreamStatistic()
{
#if defined(SRS_DEBUG)
debug_id = 0;
#endif
nn_rtp_pkts = 0;
nn_audios = nn_extras = 0;
nn_videos = nn_samples = 0;
@ -227,6 +208,7 @@ SrsRtcPlayStream::SrsRtcPlayStream(SrsRtcConnection* s, SrsContextId parent_cid)
nack_enabled_ = false;
_srs_config->subscribe(this);
timer_ = new SrsHourGlass(this, 1000 * SRS_UTIME_MILLISECONDS);
}
SrsRtcPlayStream::~SrsRtcPlayStream()
@ -234,6 +216,7 @@ SrsRtcPlayStream::~SrsRtcPlayStream()
_srs_config->unsubscribe(this);
srs_freep(trd);
srs_freep(timer_);
if (true) {
std::map<uint32_t, SrsRtcAudioSendTrack*>::iterator it;
@ -321,6 +304,10 @@ srs_error_t SrsRtcPlayStream::start()
return srs_error_wrap(err, "rtc_sender");
}
if ((err = timer_->start()) != srs_success) {
return srs_error_wrap(err, "start timer");
}
if (_srs_rtc_hijacker) {
if ((err = _srs_rtc_hijacker->on_start_play(session_, this, session_->req)) != srs_success) {
return srs_error_wrap(err, "on start play");
@ -345,18 +332,18 @@ srs_error_t SrsRtcPlayStream::cycle()
SrsRequest* req = session_->req;
if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) {
return srs_error_wrap(err, "rtc fetch source failed");
return srs_error_wrap(err, "fetch source");
}
SrsRtcConsumer* consumer = NULL;
SrsAutoFree(SrsRtcConsumer, consumer);
if ((err = source->create_consumer(consumer)) != srs_success) {
return srs_error_wrap(err, "rtc create consumer, source url=%s", req->get_stream_url().c_str());
return srs_error_wrap(err, "create consumer, source=%s", req->get_stream_url().c_str());
}
// TODO: FIXME: Dumps the SPS/PPS from gop cache, without other frames.
if ((err = source->consumer_dumps(consumer)) != srs_success) {
return srs_error_wrap(err, "dumps consumer, source url=%s", req->get_stream_url().c_str());
return srs_error_wrap(err, "dumps consumer, url=%s", req->get_stream_url().c_str());
}
realtime = _srs_config->get_realtime_enabled(req->vhost, true);
@ -439,11 +426,6 @@ srs_error_t SrsRtcPlayStream::send_packets(SrsRtcStream* source, const vector<Sr
{
srs_error_t err = srs_success;
// If DTLS is not OK, drop all messages.
if (!session_->transport_) {
return err;
}
vector<SrsRtpPacket2*> send_pkts;
// Covert kernel messages to RTP packets.
for (int i = 0; i < (int)pkts.size(); i++) {
@ -483,8 +465,13 @@ void SrsRtcPlayStream::nack_fetch(vector<SrsRtpPacket2*>& pkts, uint32_t ssrc, u
if (true) {
std::map<uint32_t, SrsRtcAudioSendTrack*>::iterator it;
for (it = audio_tracks_.begin(); it != audio_tracks_.end(); ++it) {
if (it->second->has_ssrc(ssrc)) {
SrsRtpPacket2* pkt = it->second->fetch_rtp_packet(seq);
SrsRtcAudioSendTrack* track = it->second;
if (track->has_ssrc(ssrc)) {
// update recv nack statistic
track->on_recv_nack();
SrsRtpPacket2* pkt = track->fetch_rtp_packet(seq);
if (pkt != NULL) {
pkts.push_back(pkt);
}
@ -496,8 +483,13 @@ void SrsRtcPlayStream::nack_fetch(vector<SrsRtpPacket2*>& pkts, uint32_t ssrc, u
if (true) {
std::map<uint32_t, SrsRtcVideoSendTrack*>::iterator it;
for (it = video_tracks_.begin(); it != video_tracks_.end(); ++it) {
if (it->second->has_ssrc(ssrc)) {
SrsRtpPacket2* pkt = it->second->fetch_rtp_packet(seq);
SrsRtcVideoSendTrack* track = it->second;
if (track->has_ssrc(ssrc)) {
// update recv nack statistic
track->on_recv_nack();
SrsRtpPacket2* pkt = track->fetch_rtp_packet(seq);
if (pkt != NULL) {
pkts.push_back(pkt);
}
@ -507,12 +499,54 @@ void SrsRtcPlayStream::nack_fetch(vector<SrsRtpPacket2*>& pkts, uint32_t ssrc, u
}
}
void SrsRtcPlayStream::set_all_tracks_status(bool status)
{
std::ostringstream merged_log;
// set video track status
if (true) {
std::map<uint32_t, SrsRtcVideoSendTrack*>::iterator it;
for (it = video_tracks_.begin(); it != video_tracks_.end(); ++it) {
SrsRtcVideoSendTrack* track = it->second;
bool previous = track->set_track_status(status);
merged_log << "{track: " << track->get_track_id() << ", is_active: " << previous << "=>" << status << "},";
}
}
// set audio track status
if (true) {
std::map<uint32_t, SrsRtcAudioSendTrack*>::iterator it;
for (it = audio_tracks_.begin(); it != audio_tracks_.end(); ++it) {
SrsRtcAudioSendTrack* track = it->second;
bool previous = track->set_track_status(status);
merged_log << "{track: " << track->get_track_id() << ", is_active: " << previous << "=>" << status << "},";
}
}
srs_trace("RTC: Init tracks %s ok", merged_log.str().c_str());
}
srs_error_t SrsRtcPlayStream::notify(int type, srs_utime_t interval, srs_utime_t tick)
{
srs_error_t err = srs_success;
if (!is_started) {
return err;
}
return err;
}
srs_error_t SrsRtcPlayStream::on_rtcp(char* data, int nb_data)
{
srs_error_t err = srs_success;
// TODO: Use SrsBuffer to parse it.
char* ph = data;
int nb_left = nb_data;
while (nb_left) {
uint8_t payload_type = ph[1];
uint16_t length_4bytes = (((uint16_t)ph[2]) << 8) | ph[3];
@ -520,7 +554,8 @@ srs_error_t SrsRtcPlayStream::on_rtcp(char* data, int nb_data)
int length = (length_4bytes + 1) * 4;
if (length > nb_data) {
return srs_error_new(ERROR_RTC_RTCP, "invalid rtcp packet, length=%u", length);
return srs_error_new(ERROR_RTC_RTCP, "invalid length=%u/%u, left=%u, bytes=%s",
length, nb_data, nb_left, srs_string_dumps_hex(ph, nb_left, 8).c_str());
}
srs_verbose("on rtcp, payload_type=%u", payload_type);
@ -562,7 +597,7 @@ srs_error_t SrsRtcPlayStream::on_rtcp(char* data, int nb_data)
}
if (err != srs_success) {
return srs_error_wrap(err, "rtcp");
return srs_error_wrap(err, "rtcp left=%u, bytes=%s", nb_left, srs_string_dumps_hex(ph, nb_left, 8).c_str());
}
ph += length;
@ -760,11 +795,12 @@ uint32_t SrsRtcPlayStream::get_video_publish_ssrc(uint32_t play_ssrc)
SrsRtcPublishStream::SrsRtcPublishStream(SrsRtcConnection* session)
{
report_timer = new SrsHourGlass(this, 200 * SRS_UTIME_MILLISECONDS);
timer_ = new SrsHourGlass(this, 200 * SRS_UTIME_MILLISECONDS);
session_ = session;
request_keyframe_ = false;
req = NULL;
source = NULL;
nn_simulate_nack_drop = 0;
nack_enabled_ = false;
@ -772,7 +808,6 @@ SrsRtcPublishStream::SrsRtcPublishStream(SrsRtcConnection* session)
nn_audio_frames = 0;
twcc_id_ = 0;
last_twcc_feedback_time_ = 0;
twcc_fb_count_ = 0;
}
@ -785,7 +820,7 @@ SrsRtcPublishStream::~SrsRtcPublishStream()
}
srs_freep(req);
srs_freep(report_timer);
srs_freep(timer_);
}
srs_error_t SrsRtcPublishStream::initialize(SrsRequest* r, SrsRtcStreamDescription* stream_desc)
@ -795,7 +830,7 @@ srs_error_t SrsRtcPublishStream::initialize(SrsRequest* r, SrsRtcStreamDescripti
req = r->copy();
audio_tracks_.push_back(new SrsRtcAudioRecvTrack(session_, stream_desc->audio_track_desc_));
for (int i = 0; i < stream_desc->video_track_descs_.size(); ++i) {
for (int i = 0; i < (int)stream_desc->video_track_descs_.size(); ++i) {
SrsRtcTrackDescription* desc = stream_desc->video_track_descs_.at(i);
video_tracks_.push_back(new SrsRtcVideoRecvTrack(session_, desc));
}
@ -804,7 +839,7 @@ srs_error_t SrsRtcPublishStream::initialize(SrsRequest* r, SrsRtcStreamDescripti
uint32_t media_ssrc = 0;
// because audio_track_desc have not twcc id, for example, h5demo
// fetch twcc_id from video track description,
for (int i = 0; i < stream_desc->video_track_descs_.size(); ++i) {
for (int i = 0; i < (int)stream_desc->video_track_descs_.size(); ++i) {
SrsRtcTrackDescription* desc = stream_desc->video_track_descs_.at(i);
twcc_id = desc->get_rtp_extension_id(kTWCCExt);
media_ssrc = desc->ssrc_;
@ -831,17 +866,16 @@ srs_error_t SrsRtcPublishStream::start()
{
srs_error_t err = srs_success;
// If report_timer started, we think the publisher is started.
if (is_started) {
return err;
}
if ((err = report_timer->tick(0 * SRS_UTIME_MILLISECONDS)) != srs_success) {
return srs_error_wrap(err, "hourglass tick");
if ((err = timer_->tick(SRS_TICKID_RTCP, 200 * SRS_UTIME_MILLISECONDS)) != srs_success) {
return srs_error_wrap(err, "rtcp tick");
}
if ((err = report_timer->start()) != srs_success) {
return srs_error_wrap(err, "start report_timer");
if ((err = timer_->start()) != srs_success) {
return srs_error_wrap(err, "start timer");
}
if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) {
@ -865,18 +899,51 @@ srs_error_t SrsRtcPublishStream::start()
return err;
}
void SrsRtcPublishStream::set_all_tracks_status(bool status)
{
std::ostringstream merged_log;
// set video track status
if (true) {
std::vector<SrsRtcVideoRecvTrack*>::iterator it;
for (it = video_tracks_.begin(); it != video_tracks_.end(); ++it) {
SrsRtcVideoRecvTrack* track = *it;
bool previous = track->set_track_status(status);
merged_log << "{track: " << track->get_track_id() << ", is_active: " << previous << "=>" << status << "},";
}
}
// set audio track status
if (true) {
std::vector<SrsRtcAudioRecvTrack*>::iterator it;
for (it = audio_tracks_.begin(); it != audio_tracks_.end(); ++it) {
SrsRtcAudioRecvTrack* track = *it;
bool previous = track->set_track_status(status);
merged_log << "{track: " << track->get_track_id() << ", is_active: " << previous << "=>" << status << "},";
}
}
srs_trace("RTC: Init tracks %s ok", merged_log.str().c_str());
}
srs_error_t SrsRtcPublishStream::send_rtcp_rr()
{
srs_error_t err = srs_success;
for (int i = 0; i < video_tracks_.size(); ++i) {
for (int i = 0; i < (int)video_tracks_.size(); ++i) {
SrsRtcVideoRecvTrack* track = video_tracks_.at(i);
track->send_rtcp_rr();
if ((err = track->send_rtcp_rr()) != srs_success) {
return srs_error_wrap(err, "track=%s", track->get_track_id().c_str());
}
}
for (int i = 0; i < audio_tracks_.size(); ++i) {
for (int i = 0; i < (int)audio_tracks_.size(); ++i) {
SrsRtcAudioRecvTrack* track = audio_tracks_.at(i);
track->send_rtcp_rr();
if ((err = track->send_rtcp_rr()) != srs_success) {
return srs_error_wrap(err, "track=%s", track->get_track_id().c_str());
}
}
session_->stat_->nn_rr++;
@ -888,14 +955,18 @@ srs_error_t SrsRtcPublishStream::send_rtcp_xr_rrtr()
{
srs_error_t err = srs_success;
for (int i = 0; i < video_tracks_.size(); ++i) {
for (int i = 0; i < (int)video_tracks_.size(); ++i) {
SrsRtcVideoRecvTrack* track = video_tracks_.at(i);
track->send_rtcp_xr_rrtr();
if ((err = track->send_rtcp_xr_rrtr()) != srs_success) {
return srs_error_wrap(err, "track=%s", track->get_track_id().c_str());
}
}
for (int i = 0; i < audio_tracks_.size(); ++i) {
for (int i = 0; i < (int)audio_tracks_.size(); ++i) {
SrsRtcAudioRecvTrack* track = audio_tracks_.at(i);
track->send_rtcp_xr_rrtr();
if ((err = track->send_rtcp_xr_rrtr()) != srs_success) {
return srs_error_wrap(err, "track=%s", track->get_track_id().c_str());
}
}
session_->stat_->nn_xr++;
@ -974,8 +1045,24 @@ srs_error_t SrsRtcPublishStream::on_rtp(char* data, int nb_data)
_srs_blackhole->sendto(unprotected_buf, nb_unprotected_buf);
}
char* buf = unprotected_buf;
int nb_buf = nb_unprotected_buf;
// Handle the plaintext RTP packet.
if ((err = do_on_rtp(unprotected_buf, nb_unprotected_buf)) != srs_success) {
int nb_header = h.nb_bytes();
const char* body = unprotected_buf + nb_header;
int nb_body = nb_unprotected_buf - nb_header;
return srs_error_wrap(err, "cipher=%u, plaintext=%u, body=%s", nb_data, nb_unprotected_buf,
srs_string_dumps_hex(body, nb_body, 8).c_str());
}
return err;
}
srs_error_t SrsRtcPublishStream::do_on_rtp(char* plaintext, int nb_plaintext)
{
srs_error_t err = srs_success;
char* buf = plaintext;
int nb_buf = nb_plaintext;
// Decode the RTP packet from buffer.
SrsRtpPacket2* pkt = new SrsRtpPacket2();
@ -1045,30 +1132,25 @@ void SrsRtcPublishStream::on_before_decode_payload(SrsRtpPacket2* pkt, SrsBuffer
srs_error_t SrsRtcPublishStream::send_periodic_twcc()
{
srs_error_t err = srs_success;
srs_utime_t now = srs_get_system_time();
if(0 == last_twcc_feedback_time_) {
last_twcc_feedback_time_ = now;
return err;
}
srs_utime_t diff = now - last_twcc_feedback_time_;
if( diff >= 50 * SRS_UTIME_MILLISECONDS) {
last_twcc_feedback_time_ = now;
char pkt[kRtcpPacketSize];
SrsBuffer *buffer = new SrsBuffer(pkt, sizeof(pkt));
SrsAutoFree(SrsBuffer, buffer);
rtcp_twcc_.set_feedback_count(twcc_fb_count_);
twcc_fb_count_++;
if((err = rtcp_twcc_.encode(buffer)) != srs_success) {
return srs_error_wrap(err, "fail to generate twcc feedback packet");
}
int nb_protected_buf = buffer->pos();
char protected_buf[kRtpPacketSize];
if (session_->transport_->protect_rtcp(pkt, protected_buf, nb_protected_buf) == srs_success) {
session_->sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
}
char pkt[kRtcpPacketSize];
SrsBuffer *buffer = new SrsBuffer(pkt, sizeof(pkt));
SrsAutoFree(SrsBuffer, buffer);
rtcp_twcc_.set_feedback_count(twcc_fb_count_);
twcc_fb_count_++;
if((err = rtcp_twcc_.encode(buffer)) != srs_success) {
return srs_error_wrap(err, "encode, count=%u", twcc_fb_count_);
}
return err;
int nb_protected_buf = buffer->pos();
char protected_buf[kRtpPacketSize];
if ((err = session_->transport_->protect_rtcp(pkt, protected_buf, nb_protected_buf)) != srs_success) {
return srs_error_wrap(err, "protect rtcp, size=%u", nb_protected_buf);
}
return session_->sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
}
srs_error_t SrsRtcPublishStream::on_rtcp(char* data, int nb_data)
@ -1427,14 +1509,28 @@ srs_error_t SrsRtcPublishStream::notify(int type, srs_utime_t interval, srs_utim
{
srs_error_t err = srs_success;
// TODO: FIXME: Check error.
send_rtcp_rr();
send_rtcp_xr_rrtr();
if (!is_started) {
return err;
}
// TODO: FIXME: Check error.
// We should not depends on the received packet,
// instead we should send feedback every Nms.
send_periodic_twcc();
if (type == SRS_TICKID_RTCP) {
if ((err = send_rtcp_rr()) != srs_success) {
srs_warn("RR err %s", srs_error_desc(err).c_str());
srs_freep(err);
}
if ((err = send_rtcp_xr_rrtr()) != srs_success) {
srs_warn("XR err %s", srs_error_desc(err).c_str());
srs_freep(err);
}
// We should not depends on the received packet,
// instead we should send feedback every Nms.
if ((err = send_periodic_twcc()) != srs_success) {
srs_warn("TWCC err %s", srs_error_desc(err).c_str());
srs_freep(err);
}
}
return err;
}
@ -1455,7 +1551,7 @@ void SrsRtcPublishStream::simulate_drop_packet(SrsRtpHeader* h, int nn_bytes)
SrsRtcVideoRecvTrack* SrsRtcPublishStream::get_video_track(uint32_t ssrc)
{
for (int i = 0; i < video_tracks_.size(); ++i) {
for (int i = 0; i < (int)video_tracks_.size(); ++i) {
SrsRtcVideoRecvTrack* track = video_tracks_.at(i);
if (track->has_ssrc(ssrc)) {
return track;
@ -1467,7 +1563,7 @@ SrsRtcVideoRecvTrack* SrsRtcPublishStream::get_video_track(uint32_t ssrc)
SrsRtcAudioRecvTrack* SrsRtcPublishStream::get_audio_track(uint32_t ssrc)
{
for (int i = 0; i < audio_tracks_.size(); ++i) {
for (int i = 0; i < (int)audio_tracks_.size(); ++i) {
SrsRtcAudioRecvTrack* track = audio_tracks_.at(i);
if (track->has_ssrc(ssrc)) {
return track;
@ -1555,6 +1651,7 @@ SrsRtcConnection::SrsRtcConnection(SrsRtcServer* s, SrsContextId context_id)
encrypt = true;
cid = context_id;
stat_ = new SrsRtcConnectionStatistic();
timer_ = new SrsHourGlass(this, 1000 * SRS_UTIME_MILLISECONDS);
source_ = NULL;
publisher_ = NULL;
@ -1570,10 +1667,12 @@ SrsRtcConnection::SrsRtcConnection(SrsRtcServer* s, SrsContextId context_id)
twcc_id_ = 0;
nn_simulate_player_nack_drop = 0;
pp_address_change = new SrsErrorPithyPrint();
}
SrsRtcConnection::~SrsRtcConnection()
{
srs_freep(timer_);
srs_freep(player_);
srs_freep(publisher_);
srs_freep(transport_);
@ -1587,6 +1686,8 @@ SrsRtcConnection::~SrsRtcConnection()
SrsUdpMuxSocket* addr = it->second;
srs_freep(addr);
}
srs_freep(pp_address_change);
}
SrsSdp* SrsRtcConnection::get_local_sdp()
@ -1775,6 +1876,10 @@ srs_error_t SrsRtcConnection::initialize(SrsRtcStream* source, SrsRequest* r, bo
return srs_error_wrap(err, "init");
}
if ((err = timer_->start()) != srs_success) {
return srs_error_wrap(err, "start timer");
}
// TODO: FIXME: Support reload.
session_timeout = _srs_config->get_rtc_stun_timeout(req->vhost);
last_stun_time = srs_get_system_time();
@ -1820,14 +1925,10 @@ srs_error_t SrsRtcConnection::on_rtcp(char* data, int nb_data)
{
srs_error_t err = srs_success;
if (transport_ == NULL) {
return srs_error_new(ERROR_RTC_RTCP, "recv unexpect rtp packet before dtls done");
}
char unprotected_buf[kRtpPacketSize];
int nb_unprotected_buf = nb_data;
if ((err = transport_->unprotect_rtcp(data, unprotected_buf, nb_unprotected_buf)) != srs_success) {
return srs_error_wrap(err, "rtcp unprotect failed");
return srs_error_wrap(err, "rtcp unprotect");
}
if (_srs_blackhole->blackhole) {
@ -1835,11 +1936,16 @@ srs_error_t SrsRtcConnection::on_rtcp(char* data, int nb_data)
}
if (player_) {
return player_->on_rtcp(unprotected_buf, nb_unprotected_buf);
err = player_->on_rtcp(unprotected_buf, nb_unprotected_buf);
}
if (publisher_) {
return publisher_->on_rtcp(unprotected_buf, nb_unprotected_buf);
err = publisher_->on_rtcp(unprotected_buf, nb_unprotected_buf);
}
if (err != srs_success) {
return srs_error_wrap(err, "cipher=%u, plaintext=%u, bytes=%s", nb_data, nb_unprotected_buf,
srs_string_dumps_hex(unprotected_buf, nb_unprotected_buf, 8).c_str());
}
return err;
@ -1853,11 +1959,7 @@ srs_error_t SrsRtcConnection::on_rtcp_feedback(char* data, int nb_data)
srs_error_t SrsRtcConnection::on_rtp(char* data, int nb_data)
{
if (publisher_ == NULL) {
return srs_error_new(ERROR_RTC_RTCP, "rtc publisher null");
}
if (transport_ == NULL) {
return srs_error_new(ERROR_RTC_RTCP, "recv unexpect rtp packet before dtls done");
return srs_error_new(ERROR_RTC_RTCP, "no publisher");
}
//TODO: FIXME: add unprotect_rtcp.
@ -1924,14 +2026,6 @@ void SrsRtcConnection::update_sendonly_socket(SrsUdpMuxSocket* skt)
return;
}
// Detect address change.
if (prev_peer_id.empty()) {
srs_trace("RTC: session address init %s", peer_id.c_str());
} else {
srs_trace("RTC: session address changed, update %s -> %s, total %u", prev_peer_id.c_str(),
peer_id.c_str(), peer_addresses_.size());
}
// Find object from cache.
SrsUdpMuxSocket* addr_cache = NULL;
if (true) {
@ -1941,6 +2035,14 @@ void SrsRtcConnection::update_sendonly_socket(SrsUdpMuxSocket* skt)
}
}
// Show address change log.
if (prev_peer_id.empty()) {
srs_trace("RTC: session address init %s", peer_id.c_str());
} else if (pp_address_change->can_print(skt->get_peer_port())) {
srs_trace("RTC: session address change %s -> %s, cached=%d, nn_change=%u, nn_address=%u", prev_peer_id.c_str(),
peer_id.c_str(), (addr_cache? 1:0), pp_address_change->nn_count, peer_addresses_.size());
}
// If no cache, build cache and setup the relations in connection.
if (!addr_cache) {
peer_addresses_[peer_id] = addr_cache = skt->copy_sendonly();
@ -1951,13 +2053,14 @@ void SrsRtcConnection::update_sendonly_socket(SrsUdpMuxSocket* skt)
sendonly_skt = addr_cache;
}
void SrsRtcConnection::check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc)
srs_error_t SrsRtcConnection::notify(int type, srs_utime_t interval, srs_utime_t tick)
{
// If DTLS is not OK, drop all messages.
if (!transport_) {
return;
}
srs_error_t err = srs_success;
return err;
}
void SrsRtcConnection::check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc, uint32_t& sent_nacks)
{
// @see: https://tools.ietf.org/html/rfc4585#section-6.1
vector<uint16_t> nack_seqs;
nack->get_nack_seqs(nack_seqs);
@ -1996,6 +2099,7 @@ void SrsRtcConnection::check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ss
}
++iter;
++sent_nacks;
}
}
@ -2003,11 +2107,6 @@ srs_error_t SrsRtcConnection::send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer* rtp_
{
srs_error_t err = srs_success;
// If DTLS is not OK, drop all messages.
if (!transport_) {
return err;
}
// @see https://tools.ietf.org/html/rfc3550#section-6.4.2
char buf[kRtpPacketSize];
SrsBuffer stream(buf, sizeof(buf));
@ -2016,8 +2115,6 @@ srs_error_t SrsRtcConnection::send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer* rtp_
stream.write_2bytes(7);
stream.write_4bytes(ssrc); // TODO: FIXME: Should be 1?
// TODO: FIXME: Implements it.
// TODO: FIXME: See https://github.com/ossrs/srs/blob/f81d35d20f04ebec01915cb78a882e45b7ee8800/trunk/src/app/srs_app_rtc_queue.cpp
uint8_t fraction_lost = 0;
uint32_t cumulative_number_of_packets_lost = 0 & 0x7FFFFF;
uint32_t extended_highest_sequence = rtp_queue->get_extended_highest_sequence();
@ -2040,7 +2137,7 @@ srs_error_t SrsRtcConnection::send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer* rtp_
stream.write_4bytes(rr_lsr);
stream.write_4bytes(rr_dlsr);
srs_verbose("RR ssrc=%u, fraction_lost=%u, cumulative_number_of_packets_lost=%u, extended_highest_sequence=%u, interarrival_jitter=%u",
srs_info("RR ssrc=%u, fraction_lost=%u, cumulative_number_of_packets_lost=%u, extended_highest_sequence=%u, interarrival_jitter=%u",
ssrc, fraction_lost, cumulative_number_of_packets_lost, extended_highest_sequence, interarrival_jitter);
char protected_buf[kRtpPacketSize];
@ -2049,20 +2146,13 @@ srs_error_t SrsRtcConnection::send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer* rtp_
return srs_error_wrap(err, "protect rtcp rr");
}
// TDOO: FIXME: Check error.
sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
return err;
return sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
}
srs_error_t SrsRtcConnection::send_rtcp_xr_rrtr(uint32_t ssrc)
{
srs_error_t err = srs_success;
// If DTLS is not OK, drop all messages.
if (!transport_) {
return err;
}
/*
@see: http://www.rfc-editor.org/rfc/rfc3611.html#section-2
@ -2109,21 +2199,13 @@ srs_error_t SrsRtcConnection::send_rtcp_xr_rrtr(uint32_t ssrc)
return srs_error_wrap(err, "protect rtcp xr");
}
// TDOO: FIXME: Check error.
sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
return err;
return sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
}
srs_error_t SrsRtcConnection::send_rtcp_fb_pli(uint32_t ssrc)
{
srs_error_t err = srs_success;
// If DTLS is not OK, drop all messages.
if (!transport_) {
return err;
}
char buf[kRtpPacketSize];
SrsBuffer stream(buf, sizeof(buf));
stream.write_1bytes(0x81);
@ -2144,10 +2226,7 @@ srs_error_t SrsRtcConnection::send_rtcp_fb_pli(uint32_t ssrc)
return srs_error_wrap(err, "protect rtcp psfb pli");
}
// TDOO: FIXME: Check error.
sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
return err;
return sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
}
void SrsRtcConnection::simulate_nack_drop(int nn)
@ -2226,6 +2305,17 @@ srs_error_t SrsRtcConnection::do_send_packets(const std::vector<SrsRtpPacket2*>&
return err;
}
void SrsRtcConnection::set_all_tracks_status(bool status)
{
if (player_) {
player_->set_all_tracks_status(status);
}
if (publisher_) {
publisher_->set_all_tracks_status(status);
}
}
#ifdef SRS_OSX
// These functions are similar to the older byteorder(3) family of functions.
// For example, be32toh() is identical to ntohl().
@ -2430,7 +2520,7 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRequest* req, cons
track_desc->create_auxiliary_payload(remote_media_desc.find_media_with_encoding_name("ulpfec"));
std::string track_id;
for (int i = 0; i < remote_media_desc.ssrc_infos_.size(); ++i) {
for (int i = 0; i < (int)remote_media_desc.ssrc_infos_.size(); ++i) {
SrsSSRCInfo ssrc_info = remote_media_desc.ssrc_infos_.at(i);
// ssrc have same track id, will be description in the same track description.
if(track_id != ssrc_info.msid_tracker_) {
@ -2449,7 +2539,7 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRequest* req, cons
}
// set track fec_ssrc and rtx_ssrc
for (int i = 0; i < remote_media_desc.ssrc_groups_.size(); ++i) {
for (int i = 0; i < (int)remote_media_desc.ssrc_groups_.size(); ++i) {
SrsSSRCGroup ssrc_group = remote_media_desc.ssrc_groups_.at(i);
SrsRtcTrackDescription* track_desc = stream_desc->find_track_description_by_ssrc(ssrc_group.ssrcs_[0]);
if (!track_desc) {
@ -2526,7 +2616,7 @@ srs_error_t SrsRtcConnection::generate_publish_local_sdp(SrsRequest* req, SrsSdp
local_media_desc.payload_types_.push_back(payload->generate_media_payload_type());
}
for (int i = 0; i < stream_desc->video_track_descs_.size(); ++i) {
for (int i = 0; i < (int)stream_desc->video_track_descs_.size(); ++i) {
SrsRtcTrackDescription* video_track = stream_desc->video_track_descs_.at(i);
local_sdp.media_descs_.push_back(SrsMediaDesc("video"));
@ -2622,7 +2712,7 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, const S
track_descs = source->get_track_desc("video", "H264");
}
for (int i = 0; i < track_descs.size(); ++i) {
for (int i = 0; i < (int)track_descs.size(); ++i) {
SrsRtcTrackDescription* track = track_descs[i]->copy();
track->mid_ = remote_media_desc.mid_;
uint32_t publish_ssrc = track->ssrc_;
@ -2678,7 +2768,7 @@ srs_error_t SrsRtcConnection::fetch_source_capability(SrsRequest* req, std::map<
std::vector<SrsRtcTrackDescription*> video_track_desc = source->get_track_desc("video", "H264");
track_descs.insert(track_descs.end(), video_track_desc.begin(), video_track_desc.end());
for (int i = 0; i < track_descs.size(); ++i) {
for (int i = 0; i < (int)track_descs.size(); ++i) {
SrsRtcTrackDescription* track = track_descs[i]->copy();
uint32_t publish_ssrc = track->ssrc_;
@ -2802,7 +2892,7 @@ srs_error_t SrsRtcConnection::generate_play_local_sdp(SrsRequest* req, SrsSdp& l
}
}
for (int i = 0; i < stream_desc->video_track_descs_.size(); ++i) {
for (int i = 0; i < (int)stream_desc->video_track_descs_.size(); ++i) {
SrsRtcTrackDescription* track = stream_desc->video_track_descs_[i];
// for plan b, we only add one m=

View file

@ -59,6 +59,7 @@ class SrsRtpRingBuffer;
class SrsRtcConsumer;
class SrsRtcAudioSendTrack;
class SrsRtcVideoSendTrack;
class SrsErrorPithyPrint;
const uint8_t kSR = 200;
const uint8_t kRR = 201;
@ -130,11 +131,6 @@ private:
// A group of RTP packets for outgoing(send to players).
class SrsRtcPlayStreamStatistic
{
public:
#if defined(SRS_DEBUG)
// Debug id.
uint32_t debug_id;
#endif
public:
// The total bytes of AVFrame packets.
int nn_bytes;
@ -165,13 +161,14 @@ public:
};
// A RTC play stream, client pull and play stream from SRS.
class SrsRtcPlayStream : virtual public ISrsCoroutineHandler, virtual public ISrsReloadHandler
class SrsRtcPlayStream : virtual public ISrsCoroutineHandler, virtual public ISrsReloadHandler, virtual public ISrsHourGlass
{
protected:
private:
SrsContextId _parent_cid;
SrsCoroutine* trd;
SrsRtcConnection* session_;
private:
SrsHourGlass* timer_;
// key: publish_ssrc, value: send track to process rtp/rtcp
std::map<uint32_t, SrsRtcAudioSendTrack*> audio_tracks_;
std::map<uint32_t, SrsRtcVideoSendTrack*> video_tracks_;
@ -206,6 +203,11 @@ private:
srs_error_t send_packets(SrsRtcStream* source, const std::vector<SrsRtpPacket2*>& pkts, SrsRtcPlayStreamStatistic& info);
public:
void nack_fetch(std::vector<SrsRtpPacket2*>& pkts, uint32_t ssrc, uint16_t seq);
// Directly set the status of track, generally for init to set the default value.
void set_all_tracks_status(bool status);
// interface ISrsHourGlass
public:
virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick);
public:
srs_error_t on_rtcp(char* data, int nb_data);
private:
@ -221,7 +223,7 @@ private:
class SrsRtcPublishStream : virtual public ISrsHourGlass, virtual public ISrsRtpPacketDecodeHandler, virtual public ISrsRtcPublishStream
{
private:
SrsHourGlass* report_timer;
SrsHourGlass* timer_;
uint64_t nn_audio_frames;
private:
SrsRtcConnection* session_;
@ -240,7 +242,6 @@ private:
std::vector<SrsRtcAudioRecvTrack*> audio_tracks_;
std::vector<SrsRtcVideoRecvTrack*> video_tracks_;
private:
srs_utime_t last_twcc_feedback_time_;
int twcc_id_;
uint8_t twcc_fb_count_;
SrsRtcpTWCC rtcp_twcc_;
@ -252,12 +253,16 @@ public:
public:
srs_error_t initialize(SrsRequest* req, SrsRtcStreamDescription* stream_desc);
srs_error_t start();
// Directly set the status of track, generally for init to set the default value.
void set_all_tracks_status(bool status);
private:
void check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc);
srs_error_t send_rtcp_rr();
srs_error_t send_rtcp_xr_rrtr();
public:
srs_error_t on_rtp(char* buf, int nb_buf);
private:
srs_error_t do_on_rtp(char* plaintext, int nb_plaintext);
public:
virtual void on_before_decode_payload(SrsRtpPacket2* pkt, SrsBuffer* buf, ISrsRtpPayloader** ppayload);
private:
srs_error_t send_periodic_twcc();
@ -305,7 +310,7 @@ public:
};
// A RTC Peer Connection, SDP level object.
class SrsRtcConnection
class SrsRtcConnection : virtual public ISrsHourGlass
{
friend class SrsSecurityTransport;
friend class SrsRtcPlayStream;
@ -320,6 +325,7 @@ private:
SrsRtcPlayStream* player_;
SrsRtcPublishStream* publisher_;
bool is_publisher_;
SrsHourGlass* timer_;
private:
// The local:remote username, such as m5x0n128:jvOm where local name is m5x0n128.
std::string username_;
@ -348,6 +354,8 @@ private:
int twcc_id_;
// Simulators.
int nn_simulate_player_nack_drop;
// Pithy print for address change, use port as error code.
SrsErrorPithyPrint* pp_address_change;
public:
SrsRtcConnection(SrsRtcServer* s, SrsContextId context_id);
virtual ~SrsRtcConnection();
@ -388,9 +396,12 @@ public:
srs_error_t start_publish();
bool is_stun_timeout();
void update_sendonly_socket(SrsUdpMuxSocket* skt);
// interface ISrsHourGlass
public:
virtual srs_error_t notify(int type, srs_utime_t interval, srs_utime_t tick);
public:
// send rtcp
void check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc);
void check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc, uint32_t& sent_nacks);
srs_error_t send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer* rtp_queue, const uint64_t& last_send_systime, const SrsNtp& last_send_ntp);
srs_error_t send_rtcp_xr_rrtr(uint32_t ssrc);
srs_error_t send_rtcp_fb_pli(uint32_t ssrc);
@ -399,6 +410,8 @@ public:
void simulate_nack_drop(int nn);
void simulate_player_drop_packet(SrsRtpHeader* h, int nn_bytes);
srs_error_t do_send_packets(const std::vector<SrsRtpPacket2*>& pkts, SrsRtcPlayStreamStatistic& info);
// Directly set the status of play track, generally for init to set the default value.
void set_all_tracks_status(bool status);
private:
srs_error_t on_binding_request(SrsStunPacket* r);
// publish media capabilitiy negotiate

View file

@ -35,6 +35,7 @@ using namespace std;
#include <srtp2/srtp.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
// The return value of verify_callback controls the strategy of the further verification process. If verify_callback
// returns 0, the verification process is immediately stopped with "verification failed" state. If SSL_VERIFY_PEER is
@ -161,7 +162,7 @@ srs_error_t SrsDtlsCertificate::initialize()
int serial = rand();
ASN1_INTEGER_set(X509_get_serialNumber(dtls_cert), serial);
const std::string& aor = "ossrs.net";
const std::string& aor = RTMP_SIG_SRS_DOMAIN;
X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, (unsigned char *) aor.data(), aor.size(), -1, 0);
X509_set_issuer_name(dtls_cert, subject);
@ -385,10 +386,6 @@ srs_error_t SrsDtls::do_handshake()
{
srs_error_t err = srs_success;
if (!callback) {
return srs_error_new(ERROR_RTC_DTLS, "no callback");
}
int ret = SSL_do_handshake(dtls);
unsigned char *out_bio_data;
@ -419,7 +416,7 @@ srs_error_t SrsDtls::do_handshake()
if (out_bio_len) {
if ((err = callback->write_dtls_data(out_bio_data, out_bio_len)) != srs_success) {
return srs_error_wrap(err, "dtls send");
return srs_error_wrap(err, "dtls send size=%u", out_bio_len);
}
}
@ -450,7 +447,7 @@ srs_error_t SrsDtls::on_dtls(char* data, int nb_data)
if (nb > 0 && callback) {
if ((err = callback->on_dtls_application_data(dtls_read_buf, nb)) != srs_success) {
return srs_error_wrap(err, "dtls application data process");
return srs_error_wrap(err, "on DTLS data, size=%u", nb);
}
}
}
@ -477,7 +474,7 @@ srs_error_t SrsDtls::get_srtp_key(std::string& recv_key, std::string& send_key)
unsigned char material[SRTP_MASTER_KEY_LEN * 2] = {0}; // client(SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN) + server
static const string dtls_srtp_lable = "EXTRACTOR-dtls_srtp";
if (!SSL_export_keying_material(dtls, material, sizeof(material), dtls_srtp_lable.c_str(), dtls_srtp_lable.size(), NULL, 0, 0)) {
return srs_error_new(ERROR_RTC_SRTP_INIT, "SSL_export_keying_material failed");
return srs_error_new(ERROR_RTC_SRTP_INIT, "SSL export key r0=%u", ERR_get_error());
}
size_t offset = 0;
@ -544,8 +541,9 @@ srs_error_t SrsSRTP::initialize(string recv_key, std::string send_key)
memcpy(rkey, recv_key.data(), recv_key.size());
policy.key = rkey;
if (srtp_create(&recv_ctx_, &policy) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_INIT, "srtp_create recv failed");
srtp_err_status_t r0 = srtp_err_status_ok;
if ((r0 = srtp_create(&recv_ctx_, &policy)) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_INIT, "srtp create r0=%u", r0);
}
policy.ssrc.type = ssrc_any_outbound;
@ -554,8 +552,8 @@ srs_error_t SrsSRTP::initialize(string recv_key, std::string send_key)
memcpy(skey, send_key.data(), send_key.size());
policy.key = skey;
if (srtp_create(&send_ctx_, &policy) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_INIT, "srtp_create recv failed");
if ((r0 = srtp_create(&send_ctx_, &policy)) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_INIT, "srtp create r0=%u", r0);
}
return err;
@ -565,10 +563,16 @@ srs_error_t SrsSRTP::protect_rtp(const char* plaintext, char* cipher, int& nb_ci
{
srs_error_t err = srs_success;
// If DTLS/SRTP is not ready, fail.
if (!send_ctx_) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "not ready");
}
memcpy(cipher, plaintext, nb_cipher);
// TODO: FIXME: Wrap error code.
if (srtp_protect(send_ctx_, cipher, &nb_cipher) != 0) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect failed");
srtp_err_status_t r0 = srtp_err_status_ok;
if ((r0 = srtp_protect(send_ctx_, cipher, &nb_cipher)) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect r0=%u", r0);
}
return err;
@ -578,10 +582,16 @@ srs_error_t SrsSRTP::protect_rtcp(const char* plaintext, char* cipher, int& nb_c
{
srs_error_t err = srs_success;
// If DTLS/SRTP is not ready, fail.
if (!send_ctx_) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "not ready");
}
memcpy(cipher, plaintext, nb_cipher);
// TODO: FIXME: Wrap error code.
if (srtp_protect_rtcp(send_ctx_, cipher, &nb_cipher) != 0) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtcp protect failed");
srtp_err_status_t r0 = srtp_err_status_ok;
if ((r0 = srtp_protect_rtcp(send_ctx_, cipher, &nb_cipher)) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtcp protect r0=%u", r0);
}
return err;
@ -591,9 +601,14 @@ srs_error_t SrsSRTP::protect_rtp2(void* rtp_hdr, int* len_ptr)
{
srs_error_t err = srs_success;
// TODO: FIXME: Wrap error code.
if (srtp_protect(send_ctx_, rtp_hdr, len_ptr) != 0) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect");
// If DTLS/SRTP is not ready, fail.
if (!send_ctx_) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "not ready");
}
srtp_err_status_t r0 = srtp_err_status_ok;
if ((r0 = srtp_protect(send_ctx_, rtp_hdr, len_ptr)) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect r0=%u", r0);
}
return err;
@ -603,10 +618,16 @@ srs_error_t SrsSRTP::unprotect_rtp(const char* cipher, char* plaintext, int& nb_
{
srs_error_t err = srs_success;
// If DTLS/SRTP is not ready, fail.
if (!recv_ctx_) {
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "not ready");
}
memcpy(plaintext, cipher, nb_plaintext);
srtp_err_status_t r0 = srtp_unprotect(recv_ctx_, plaintext, &nb_plaintext);
if (r0 != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "unprotect r0=%u", r0);
srtp_err_status_t r0 = srtp_err_status_ok;
if ((r0 = srtp_unprotect(recv_ctx_, plaintext, &nb_plaintext)) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "rtp unprotect r0=%u", r0);
}
return err;
@ -616,11 +637,18 @@ srs_error_t SrsSRTP::unprotect_rtcp(const char* cipher, char* plaintext, int& nb
{
srs_error_t err = srs_success;
// If DTLS/SRTP is not ready, fail.
if (!recv_ctx_) {
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "not ready");
}
memcpy(plaintext, cipher, nb_plaintext);
// TODO: FIXME: Wrap error code.
if (srtp_unprotect_rtcp(recv_ctx_, plaintext, &nb_plaintext) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "rtcp unprotect failed");
srtp_err_status_t r0 = srtp_err_status_ok;
if ((r0 = srtp_unprotect_rtcp(recv_ctx_, plaintext, &nb_plaintext)) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "rtcp unprotect r0=%u", r0);
}
return err;
}
}

View file

@ -34,7 +34,7 @@ using namespace std;
#include <srs_kernel_log.hpp>
// TODO: FIXME: Maybe we should use json.encode to escape it?
const std::string kCRLF = "\\r\\n";
const std::string kCRLF = "\r\n";
#define FETCH(is,word) \
if (!(is >> word)) {\

View file

@ -97,6 +97,7 @@ void SrsRtcBlackhole::sendto(void* data, int len)
return;
}
// For blackhole, we ignore any error.
srs_sendto(blackhole_stfd, data, len, (sockaddr*)blackhole_addr, sizeof(sockaddr_in), SRS_UTIME_NO_TIMEOUT);
}
@ -105,24 +106,32 @@ SrsRtcBlackhole* _srs_blackhole = new SrsRtcBlackhole();
// @global dtls certficate for rtc module.
SrsDtlsCertificate* _srs_rtc_dtls_certificate = new SrsDtlsCertificate();
static bool is_stun(const uint8_t* data, const int size)
// TODO: Should support error response.
// For STUN packet, 0x00 is binding request, 0x01 is binding success response.
bool srs_is_stun(const uint8_t* data, size_t size)
{
return data != NULL && size > 0 && (data[0] == 0 || data[0] == 1);
return size > 0 && (data[0] == 0 || data[0] == 1);
}
static bool is_dtls(const uint8_t* data, size_t len)
// change_cipher_spec(20), alert(21), handshake(22), application_data(23)
// @see https://tools.ietf.org/html/rfc2246#section-6.2.1
bool srs_is_dtls(const uint8_t* data, size_t len)
{
return (len >= 13 && (data[0] > 19 && data[0] < 64));
return (len >= 13 && (data[0] > 19 && data[0] < 64));
}
static bool is_rtp_or_rtcp(const uint8_t* data, size_t len)
// For RTP or RTCP, the V=2 which is in the high 2bits, 0xC0 (1100 0000)
bool srs_is_rtp_or_rtcp(const uint8_t* data, size_t len)
{
return (len >= 12 && (data[0] & 0xC0) == 0x80);
return (len >= 12 && (data[0] & 0xC0) == 0x80);
}
static bool is_rtcp(const uint8_t* data, size_t len)
// For RTCP, PT is [128, 223] (or without marker [0, 95]).
// Literally, RTCP starts from 64 not 0, so PT is [192, 223] (or without marker [64, 95]).
// @note For RTP, the PT is [96, 127], or [224, 255] with marker.
bool srs_is_rtcp(const uint8_t* data, size_t len)
{
return (len >= 12) && (data[0] & 0x80) && (data[1] >= 200 && data[1] <= 209);
return (len >= 12) && (data[0] & 0x80) && (data[1] >= 192 && data[1] <= 223);
}
static std::vector<std::string> get_candidate_ips()
@ -301,7 +310,7 @@ srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt)
}
// For STUN, the peer address may change.
if (is_stun((uint8_t*)data, size)) {
if (srs_is_stun((uint8_t*)data, size)) {
SrsStunPacket ping;
if ((err = ping.decode(data, size)) != srs_success) {
return srs_error_wrap(err, "decode stun packet failed");
@ -309,7 +318,6 @@ srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt)
srs_info("recv stun packet from %s, use-candidate=%d, ice-controlled=%d, ice-controlling=%d",
peer_id.c_str(), ping.get_use_candidate(), ping.get_ice_controlled(), ping.get_ice_controlling());
// TODO: FIXME: For ICE trickle, we may get STUN packets before SDP answer, so maybe should response it.
if (!session) {
session = find_session_by_username(ping.get_username());
@ -318,8 +326,10 @@ srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt)
session->switch_to_context();
}
}
// TODO: FIXME: For ICE trickle, we may get STUN packets before SDP answer, so maybe should response it.
if (!session) {
return srs_error_new(ERROR_RTC_STUN, "can not find session, stun username=%s, peer_id=%s",
return srs_error_new(ERROR_RTC_STUN, "no session, stun username=%s, peer_id=%s",
ping.get_username().c_str(), peer_id.c_str());
}
@ -328,19 +338,19 @@ srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt)
// For DTLS, RTCP or RTP, which does not support peer address changing.
if (!session) {
return srs_error_new(ERROR_RTC_STUN, "can not find session, peer_id=%s", peer_id.c_str());
return srs_error_new(ERROR_RTC_STUN, "no session, peer_id=%s", peer_id.c_str());
}
if (is_dtls((uint8_t*)data, size)) {
if (srs_is_dtls((uint8_t*)data, size)) {
return session->on_dtls(data, size);
} else if (is_rtp_or_rtcp((uint8_t*)data, size)) {
if (is_rtcp((uint8_t*)data, size)) {
} else if (srs_is_rtp_or_rtcp((uint8_t*)data, size)) {
if (srs_is_rtcp((uint8_t*)data, size)) {
return session->on_rtcp(data, size);
}
return session->on_rtp(data, size);
}
return srs_error_new(ERROR_RTC_UDP, "unknown udp packet type");
return srs_error_new(ERROR_RTC_UDP, "unknown packet");
}
srs_error_t SrsRtcServer::listen_api()
@ -415,6 +425,9 @@ srs_error_t SrsRtcServer::do_create_session(
}
}
// All tracks default as inactive, so we must enable them.
session->set_all_tracks_status(true);
std::string local_pwd = srs_random_str(32);
std::string local_ufrag = "";
// TODO: FIXME: Rename for a better name, it's not an username.

View file

@ -1349,7 +1349,7 @@ SrsRtcTrackDescription::SrsRtcTrackDescription()
ssrc_ = 0;
rtx_ssrc_ = 0;
fec_ssrc_ = 0;
is_active_ = true;
is_active_ = false;
media_ = NULL;
red_ = NULL;
@ -1374,6 +1374,7 @@ bool SrsRtcTrackDescription::has_ssrc(uint32_t ssrc)
if (ssrc == ssrc_ || ssrc == rtx_ssrc_ || ssrc == fec_ssrc_) {
return true;
}
return false;
}
@ -1468,7 +1469,7 @@ SrsRtcStreamDescription::~SrsRtcStreamDescription()
{
srs_freep(audio_track_desc_);
for (int i = 0; i < video_track_descs_.size(); ++i) {
for (int i = 0; i < (int)video_track_descs_.size(); ++i) {
srs_freep(video_track_descs_.at(i));
}
video_track_descs_.clear();
@ -1482,7 +1483,7 @@ SrsRtcStreamDescription* SrsRtcStreamDescription::copy()
stream_desc->audio_track_desc_ = audio_track_desc_->copy();
}
for (int i = 0; i < video_track_descs_.size(); ++i) {
for (int i = 0; i < (int)video_track_descs_.size(); ++i) {
stream_desc->video_track_descs_.push_back(video_track_descs_.at(i)->copy());
}
@ -1495,7 +1496,7 @@ SrsRtcTrackDescription* SrsRtcStreamDescription::find_track_description_by_ssrc(
return audio_track_desc_;
}
for (int i = 0; i < video_track_descs_.size(); ++i) {
for (int i = 0; i < (int)video_track_descs_.size(); ++i) {
if (video_track_descs_.at(i)->has_ssrc(ssrc)) {
return video_track_descs_.at(i);
}
@ -1504,10 +1505,30 @@ SrsRtcTrackDescription* SrsRtcStreamDescription::find_track_description_by_ssrc(
return NULL;
}
SrsRtcTrackStatistic::SrsRtcTrackStatistic()
{
packets = 0;
last_packets = 0;
bytes = 0;
last_bytes = 0;
nacks = 0;
last_nacks = 0;
padding_packets = 0;
last_padding_packets = 0;
padding_bytes = 0;
last_padding_bytes = 0;
replay_packets = 0;
last_replay_packets = 0;
replay_bytes = 0;
last_replay_bytes = 0;
}
SrsRtcRecvTrack::SrsRtcRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc, bool is_audio)
{
session_ = session;
track_desc_ = track_desc->copy();
statistic_ = new SrsRtcTrackStatistic();
if (is_audio) {
rtp_queue_ = new SrsRtpRingBuffer(100);
nack_receiver_ = new SrsRtpNackForReceiver(rtp_queue_, 100 * 2 / 3);
@ -1522,22 +1543,17 @@ SrsRtcRecvTrack::~SrsRtcRecvTrack()
srs_freep(rtp_queue_);
srs_freep(nack_receiver_);
srs_freep(track_desc_);
srs_freep(statistic_);
}
bool SrsRtcRecvTrack::has_ssrc(uint32_t ssrc)
{
if (track_desc_) {
return track_desc_->has_ssrc(ssrc);
}
return false;
return track_desc_->has_ssrc(ssrc);
}
void SrsRtcRecvTrack::update_rtt(int rtt)
{
if (nack_receiver_) {
nack_receiver_->update_rtt(rtt);
}
nack_receiver_->update_rtt(rtt);
}
void SrsRtcRecvTrack::update_send_report_time(const SrsNtp& ntp)
@ -1550,8 +1566,10 @@ srs_error_t SrsRtcRecvTrack::send_rtcp_rr()
{
srs_error_t err = srs_success;
if (session_) {
return session_->send_rtcp_rr(track_desc_->ssrc_, rtp_queue_, last_sender_report_sys_time, last_sender_report_ntp);
uint32_t ssrc = track_desc_->ssrc_;
const uint64_t& last_time = last_sender_report_sys_time;
if ((err = session_->send_rtcp_rr(ssrc, rtp_queue_, last_time, last_sender_report_ntp)) != srs_success) {
return srs_error_wrap(err, "ssrc=%u, last_time=%" PRId64, ssrc, last_time);
}
return err;
@ -1561,13 +1579,30 @@ srs_error_t SrsRtcRecvTrack::send_rtcp_xr_rrtr()
{
srs_error_t err = srs_success;
if (track_desc_) {
return session_->send_rtcp_xr_rrtr(track_desc_->ssrc_);
if ((err = session_->send_rtcp_xr_rrtr(track_desc_->ssrc_)) != srs_success) {
return srs_error_wrap(err, "ssrc=%u", track_desc_->ssrc_);
}
return err;
}
bool SrsRtcRecvTrack::set_track_status(bool active)
{
bool previous_status = track_desc_->is_active_;
track_desc_->is_active_ = active;
return previous_status;
}
bool SrsRtcRecvTrack::get_track_status()
{
return track_desc_->is_active_;
}
std::string SrsRtcRecvTrack::get_track_id()
{
return track_desc_->id_;
}
srs_error_t SrsRtcRecvTrack::on_nack(SrsRtpPacket2* pkt)
{
srs_error_t err = srs_success;
@ -1597,17 +1632,15 @@ srs_error_t SrsRtcRecvTrack::on_nack(SrsRtpPacket2* pkt)
// insert into video_queue and audio_queue
rtp_queue_->set(seq, pkt->copy());
// send_nack
session_->check_send_nacks(nack_receiver_, ssrc);
uint32_t sent_nacks = 0;
session_->check_send_nacks(nack_receiver_, ssrc, sent_nacks);
statistic_->nacks += sent_nacks;
return err;
}
srs_error_t SrsRtcRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket2* pkt)
{
return srs_success;
}
SrsRtcAudioRecvTrack::SrsRtcAudioRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc)
: SrsRtcRecvTrack(session, track_desc, true)
{
@ -1621,10 +1654,15 @@ srs_error_t SrsRtcAudioRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket2* pk
{
srs_error_t err = srs_success;
if (source) {
if ((err = source->on_rtp(pkt)) != srs_success) {
return srs_error_wrap(err, "source on rtp");
}
// connection level statistic
session_->stat_->nn_in_audios++;
// track level statistic
statistic_->packets++;
statistic_->bytes += pkt->nb_bytes();
if ((err = source->on_rtp(pkt)) != srs_success) {
return srs_error_wrap(err, "source on rtp");
}
// For NACK to handle packet.
@ -1632,8 +1670,6 @@ srs_error_t SrsRtcAudioRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket2* pk
return srs_error_wrap(err, "on nack");
}
session_->stat_->nn_in_audios++;
return err;
}
@ -1651,20 +1687,28 @@ srs_error_t SrsRtcVideoRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket2* pk
{
srs_error_t err = srs_success;
// connection level statistic
session_->stat_->nn_in_videos++;
// track level statistic
statistic_->packets++;
statistic_->bytes += pkt->nb_bytes();
pkt->frame_type = SrsFrameTypeVideo;
if (source) {
if ((err = source->on_rtp(pkt)) != srs_success) {
return srs_error_wrap(err, "source on rtp");
}
if ((err = source->on_rtp(pkt)) != srs_success) {
return srs_error_wrap(err, "source on rtp");
}
// TODO: FIXME: add rtp process
if (request_key_frame_) {
// TODO: FIXME: add coroutine to request key frame.
request_key_frame_ = false;
// TODO: FIXME: Check error.
session_->send_rtcp_fb_pli(track_desc_->ssrc_);
if ((err = session_->send_rtcp_fb_pli(track_desc_->ssrc_)) != srs_success) {
srs_warn("PLI err %s", srs_error_desc(err).c_str());
srs_freep(err);
}
}
// For NACK to handle packet.
@ -1672,8 +1716,6 @@ srs_error_t SrsRtcVideoRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket2* pk
return srs_error_wrap(err, "on nack");
}
session_->stat_->nn_in_videos++;
return err;
}
@ -1687,25 +1729,25 @@ SrsRtcSendTrack::SrsRtcSendTrack(SrsRtcConnection* session, SrsRtcTrackDescripti
{
session_ = session;
track_desc_ = track_desc->copy();
statistic_ = new SrsRtcTrackStatistic();
if (is_audio) {
rtp_queue_ = new SrsRtpRingBuffer(100);
} else {
rtp_queue_ = new SrsRtpRingBuffer(1000);
}
}
SrsRtcSendTrack::~SrsRtcSendTrack()
{
srs_freep(rtp_queue_);
srs_freep(track_desc_);
srs_freep(statistic_);
}
bool SrsRtcSendTrack::has_ssrc(uint32_t ssrc)
{
if (track_desc_) {
return track_desc_->has_ssrc(ssrc);
}
return false;
return track_desc_->has_ssrc(ssrc);
}
SrsRtpPacket2* SrsRtcSendTrack::fetch_rtp_packet(uint16_t seq)
@ -1718,9 +1760,16 @@ SrsRtpPacket2* SrsRtcSendTrack::fetch_rtp_packet(uint16_t seq)
}
// TODO: FIXME: Should refine logs, set tracks in a time.
void SrsRtcSendTrack::set_track_status(bool active)
bool SrsRtcSendTrack::set_track_status(bool active)
{
bool previous_status = track_desc_->is_active_;
track_desc_->is_active_ = active;
return previous_status;
}
bool SrsRtcSendTrack::get_track_status()
{
return track_desc_->is_active_;
}
std::string SrsRtcSendTrack::get_track_id()
@ -1728,14 +1777,11 @@ std::string SrsRtcSendTrack::get_track_id()
return track_desc_->id_;
}
srs_error_t SrsRtcSendTrack::on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamStatistic& info)
void SrsRtcSendTrack::on_recv_nack()
{
return srs_success;
}
SrsRtcTrackStatistic* statistic = statistic_;
srs_error_t SrsRtcSendTrack::on_rtcp(SrsRtpPacket2* pkt)
{
return srs_success;
statistic->nacks++;
}
SrsRtcAudioSendTrack::SrsRtcAudioSendTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc)
@ -1755,7 +1801,6 @@ srs_error_t SrsRtcAudioSendTrack::on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamSta
return err;
}
std::vector<SrsRtpPacket2*> pkts;
pkt->header.set_ssrc(track_desc_->ssrc_);
// Put rtp packet to NACK/ARQ queue
@ -1764,15 +1809,22 @@ srs_error_t SrsRtcAudioSendTrack::on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamSta
rtp_queue_->set(nack->header.get_sequence(), nack);
}
pkts.push_back(pkt);
// Update stats.
info.nn_bytes += pkt->nb_bytes();
info.nn_audios++;
session_->stat_->nn_out_audios++;
if ((err = session_->do_send_packets(pkts, info)) != srs_success) {
return srs_error_wrap(err, "raw send");
// track level statistic
statistic_->packets++;
statistic_->bytes += pkt->nb_bytes();
if (true) {
std::vector<SrsRtpPacket2*> pkts;
pkts.push_back(pkt);
if ((err = session_->do_send_packets(pkts, info)) != srs_success) {
return srs_error_wrap(err, "raw send");
}
}
return err;
@ -1801,24 +1853,33 @@ srs_error_t SrsRtcVideoSendTrack::on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamSta
if (!track_desc_->is_active_) {
return err;
}
std::vector<SrsRtpPacket2*> pkts;
SrsRtcTrackStatistic* statistic = statistic_;
pkt->header.set_ssrc(track_desc_->ssrc_);
// Put rtp packet to NACK/ARQ queue
if (true) {
SrsRtpPacket2* nack = pkt->copy();
rtp_queue_->set(nack->header.get_sequence(), nack);
}
pkts.push_back(pkt);
// Update stats.
info.nn_bytes += pkt->nb_bytes();
info.nn_videos++;
session_->stat_->nn_out_videos++;
if ((err = session_->do_send_packets(pkts, info)) != srs_success) {
return srs_error_wrap(err, "raw send");
// track level statistic
statistic->packets++;
statistic->bytes += pkt->nb_bytes();
if (true) {
std::vector<SrsRtpPacket2*> pkts;
pkts.push_back(pkt);
if ((err = session_->do_send_packets(pkts, info)) != srs_success) {
return srs_error_wrap(err, "raw send");
}
}
return err;

View file

@ -404,10 +404,50 @@ public:
SrsRtcTrackDescription* find_track_description_by_ssrc(uint32_t ssrc);
};
class SrsRtcTrackStatistic
{
public:
// packets received or sent.
uint32_t packets;
// packets received or sent at last statistic time.
uint32_t last_packets;
// bytes received or sent.
uint64_t bytes;
// bytes received or sent at last statistic time.
uint32_t last_bytes;
// nacks received or sent.
uint32_t nacks;
// nacks received or sent at last statistic time.
uint32_t last_nacks;
// padding packets received or sent.
uint32_t padding_packets;
// padding packets received or sent at last statistic time.
uint32_t last_padding_packets;
// padding bytes received or sent.
uint32_t padding_bytes;
// padding bytes received or sent at last statistic time.
uint32_t last_padding_bytes;
// replay packets received or sent.
uint32_t replay_packets;
// replay packets received or sent at last statistic time.
uint32_t last_replay_packets;
// replay bytes received or sent.
uint64_t replay_bytes;
// replay bytes received or sent at last statistic time.
uint64_t last_replay_bytes;
public:
SrsRtcTrackStatistic();
};
class SrsRtcRecvTrack
{
protected:
SrsRtcTrackDescription* track_desc_;
SrsRtcTrackStatistic* statistic_;
SrsRtcConnection* session_;
SrsRtpRingBuffer* rtp_queue_;
@ -425,10 +465,13 @@ public:
void update_send_report_time(const SrsNtp& ntp);
srs_error_t send_rtcp_rr();
srs_error_t send_rtcp_xr_rrtr();
bool set_track_status(bool active);
bool get_track_status();
std::string get_track_id();
protected:
srs_error_t on_nack(SrsRtpPacket2* pkt);
public:
virtual srs_error_t on_rtp(SrsRtcStream* source, SrsRtpPacket2* pkt);
virtual srs_error_t on_rtp(SrsRtcStream* source, SrsRtpPacket2* pkt) = 0;
};
class SrsRtcAudioRecvTrack : public SrsRtcRecvTrack
@ -458,7 +501,9 @@ class SrsRtcSendTrack
protected:
// send track description
SrsRtcTrackDescription* track_desc_;
SrsRtcTrackStatistic* statistic_;
// The owner connection for this track.
SrsRtcConnection* session_;
// NACK ARQ ring buffer.
SrsRtpRingBuffer* rtp_queue_;
@ -468,11 +513,13 @@ public:
public:
bool has_ssrc(uint32_t ssrc);
SrsRtpPacket2* fetch_rtp_packet(uint16_t seq);
void set_track_status(bool active);
bool set_track_status(bool active);
bool get_track_status();
std::string get_track_id();
public:
virtual srs_error_t on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamStatistic& info);
virtual srs_error_t on_rtcp(SrsRtpPacket2* pkt);
virtual srs_error_t on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamStatistic& info) = 0;
virtual srs_error_t on_rtcp(SrsRtpPacket2* pkt) = 0;
virtual void on_recv_nack();
};
class SrsRtcAudioSendTrack : public SrsRtcSendTrack

View file

@ -1281,37 +1281,51 @@ void srs_api_dump_summaries(SrsJsonObject* obj)
sys->set("conn_srs", SrsJsonAny::integer(nrs->nb_conn_srs));
}
string srs_string_dumps_hex(const std::string& str, const int& limit)
string srs_string_dumps_hex(const std::string& str)
{
return srs_string_dumps_hex(str.c_str(), str.size(), limit);
return srs_string_dumps_hex(str.c_str(), str.size());
}
string srs_string_dumps_hex(const char* buf, const int length, const int& limit)
string srs_string_dumps_hex(const char* str, int length)
{
string ret;
return srs_string_dumps_hex(str, length, INT_MAX);
}
char tmp_buf[1024*16];
tmp_buf[0] = '\n';
int len = 1;
for (int i = 0; i < length && i < limit; ++i) {
int nb = snprintf(tmp_buf + len, sizeof(tmp_buf) - len - 2, "%02X ", (uint8_t)buf[i]);
if (nb <= 0)
string srs_string_dumps_hex(const char* str, int length, int limit)
{
return srs_string_dumps_hex(str, length, limit, ' ', 128, '\n');
}
string srs_string_dumps_hex(const char* str, int length, int limit, char seperator, int line_limit, char newline)
{
const int LIMIT = 1024*16;
static char buf[LIMIT];
int len = 0;
for (int i = 0; i < length && i < limit && i < LIMIT; ++i) {
int nb = snprintf(buf + len, LIMIT - len, "%02x", (uint8_t)str[i]);
if (nb < 0 || nb > LIMIT - len) {
break;
}
len += nb;
len += nb;
// Only append seperator and newline when not last byte.
if (i < length - 1 && i < limit - 1 && i < LIMIT - 1) {
if (seperator && len < LIMIT) {
buf[len++] = seperator;
}
if (i % 48 == 47) {
tmp_buf[len++] = '\n';
ret.append(tmp_buf, len);
len = 0;
}
}
tmp_buf[len] = '\0';
ret.append(tmp_buf, len);
return ret;
if (newline && line_limit && i > 0 && ((i + 1) % line_limit) == 0 && len < LIMIT) {
buf[len++] = newline;
}
}
}
// Empty string.
if (len <= 0) {
return "";
}
return string(buf, len);
}
string srs_getenv(string key)

View file

@ -652,8 +652,11 @@ extern bool srs_is_boolean(std::string str);
extern void srs_api_dump_summaries(SrsJsonObject* obj);
// Dump string(str in length) to hex, it will process min(limit, length) chars.
extern std::string srs_string_dumps_hex(const std::string& str, const int& limit = INT_MAX);
extern std::string srs_string_dumps_hex(const char* str, const int length, const int& limit = INT_MAX);
// Append seperator between each elem, and newline when exceed line_limit, '\0' to ignore.
extern std::string srs_string_dumps_hex(const std::string& str);
extern std::string srs_string_dumps_hex(const char* str, int length);
extern std::string srs_string_dumps_hex(const char* str, int length, int limit);
extern std::string srs_string_dumps_hex(const char* str, int length, int limit, char seperator, int line_limit, char newline);
// Get ENV variable, which may starts with $.
// srs_getenv("EIP") === srs_getenv("$EIP")

View file

@ -46,6 +46,7 @@
#define RTMP_SIG_SRS_AUTHORS "Winlin,Wenjie,Runner365,John,B.P.Y,Lixin"
#define RTMP_SIG_SRS_VERSION SRS_XSTR(VERSION_MAJOR) "." SRS_XSTR(VERSION_MINOR) "." SRS_XSTR(VERSION_REVISION)
#define RTMP_SIG_SRS_SERVER RTMP_SIG_SRS_KEY "/" RTMP_SIG_SRS_VERSION "(" RTMP_SIG_SRS_CODE ")"
#define RTMP_SIG_SRS_DOMAIN "ossrs.net"
// The current stable release.
#define VERSION_STABLE 3

View file

@ -24,6 +24,6 @@
#ifndef SRS_CORE_VERSION4_HPP
#define SRS_CORE_VERSION4_HPP
#define SRS_VERSION4_REVISION 36
#define SRS_VERSION4_REVISION 37
#endif

View file

@ -356,6 +356,7 @@
#define ERROR_RTC_STREM_STARTED 5025
#define ERROR_RTC_STREAM_DESC 5026
#define ERROR_RTC_TRACK_CODEC 5027
#define ERROR_RTC_NO_PLAYER 5028
///////////////////////////////////////////////////////
// GB28181 API error.

View file

@ -37,7 +37,7 @@ SrsRtcpCommon::~SrsRtcpCommon()
{
}
const uint8_t SrsRtcpCommon::type() const
uint8_t SrsRtcpCommon::type() const
{
return header_.type;
}
@ -90,27 +90,27 @@ SrsRtcpApp::~SrsRtcpApp()
{
}
const uint8_t SrsRtcpApp::type() const
uint8_t SrsRtcpApp::type() const
{
return SrsRtcpType_app;
}
const uint32_t SrsRtcpApp::get_ssrc() const
uint32_t SrsRtcpApp::get_ssrc() const
{
return ssrc_;
}
const uint8_t SrsRtcpApp::get_subtype() const
uint8_t SrsRtcpApp::get_subtype() const
{
return header_.rc;
}
const string SrsRtcpApp::get_name() const
string SrsRtcpApp::get_name() const
{
return string((char*)name_);
}
const srs_error_t SrsRtcpApp::get_payload(uint8_t*& payload, int& len)
srs_error_t SrsRtcpApp::get_payload(uint8_t*& payload, int& len)
{
len = payload_len_;
payload = payload_;
@ -213,37 +213,37 @@ SrsRtcpSR::~SrsRtcpSR()
{
}
const uint8_t SrsRtcpSR::get_rc() const
uint8_t SrsRtcpSR::get_rc() const
{
return header_.rc;
}
const uint8_t SrsRtcpSR::type() const
uint8_t SrsRtcpSR::type() const
{
return SrsRtcpType_sr;
}
const uint32_t SrsRtcpSR::get_sender_ssrc() const
uint32_t SrsRtcpSR::get_sender_ssrc() const
{
return sender_ssrc_;
}
const uint64_t SrsRtcpSR::get_ntp() const
uint64_t SrsRtcpSR::get_ntp() const
{
return ntp_;
}
const uint32_t SrsRtcpSR::get_rtp_ts() const
uint32_t SrsRtcpSR::get_rtp_ts() const
{
return rtp_ts_;
}
const uint32_t SrsRtcpSR::get_rtp_send_packets() const
uint32_t SrsRtcpSR::get_rtp_send_packets() const
{
return send_rtp_packets_;
}
const uint32_t SrsRtcpSR::get_rtp_send_bytes() const
uint32_t SrsRtcpSR::get_rtp_send_bytes() const
{
return send_rtp_bytes_;
}
@ -334,42 +334,42 @@ SrsRtcpRR::~SrsRtcpRR()
{
}
const uint8_t SrsRtcpRR::type() const
uint8_t SrsRtcpRR::type() const
{
return SrsRtcpType_rr;
}
const uint32_t SrsRtcpRR::get_rb_ssrc() const
uint32_t SrsRtcpRR::get_rb_ssrc() const
{
return rb_.ssrc;
}
const float SrsRtcpRR::get_lost_rate() const
float SrsRtcpRR::get_lost_rate() const
{
return rb_.fraction_lost / 256;
}
const uint32_t SrsRtcpRR::get_lost_packets() const
uint32_t SrsRtcpRR::get_lost_packets() const
{
return rb_.lost_packets;
}
const uint32_t SrsRtcpRR::get_highest_sn() const
uint32_t SrsRtcpRR::get_highest_sn() const
{
return rb_.highest_sn;
}
const uint32_t SrsRtcpRR::get_jitter() const
uint32_t SrsRtcpRR::get_jitter() const
{
return rb_.jitter;
}
const uint32_t SrsRtcpRR::get_lsr() const
uint32_t SrsRtcpRR::get_lsr() const
{
return rb_.lsr;
}
const uint32_t SrsRtcpRR::get_dlsr() const
uint32_t SrsRtcpRR::get_dlsr() const
{
return rb_.dlsr;
}
@ -493,36 +493,36 @@ void SrsRtcpTWCC::clear()
recv_sns_.clear();
}
const uint32_t SrsRtcpTWCC::get_media_ssrc() const
uint32_t SrsRtcpTWCC::get_media_ssrc() const
{
return media_ssrc_;
}
const uint16_t SrsRtcpTWCC::get_base_sn() const
uint16_t SrsRtcpTWCC::get_base_sn() const
{
return base_sn_;
}
const uint32_t SrsRtcpTWCC::get_reference_time() const
uint32_t SrsRtcpTWCC::get_reference_time() const
{
return reference_time_;
}
const uint8_t SrsRtcpTWCC::get_feedback_count() const
uint8_t SrsRtcpTWCC::get_feedback_count() const
{
return fb_pkt_count_;
}
const uint16_t SrsRtcpTWCC::get_packet_status_count() const
uint16_t SrsRtcpTWCC::get_packet_status_count() const
{
return packet_count_;
}
const vector<uint16_t> SrsRtcpTWCC::get_packet_chucks() const
vector<uint16_t> SrsRtcpTWCC::get_packet_chucks() const
{
return encoded_chucks_;
}
const vector<uint16_t> SrsRtcpTWCC::get_recv_deltas() const
vector<uint16_t> SrsRtcpTWCC::get_recv_deltas() const
{
return pkt_deltas_;
}
@ -887,12 +887,12 @@ SrsRtcpNack::~SrsRtcpNack()
{
}
const uint32_t SrsRtcpNack::get_media_ssrc() const
uint32_t SrsRtcpNack::get_media_ssrc() const
{
return media_ssrc_;
}
const vector<uint16_t> SrsRtcpNack::get_lost_sns() const
vector<uint16_t> SrsRtcpNack::get_lost_sns() const
{
vector<uint16_t> sn;
for(set<uint16_t, SrsSeqCompareLess>::iterator it = lost_sns_.begin(); it != lost_sns_.end(); ++it) {

View file

@ -73,7 +73,7 @@ protected:
public:
SrsRtcpCommon();
virtual ~SrsRtcpCommon();
virtual const uint8_t type() const;
virtual uint8_t type() const;
// interface ISrsCodec
public:
virtual srs_error_t decode(SrsBuffer *buffer);
@ -93,12 +93,12 @@ public:
SrsRtcpApp();
virtual ~SrsRtcpApp();
virtual const uint8_t type() const;
virtual uint8_t type() const;
const uint32_t get_ssrc() const;
const uint8_t get_subtype() const;
const std::string get_name() const;
const srs_error_t get_payload(uint8_t*& payload, int& len);
uint32_t get_ssrc() const;
uint8_t get_subtype() const;
std::string get_name() const;
srs_error_t get_payload(uint8_t*& payload, int& len);
void set_ssrc(uint32_t ssrc);
srs_error_t set_subtype(uint8_t type);
@ -134,14 +134,14 @@ public:
SrsRtcpSR();
virtual ~SrsRtcpSR();
const uint8_t get_rc() const;
uint8_t get_rc() const;
// overload SrsRtcpCommon
virtual const uint8_t type() const;
const uint32_t get_sender_ssrc() const;
const uint64_t get_ntp() const;
const uint32_t get_rtp_ts() const;
const uint32_t get_rtp_send_packets() const;
const uint32_t get_rtp_send_bytes() const;
virtual uint8_t type() const;
uint32_t get_sender_ssrc() const;
uint64_t get_ntp() const;
uint32_t get_rtp_ts() const;
uint32_t get_rtp_send_packets() const;
uint32_t get_rtp_send_bytes() const;
void set_sender_ssrc(uint32_t ssrc);
void set_ntp(uint64_t ntp);
@ -165,15 +165,15 @@ public:
virtual ~SrsRtcpRR();
// overload SrsRtcpCommon
virtual const uint8_t type() const;
virtual uint8_t type() const;
const uint32_t get_rb_ssrc() const;
const float get_lost_rate() const;
const uint32_t get_lost_packets() const;
const uint32_t get_highest_sn() const;
const uint32_t get_jitter() const;
const uint32_t get_lsr() const;
const uint32_t get_dlsr() const;
uint32_t get_rb_ssrc() const;
float get_lost_rate() const;
uint32_t get_lost_packets() const;
uint32_t get_highest_sn() const;
uint32_t get_jitter() const;
uint32_t get_lsr() const;
uint32_t get_dlsr() const;
void set_rb_ssrc(uint32_t ssrc);
void set_lost_rate(float rate);
@ -271,13 +271,13 @@ public:
SrsRtcpTWCC(uint32_t sender_ssrc = 0);
virtual ~SrsRtcpTWCC();
const uint32_t get_media_ssrc() const;
const uint16_t get_base_sn() const;
const uint16_t get_packet_status_count() const;
const uint32_t get_reference_time() const;
const uint8_t get_feedback_count() const;
const std::vector<uint16_t> get_packet_chucks() const;
const std::vector<uint16_t> get_recv_deltas() const;
uint32_t get_media_ssrc() const;
uint16_t get_base_sn() const;
uint16_t get_packet_status_count() const;
uint32_t get_reference_time() const;
uint8_t get_feedback_count() const;
std::vector<uint16_t> get_packet_chucks() const;
std::vector<uint16_t> get_recv_deltas() const;
void set_media_ssrc(uint32_t ssrc);
void set_base_sn(uint16_t sn);
@ -313,8 +313,8 @@ public:
SrsRtcpNack(uint32_t sender_ssrc = 0);
virtual ~SrsRtcpNack();
const uint32_t get_media_ssrc() const;
const std::vector<uint16_t> get_lost_sns() const;
uint32_t get_media_ssrc() const;
std::vector<uint16_t> get_lost_sns() const;
void set_media_ssrc(uint32_t ssrc);
void add_lost_sn(uint16_t sn);

View file

@ -1576,7 +1576,7 @@ string escape(string v)
{
stringstream ss;
for (int i = 0; i < v.length(); i++) {
for (int i = 0; i < (int)v.length(); i++) {
if (v.at(i) == '"') {
ss << '\\';
}

View file

@ -43,7 +43,7 @@ srs_utime_t _srs_tmp_timeout = (100 * SRS_UTIME_MILLISECONDS);
ISrsLog* _srs_log = new MockEmptyLog(SrsLogLevelDisabled);
ISrsContext* _srs_context = new SrsThreadContext();
// app module.
SrsConfig* _srs_config = NULL;
SrsConfig* _srs_config = new SrsConfig();
SrsServer* _srs_server = NULL;
bool _srs_in_docker = false;

View file

@ -24,6 +24,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef SRS_UTEST_PUBLIC_SHARED_HPP
#define SRS_UTEST_PUBLIC_SHARED_HPP
// Before define the private/protected, we must include some system header files.
// Or it may fail with:
// redeclared with different access struct __xfer_bufptrs
// @see https://stackoverflow.com/questions/47839718/sstream-redeclared-with-public-access-compiler-error
#include "gtest/gtest.h"
// Public all private and protected members.
#define private public
#define protected public
@ -33,7 +39,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_core.hpp>
#include "gtest/gtest.h"
#include <string>
using namespace std;

View file

@ -25,7 +25,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#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 <vector>
using namespace std;
VOID TEST(KernelRTCTest, SequenceCompare)
{
@ -123,3 +129,191 @@ VOID TEST(KernelRTCTest, SequenceCompare)
}
}
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());
}
}
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());
}
}