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 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-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-11, Refine log context with random string. 4.0.35
* v4.0, 2020-07-04, Fix some bugs for RTC. 4.0.34 * 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 if [[ -f ${SRS_OBJS}/${SRS_PLATFORM}/ffmpeg/bin/ffmpeg ]]; then
echo "ffmpeg-4.1 is ok."; echo "ffmpeg-4.1 is ok.";
else 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 " please copy it from srs-docker";
echo " or download from http://ffmpeg.org/download.html"; echo " or download from http://ffmpeg.org/download.html";
echo " or disable it by --without-ffmpeg"; echo " or disable it by --without-ffmpeg";
exit -1;
fi fi
# Always update the links. # 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 fi
##################################################################################### #####################################################################################

View file

@ -482,24 +482,24 @@ function apply_detail_options() {
# if specified export single file, export project first. # if specified export single file, export project first.
if [ $SRS_EXPORT_LIBRTMP_SINGLE != NO ]; then if [ $SRS_EXPORT_LIBRTMP_SINGLE != NO ]; then
echo "Not support --export-librtmp-single" echo "Warning: Ingore --export-librtmp-single"
exit -1 SRS_EXPORT_LIBRTMP_SINGLE=NO
fi fi
# disable almost all features for export srs-librtmp. # disable almost all features for export srs-librtmp.
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
echo "Not support --export-librtmp-project" echo "Warning: Ingore --export-librtmp-project"
exit -1 SRS_EXPORT_LIBRTMP_PROJECT=NO
fi fi
if [[ $SRS_LIBRTMP != NO ]]; then if [[ $SRS_LIBRTMP != NO ]]; then
echo "Not support --librtmp" echo "Warning: Ingore --librtmp"
exit -1 SRS_LIBRTMP=NO
fi fi
if [[ $SRS_RESEARCH != NO ]]; then if [[ $SRS_RESEARCH != NO ]]; then
echo "Not support --research" echo "Warning: Ingore --research"
exit -1 SRS_RESEARCH=NO
fi fi
if [[ $SRS_SRTP_ASM == YES && $SRS_RTC == NO ]]; then if [[ $SRS_SRTP_ASM == YES && $SRS_RTC == NO ]]; then
@ -583,11 +583,13 @@ function check_option_conflicts() {
fi fi
if [[ $SRS_CROSS_BUILD == YES && ($SRS_TOOL_CC == 'gcc' || $SRS_TOOL_CXX == 'g++' || $SRS_TOOL_AR == 'ar') ]]; then 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 fi
if [[ $SRS_NGINX == YES ]]; then 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 fi
# For OSX, recommend to use DTrace, https://blog.csdn.net/win_lin/article/details/53503869 # 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 CPPFLAGS += -I\$(GTEST_DIR)/include
# Flags passed to the C++ compiler. # 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 # All tests produced by this Makefile. Remember to add new tests you
# created to the list. # created to the list.

View file

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

View file

@ -116,6 +116,7 @@ SrsStageInfo* SrsStageManager::fetch_or_create(int stage_id, bool* pnew)
SrsErrorPithyPrint::SrsErrorPithyPrint() SrsErrorPithyPrint::SrsErrorPithyPrint()
{ {
nn_count = 0;
} }
SrsErrorPithyPrint::~SrsErrorPithyPrint() SrsErrorPithyPrint::~SrsErrorPithyPrint()
@ -125,6 +126,12 @@ SrsErrorPithyPrint::~SrsErrorPithyPrint()
bool SrsErrorPithyPrint::can_print(srs_error_t err) bool SrsErrorPithyPrint::can_print(srs_error_t err)
{ {
int error_code = srs_error_code(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; bool new_stage = false;
SrsStageInfo* stage = stages.fetch_or_create(error_code, &new_stage); 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. // For example, we use it for error pithy print for each UDP packet processing.
class SrsErrorPithyPrint class SrsErrorPithyPrint
{ {
public:
// The number of call of can_print().
uint32_t nn_count;
private: private:
SrsStageManager stages; SrsStageManager stages;
std::map<int, srs_utime_t> ticks; std::map<int, srs_utime_t> ticks;
@ -77,6 +80,8 @@ public:
public: public:
// Whether specified stage is ready for print. // Whether specified stage is ready for print.
bool can_print(srs_error_t err); 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, // 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(); 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("code", SrsJsonAny::integer(ERROR_SUCCESS));
res->set("server", SrsJsonAny::integer(SrsStatistic::instance()->server_id())); 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(), srs_trace("RTC username=%s, offer=%dB, answer=%dB", session->username().c_str(),
remote_sdp_str.length(), local_sdp_str.length()); 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 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; return err;
} }
@ -545,6 +547,8 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHtt
} }
string local_sdp_str = os.str(); 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("code", SrsJsonAny::integer(ERROR_SUCCESS));
res->set("server", SrsJsonAny::integer(SrsStatistic::instance()->server_id())); 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(), srs_trace("RTC username=%s, offer=%dB, answer=%dB", session->username().c_str(),
remote_sdp_str.length(), local_sdp_str.length()); 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 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; return err;
} }

View file

@ -58,6 +58,8 @@ using namespace std;
#include <srs_app_rtc_source.hpp> #include <srs_app_rtc_source.hpp>
#include <srs_protocol_utility.hpp> #include <srs_protocol_utility.hpp>
#define SRS_TICKID_RTCP 0
SrsSecurityTransport::SrsSecurityTransport(SrsRtcConnection* s) SrsSecurityTransport::SrsSecurityTransport(SrsRtcConnection* s)
{ {
session_ = 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 SrsSecurityTransport::write_dtls_data(void* data, int size)
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
if (size) {
if ((err = session_->sendonly_skt->sendto(data, size, 0)) != srs_success) { if (!size) {
return srs_error_wrap(err, "send dtls packet"); 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) { if (_srs_blackhole->blackhole) {
@ -112,13 +117,13 @@ srs_error_t SrsSecurityTransport::on_dtls_handshake_done()
if (handshake_done) { if (handshake_done) {
return err; return err;
} }
handshake_done = true;
// TODO: FIXME: Add cost for DTLS. // TODO: FIXME: Add cost for DTLS.
srs_trace("RTC: DTLS handshake done."); srs_trace("RTC: DTLS handshake done.");
handshake_done = true;
if ((err = srtp_initialize()) != srs_success) { 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(); 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) { 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; 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) 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); return srtp_->protect_rtp(plaintext, cipher, nb_cipher);
} }
srs_error_t SrsSecurityTransport::protect_rtcp(const char* plaintext, char* cipher, int& 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); return srtp_->protect_rtcp(plaintext, cipher, nb_cipher);
} }
// TODO: FIXME: Merge with protect_rtp. // TODO: FIXME: Merge with protect_rtp.
srs_error_t SrsSecurityTransport::protect_rtp2(void* rtp_hdr, int* len_ptr) 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); return srtp_->protect_rtp2(rtp_hdr, len_ptr);
} }
srs_error_t SrsSecurityTransport::unprotect_rtp(const char* cipher, char* plaintext, int& nb_plaintext) 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); return srtp_->unprotect_rtp(cipher, plaintext, nb_plaintext);
} }
srs_error_t SrsSecurityTransport::unprotect_rtcp(const char* cipher, char* plaintext, int& 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); return srtp_->unprotect_rtcp(cipher, plaintext, nb_plaintext);
} }
SrsRtcPlayStreamStatistic::SrsRtcPlayStreamStatistic() SrsRtcPlayStreamStatistic::SrsRtcPlayStreamStatistic()
{ {
#if defined(SRS_DEBUG)
debug_id = 0;
#endif
nn_rtp_pkts = 0; nn_rtp_pkts = 0;
nn_audios = nn_extras = 0; nn_audios = nn_extras = 0;
nn_videos = nn_samples = 0; nn_videos = nn_samples = 0;
@ -227,6 +208,7 @@ SrsRtcPlayStream::SrsRtcPlayStream(SrsRtcConnection* s, SrsContextId parent_cid)
nack_enabled_ = false; nack_enabled_ = false;
_srs_config->subscribe(this); _srs_config->subscribe(this);
timer_ = new SrsHourGlass(this, 1000 * SRS_UTIME_MILLISECONDS);
} }
SrsRtcPlayStream::~SrsRtcPlayStream() SrsRtcPlayStream::~SrsRtcPlayStream()
@ -234,6 +216,7 @@ SrsRtcPlayStream::~SrsRtcPlayStream()
_srs_config->unsubscribe(this); _srs_config->unsubscribe(this);
srs_freep(trd); srs_freep(trd);
srs_freep(timer_);
if (true) { if (true) {
std::map<uint32_t, SrsRtcAudioSendTrack*>::iterator it; std::map<uint32_t, SrsRtcAudioSendTrack*>::iterator it;
@ -321,6 +304,10 @@ srs_error_t SrsRtcPlayStream::start()
return srs_error_wrap(err, "rtc_sender"); 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 (_srs_rtc_hijacker) {
if ((err = _srs_rtc_hijacker->on_start_play(session_, this, session_->req)) != srs_success) { if ((err = _srs_rtc_hijacker->on_start_play(session_, this, session_->req)) != srs_success) {
return srs_error_wrap(err, "on start play"); return srs_error_wrap(err, "on start play");
@ -345,18 +332,18 @@ srs_error_t SrsRtcPlayStream::cycle()
SrsRequest* req = session_->req; SrsRequest* req = session_->req;
if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) { 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; SrsRtcConsumer* consumer = NULL;
SrsAutoFree(SrsRtcConsumer, consumer); SrsAutoFree(SrsRtcConsumer, consumer);
if ((err = source->create_consumer(consumer)) != srs_success) { 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. // TODO: FIXME: Dumps the SPS/PPS from gop cache, without other frames.
if ((err = source->consumer_dumps(consumer)) != srs_success) { 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); 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; srs_error_t err = srs_success;
// If DTLS is not OK, drop all messages.
if (!session_->transport_) {
return err;
}
vector<SrsRtpPacket2*> send_pkts; vector<SrsRtpPacket2*> send_pkts;
// Covert kernel messages to RTP packets. // Covert kernel messages to RTP packets.
for (int i = 0; i < (int)pkts.size(); i++) { 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) { if (true) {
std::map<uint32_t, SrsRtcAudioSendTrack*>::iterator it; std::map<uint32_t, SrsRtcAudioSendTrack*>::iterator it;
for (it = audio_tracks_.begin(); it != audio_tracks_.end(); ++it) { for (it = audio_tracks_.begin(); it != audio_tracks_.end(); ++it) {
if (it->second->has_ssrc(ssrc)) { SrsRtcAudioSendTrack* track = it->second;
SrsRtpPacket2* pkt = it->second->fetch_rtp_packet(seq);
if (track->has_ssrc(ssrc)) {
// update recv nack statistic
track->on_recv_nack();
SrsRtpPacket2* pkt = track->fetch_rtp_packet(seq);
if (pkt != NULL) { if (pkt != NULL) {
pkts.push_back(pkt); pkts.push_back(pkt);
} }
@ -496,8 +483,13 @@ void SrsRtcPlayStream::nack_fetch(vector<SrsRtpPacket2*>& pkts, uint32_t ssrc, u
if (true) { if (true) {
std::map<uint32_t, SrsRtcVideoSendTrack*>::iterator it; std::map<uint32_t, SrsRtcVideoSendTrack*>::iterator it;
for (it = video_tracks_.begin(); it != video_tracks_.end(); ++it) { for (it = video_tracks_.begin(); it != video_tracks_.end(); ++it) {
if (it->second->has_ssrc(ssrc)) { SrsRtcVideoSendTrack* track = it->second;
SrsRtpPacket2* pkt = it->second->fetch_rtp_packet(seq);
if (track->has_ssrc(ssrc)) {
// update recv nack statistic
track->on_recv_nack();
SrsRtpPacket2* pkt = track->fetch_rtp_packet(seq);
if (pkt != NULL) { if (pkt != NULL) {
pkts.push_back(pkt); 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 SrsRtcPlayStream::on_rtcp(char* data, int nb_data)
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
// TODO: Use SrsBuffer to parse it.
char* ph = data; char* ph = data;
int nb_left = nb_data; int nb_left = nb_data;
while (nb_left) { while (nb_left) {
uint8_t payload_type = ph[1]; uint8_t payload_type = ph[1];
uint16_t length_4bytes = (((uint16_t)ph[2]) << 8) | ph[3]; 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; int length = (length_4bytes + 1) * 4;
if (length > nb_data) { 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); 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) { 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; ph += length;
@ -760,11 +795,12 @@ uint32_t SrsRtcPlayStream::get_video_publish_ssrc(uint32_t play_ssrc)
SrsRtcPublishStream::SrsRtcPublishStream(SrsRtcConnection* session) SrsRtcPublishStream::SrsRtcPublishStream(SrsRtcConnection* session)
{ {
report_timer = new SrsHourGlass(this, 200 * SRS_UTIME_MILLISECONDS); timer_ = new SrsHourGlass(this, 200 * SRS_UTIME_MILLISECONDS);
session_ = session; session_ = session;
request_keyframe_ = false; request_keyframe_ = false;
req = NULL;
source = NULL; source = NULL;
nn_simulate_nack_drop = 0; nn_simulate_nack_drop = 0;
nack_enabled_ = false; nack_enabled_ = false;
@ -772,7 +808,6 @@ SrsRtcPublishStream::SrsRtcPublishStream(SrsRtcConnection* session)
nn_audio_frames = 0; nn_audio_frames = 0;
twcc_id_ = 0; twcc_id_ = 0;
last_twcc_feedback_time_ = 0;
twcc_fb_count_ = 0; twcc_fb_count_ = 0;
} }
@ -785,7 +820,7 @@ SrsRtcPublishStream::~SrsRtcPublishStream()
} }
srs_freep(req); srs_freep(req);
srs_freep(report_timer); srs_freep(timer_);
} }
srs_error_t SrsRtcPublishStream::initialize(SrsRequest* r, SrsRtcStreamDescription* stream_desc) 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(); req = r->copy();
audio_tracks_.push_back(new SrsRtcAudioRecvTrack(session_, stream_desc->audio_track_desc_)); 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); SrsRtcTrackDescription* desc = stream_desc->video_track_descs_.at(i);
video_tracks_.push_back(new SrsRtcVideoRecvTrack(session_, desc)); 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; uint32_t media_ssrc = 0;
// because audio_track_desc have not twcc id, for example, h5demo // because audio_track_desc have not twcc id, for example, h5demo
// fetch twcc_id from video track description, // 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); SrsRtcTrackDescription* desc = stream_desc->video_track_descs_.at(i);
twcc_id = desc->get_rtp_extension_id(kTWCCExt); twcc_id = desc->get_rtp_extension_id(kTWCCExt);
media_ssrc = desc->ssrc_; media_ssrc = desc->ssrc_;
@ -831,17 +866,16 @@ srs_error_t SrsRtcPublishStream::start()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
// If report_timer started, we think the publisher is started.
if (is_started) { if (is_started) {
return err; return err;
} }
if ((err = report_timer->tick(0 * SRS_UTIME_MILLISECONDS)) != srs_success) { if ((err = timer_->tick(SRS_TICKID_RTCP, 200 * SRS_UTIME_MILLISECONDS)) != srs_success) {
return srs_error_wrap(err, "hourglass tick"); return srs_error_wrap(err, "rtcp tick");
} }
if ((err = report_timer->start()) != srs_success) { if ((err = timer_->start()) != srs_success) {
return srs_error_wrap(err, "start report_timer"); return srs_error_wrap(err, "start timer");
} }
if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) { if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) {
@ -865,18 +899,51 @@ srs_error_t SrsRtcPublishStream::start()
return err; 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 SrsRtcPublishStream::send_rtcp_rr()
{ {
srs_error_t err = srs_success; 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); 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); 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++; session_->stat_->nn_rr++;
@ -888,14 +955,18 @@ srs_error_t SrsRtcPublishStream::send_rtcp_xr_rrtr()
{ {
srs_error_t err = srs_success; 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); 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); 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++; 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); _srs_blackhole->sendto(unprotected_buf, nb_unprotected_buf);
} }
char* buf = unprotected_buf; // Handle the plaintext RTP packet.
int nb_buf = nb_unprotected_buf; 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. // Decode the RTP packet from buffer.
SrsRtpPacket2* pkt = new SrsRtpPacket2(); 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 SrsRtcPublishStream::send_periodic_twcc()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
srs_utime_t now = srs_get_system_time();
if(0 == last_twcc_feedback_time_) { char pkt[kRtcpPacketSize];
last_twcc_feedback_time_ = now; SrsBuffer *buffer = new SrsBuffer(pkt, sizeof(pkt));
return err; SrsAutoFree(SrsBuffer, buffer);
}
srs_utime_t diff = now - last_twcc_feedback_time_; rtcp_twcc_.set_feedback_count(twcc_fb_count_);
if( diff >= 50 * SRS_UTIME_MILLISECONDS) { twcc_fb_count_++;
last_twcc_feedback_time_ = now;
char pkt[kRtcpPacketSize]; if((err = rtcp_twcc_.encode(buffer)) != srs_success) {
SrsBuffer *buffer = new SrsBuffer(pkt, sizeof(pkt)); return srs_error_wrap(err, "encode, count=%u", twcc_fb_count_);
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);
}
} }
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) 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; srs_error_t err = srs_success;
// TODO: FIXME: Check error. if (!is_started) {
send_rtcp_rr(); return err;
send_rtcp_xr_rrtr(); }
// TODO: FIXME: Check error. if (type == SRS_TICKID_RTCP) {
// We should not depends on the received packet, if ((err = send_rtcp_rr()) != srs_success) {
// instead we should send feedback every Nms. srs_warn("RR err %s", srs_error_desc(err).c_str());
send_periodic_twcc(); 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; return err;
} }
@ -1455,7 +1551,7 @@ void SrsRtcPublishStream::simulate_drop_packet(SrsRtpHeader* h, int nn_bytes)
SrsRtcVideoRecvTrack* SrsRtcPublishStream::get_video_track(uint32_t ssrc) 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); SrsRtcVideoRecvTrack* track = video_tracks_.at(i);
if (track->has_ssrc(ssrc)) { if (track->has_ssrc(ssrc)) {
return track; return track;
@ -1467,7 +1563,7 @@ SrsRtcVideoRecvTrack* SrsRtcPublishStream::get_video_track(uint32_t ssrc)
SrsRtcAudioRecvTrack* SrsRtcPublishStream::get_audio_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); SrsRtcAudioRecvTrack* track = audio_tracks_.at(i);
if (track->has_ssrc(ssrc)) { if (track->has_ssrc(ssrc)) {
return track; return track;
@ -1555,6 +1651,7 @@ SrsRtcConnection::SrsRtcConnection(SrsRtcServer* s, SrsContextId context_id)
encrypt = true; encrypt = true;
cid = context_id; cid = context_id;
stat_ = new SrsRtcConnectionStatistic(); stat_ = new SrsRtcConnectionStatistic();
timer_ = new SrsHourGlass(this, 1000 * SRS_UTIME_MILLISECONDS);
source_ = NULL; source_ = NULL;
publisher_ = NULL; publisher_ = NULL;
@ -1570,10 +1667,12 @@ SrsRtcConnection::SrsRtcConnection(SrsRtcServer* s, SrsContextId context_id)
twcc_id_ = 0; twcc_id_ = 0;
nn_simulate_player_nack_drop = 0; nn_simulate_player_nack_drop = 0;
pp_address_change = new SrsErrorPithyPrint();
} }
SrsRtcConnection::~SrsRtcConnection() SrsRtcConnection::~SrsRtcConnection()
{ {
srs_freep(timer_);
srs_freep(player_); srs_freep(player_);
srs_freep(publisher_); srs_freep(publisher_);
srs_freep(transport_); srs_freep(transport_);
@ -1587,6 +1686,8 @@ SrsRtcConnection::~SrsRtcConnection()
SrsUdpMuxSocket* addr = it->second; SrsUdpMuxSocket* addr = it->second;
srs_freep(addr); srs_freep(addr);
} }
srs_freep(pp_address_change);
} }
SrsSdp* SrsRtcConnection::get_local_sdp() 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"); return srs_error_wrap(err, "init");
} }
if ((err = timer_->start()) != srs_success) {
return srs_error_wrap(err, "start timer");
}
// TODO: FIXME: Support reload. // TODO: FIXME: Support reload.
session_timeout = _srs_config->get_rtc_stun_timeout(req->vhost); session_timeout = _srs_config->get_rtc_stun_timeout(req->vhost);
last_stun_time = srs_get_system_time(); 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; 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]; char unprotected_buf[kRtpPacketSize];
int nb_unprotected_buf = nb_data; int nb_unprotected_buf = nb_data;
if ((err = transport_->unprotect_rtcp(data, unprotected_buf, nb_unprotected_buf)) != srs_success) { 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) { if (_srs_blackhole->blackhole) {
@ -1835,11 +1936,16 @@ srs_error_t SrsRtcConnection::on_rtcp(char* data, int nb_data)
} }
if (player_) { if (player_) {
return player_->on_rtcp(unprotected_buf, nb_unprotected_buf); err = player_->on_rtcp(unprotected_buf, nb_unprotected_buf);
} }
if (publisher_) { 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; 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) srs_error_t SrsRtcConnection::on_rtp(char* data, int nb_data)
{ {
if (publisher_ == NULL) { if (publisher_ == NULL) {
return srs_error_new(ERROR_RTC_RTCP, "rtc publisher null"); return srs_error_new(ERROR_RTC_RTCP, "no publisher");
}
if (transport_ == NULL) {
return srs_error_new(ERROR_RTC_RTCP, "recv unexpect rtp packet before dtls done");
} }
//TODO: FIXME: add unprotect_rtcp. //TODO: FIXME: add unprotect_rtcp.
@ -1924,14 +2026,6 @@ void SrsRtcConnection::update_sendonly_socket(SrsUdpMuxSocket* skt)
return; 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. // Find object from cache.
SrsUdpMuxSocket* addr_cache = NULL; SrsUdpMuxSocket* addr_cache = NULL;
if (true) { 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 no cache, build cache and setup the relations in connection.
if (!addr_cache) { if (!addr_cache) {
peer_addresses_[peer_id] = addr_cache = skt->copy_sendonly(); peer_addresses_[peer_id] = addr_cache = skt->copy_sendonly();
@ -1951,13 +2053,14 @@ void SrsRtcConnection::update_sendonly_socket(SrsUdpMuxSocket* skt)
sendonly_skt = addr_cache; 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. srs_error_t err = srs_success;
if (!transport_) { return err;
return; }
}
void SrsRtcConnection::check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc, uint32_t& sent_nacks)
{
// @see: https://tools.ietf.org/html/rfc4585#section-6.1 // @see: https://tools.ietf.org/html/rfc4585#section-6.1
vector<uint16_t> nack_seqs; vector<uint16_t> nack_seqs;
nack->get_nack_seqs(nack_seqs); nack->get_nack_seqs(nack_seqs);
@ -1996,6 +2099,7 @@ void SrsRtcConnection::check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ss
} }
++iter; ++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; 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 // @see https://tools.ietf.org/html/rfc3550#section-6.4.2
char buf[kRtpPacketSize]; char buf[kRtpPacketSize];
SrsBuffer stream(buf, sizeof(buf)); 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_2bytes(7);
stream.write_4bytes(ssrc); // TODO: FIXME: Should be 1? 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; uint8_t fraction_lost = 0;
uint32_t cumulative_number_of_packets_lost = 0 & 0x7FFFFF; uint32_t cumulative_number_of_packets_lost = 0 & 0x7FFFFF;
uint32_t extended_highest_sequence = rtp_queue->get_extended_highest_sequence(); 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_lsr);
stream.write_4bytes(rr_dlsr); 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); ssrc, fraction_lost, cumulative_number_of_packets_lost, extended_highest_sequence, interarrival_jitter);
char protected_buf[kRtpPacketSize]; 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"); return srs_error_wrap(err, "protect rtcp rr");
} }
// TDOO: FIXME: Check error. return sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
return err;
} }
srs_error_t SrsRtcConnection::send_rtcp_xr_rrtr(uint32_t ssrc) srs_error_t SrsRtcConnection::send_rtcp_xr_rrtr(uint32_t ssrc)
{ {
srs_error_t err = srs_success; 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 @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"); return srs_error_wrap(err, "protect rtcp xr");
} }
// TDOO: FIXME: Check error. return sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
return err;
} }
srs_error_t SrsRtcConnection::send_rtcp_fb_pli(uint32_t ssrc) srs_error_t SrsRtcConnection::send_rtcp_fb_pli(uint32_t ssrc)
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
// If DTLS is not OK, drop all messages.
if (!transport_) {
return err;
}
char buf[kRtpPacketSize]; char buf[kRtpPacketSize];
SrsBuffer stream(buf, sizeof(buf)); SrsBuffer stream(buf, sizeof(buf));
stream.write_1bytes(0x81); 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"); return srs_error_wrap(err, "protect rtcp psfb pli");
} }
// TDOO: FIXME: Check error. return sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
sendonly_skt->sendto(protected_buf, nb_protected_buf, 0);
return err;
} }
void SrsRtcConnection::simulate_nack_drop(int nn) void SrsRtcConnection::simulate_nack_drop(int nn)
@ -2226,6 +2305,17 @@ srs_error_t SrsRtcConnection::do_send_packets(const std::vector<SrsRtpPacket2*>&
return err; 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 #ifdef SRS_OSX
// These functions are similar to the older byteorder(3) family of functions. // These functions are similar to the older byteorder(3) family of functions.
// For example, be32toh() is identical to ntohl(). // 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")); track_desc->create_auxiliary_payload(remote_media_desc.find_media_with_encoding_name("ulpfec"));
std::string track_id; 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); SrsSSRCInfo ssrc_info = remote_media_desc.ssrc_infos_.at(i);
// ssrc have same track id, will be description in the same track description. // ssrc have same track id, will be description in the same track description.
if(track_id != ssrc_info.msid_tracker_) { 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 // 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); SrsSSRCGroup ssrc_group = remote_media_desc.ssrc_groups_.at(i);
SrsRtcTrackDescription* track_desc = stream_desc->find_track_description_by_ssrc(ssrc_group.ssrcs_[0]); SrsRtcTrackDescription* track_desc = stream_desc->find_track_description_by_ssrc(ssrc_group.ssrcs_[0]);
if (!track_desc) { 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()); 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); SrsRtcTrackDescription* video_track = stream_desc->video_track_descs_.at(i);
local_sdp.media_descs_.push_back(SrsMediaDesc("video")); 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"); 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(); SrsRtcTrackDescription* track = track_descs[i]->copy();
track->mid_ = remote_media_desc.mid_; track->mid_ = remote_media_desc.mid_;
uint32_t publish_ssrc = track->ssrc_; 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"); 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()); 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(); SrsRtcTrackDescription* track = track_descs[i]->copy();
uint32_t publish_ssrc = track->ssrc_; 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]; SrsRtcTrackDescription* track = stream_desc->video_track_descs_[i];
// for plan b, we only add one m= // for plan b, we only add one m=

View file

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

View file

@ -35,6 +35,7 @@ using namespace std;
#include <srtp2/srtp.h> #include <srtp2/srtp.h>
#include <openssl/ssl.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 // 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 // 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(); int serial = rand();
ASN1_INTEGER_set(X509_get_serialNumber(dtls_cert), serial); 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_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, (unsigned char *) aor.data(), aor.size(), -1, 0);
X509_set_issuer_name(dtls_cert, subject); X509_set_issuer_name(dtls_cert, subject);
@ -385,10 +386,6 @@ srs_error_t SrsDtls::do_handshake()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
if (!callback) {
return srs_error_new(ERROR_RTC_DTLS, "no callback");
}
int ret = SSL_do_handshake(dtls); int ret = SSL_do_handshake(dtls);
unsigned char *out_bio_data; unsigned char *out_bio_data;
@ -419,7 +416,7 @@ srs_error_t SrsDtls::do_handshake()
if (out_bio_len) { if (out_bio_len) {
if ((err = callback->write_dtls_data(out_bio_data, out_bio_len)) != srs_success) { 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 (nb > 0 && callback) {
if ((err = callback->on_dtls_application_data(dtls_read_buf, nb)) != srs_success) { 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 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"; 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)) { 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; 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()); memcpy(rkey, recv_key.data(), recv_key.size());
policy.key = rkey; policy.key = rkey;
if (srtp_create(&recv_ctx_, &policy) != srtp_err_status_ok) { srtp_err_status_t r0 = srtp_err_status_ok;
return srs_error_new(ERROR_RTC_SRTP_INIT, "srtp_create recv failed"); 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; 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()); memcpy(skey, send_key.data(), send_key.size());
policy.key = skey; policy.key = skey;
if (srtp_create(&send_ctx_, &policy) != srtp_err_status_ok) { if ((r0 = srtp_create(&send_ctx_, &policy)) != srtp_err_status_ok) {
return srs_error_new(ERROR_RTC_SRTP_INIT, "srtp_create recv failed"); return srs_error_new(ERROR_RTC_SRTP_INIT, "srtp create r0=%u", r0);
} }
return err; 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; 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); memcpy(cipher, plaintext, nb_cipher);
// TODO: FIXME: Wrap error code.
if (srtp_protect(send_ctx_, cipher, &nb_cipher) != 0) { srtp_err_status_t r0 = srtp_err_status_ok;
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect failed"); 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; 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; 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); memcpy(cipher, plaintext, nb_cipher);
// TODO: FIXME: Wrap error code.
if (srtp_protect_rtcp(send_ctx_, cipher, &nb_cipher) != 0) { srtp_err_status_t r0 = srtp_err_status_ok;
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtcp protect failed"); 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; return err;
@ -591,9 +601,14 @@ srs_error_t SrsSRTP::protect_rtp2(void* rtp_hdr, int* len_ptr)
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
// TODO: FIXME: Wrap error code. // If DTLS/SRTP is not ready, fail.
if (srtp_protect(send_ctx_, rtp_hdr, len_ptr) != 0) { if (!send_ctx_) {
return srs_error_new(ERROR_RTC_SRTP_PROTECT, "rtp protect"); 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; 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; 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); memcpy(plaintext, cipher, nb_plaintext);
srtp_err_status_t r0 = srtp_unprotect(recv_ctx_, plaintext, &nb_plaintext);
if (r0 != srtp_err_status_ok) { srtp_err_status_t r0 = srtp_err_status_ok;
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "unprotect r0=%u", r0); 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; 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; 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); memcpy(plaintext, cipher, nb_plaintext);
// TODO: FIXME: Wrap error code.
if (srtp_unprotect_rtcp(recv_ctx_, plaintext, &nb_plaintext) != srtp_err_status_ok) { srtp_err_status_t r0 = srtp_err_status_ok;
return srs_error_new(ERROR_RTC_SRTP_UNPROTECT, "rtcp unprotect failed"); 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; return err;
} }

View file

@ -34,7 +34,7 @@ using namespace std;
#include <srs_kernel_log.hpp> #include <srs_kernel_log.hpp>
// TODO: FIXME: Maybe we should use json.encode to escape it? // 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) \ #define FETCH(is,word) \
if (!(is >> word)) {\ if (!(is >> word)) {\

View file

@ -97,6 +97,7 @@ void SrsRtcBlackhole::sendto(void* data, int len)
return; return;
} }
// For blackhole, we ignore any error.
srs_sendto(blackhole_stfd, data, len, (sockaddr*)blackhole_addr, sizeof(sockaddr_in), SRS_UTIME_NO_TIMEOUT); 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. // @global dtls certficate for rtc module.
SrsDtlsCertificate* _srs_rtc_dtls_certificate = new SrsDtlsCertificate(); 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() 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. // For STUN, the peer address may change.
if (is_stun((uint8_t*)data, size)) { if (srs_is_stun((uint8_t*)data, size)) {
SrsStunPacket ping; SrsStunPacket ping;
if ((err = ping.decode(data, size)) != srs_success) { if ((err = ping.decode(data, size)) != srs_success) {
return srs_error_wrap(err, "decode stun packet failed"); 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", 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()); 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) { if (!session) {
session = find_session_by_username(ping.get_username()); 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(); session->switch_to_context();
} }
} }
// TODO: FIXME: For ICE trickle, we may get STUN packets before SDP answer, so maybe should response it.
if (!session) { 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()); 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. // For DTLS, RTCP or RTP, which does not support peer address changing.
if (!session) { 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); return session->on_dtls(data, size);
} else if (is_rtp_or_rtcp((uint8_t*)data, size)) { } else if (srs_is_rtp_or_rtcp((uint8_t*)data, size)) {
if (is_rtcp((uint8_t*)data, size)) { if (srs_is_rtcp((uint8_t*)data, size)) {
return session->on_rtcp(data, size); return session->on_rtcp(data, size);
} }
return session->on_rtp(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() 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_pwd = srs_random_str(32);
std::string local_ufrag = ""; std::string local_ufrag = "";
// TODO: FIXME: Rename for a better name, it's not an username. // TODO: FIXME: Rename for a better name, it's not an username.

View file

@ -1349,7 +1349,7 @@ SrsRtcTrackDescription::SrsRtcTrackDescription()
ssrc_ = 0; ssrc_ = 0;
rtx_ssrc_ = 0; rtx_ssrc_ = 0;
fec_ssrc_ = 0; fec_ssrc_ = 0;
is_active_ = true; is_active_ = false;
media_ = NULL; media_ = NULL;
red_ = NULL; red_ = NULL;
@ -1374,6 +1374,7 @@ bool SrsRtcTrackDescription::has_ssrc(uint32_t ssrc)
if (ssrc == ssrc_ || ssrc == rtx_ssrc_ || ssrc == fec_ssrc_) { if (ssrc == ssrc_ || ssrc == rtx_ssrc_ || ssrc == fec_ssrc_) {
return true; return true;
} }
return false; return false;
} }
@ -1468,7 +1469,7 @@ SrsRtcStreamDescription::~SrsRtcStreamDescription()
{ {
srs_freep(audio_track_desc_); 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)); srs_freep(video_track_descs_.at(i));
} }
video_track_descs_.clear(); video_track_descs_.clear();
@ -1482,7 +1483,7 @@ SrsRtcStreamDescription* SrsRtcStreamDescription::copy()
stream_desc->audio_track_desc_ = audio_track_desc_->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()); 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_; 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)) { if (video_track_descs_.at(i)->has_ssrc(ssrc)) {
return video_track_descs_.at(i); return video_track_descs_.at(i);
} }
@ -1504,10 +1505,30 @@ SrsRtcTrackDescription* SrsRtcStreamDescription::find_track_description_by_ssrc(
return NULL; 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) SrsRtcRecvTrack::SrsRtcRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc, bool is_audio)
{ {
session_ = session; session_ = session;
track_desc_ = track_desc->copy(); track_desc_ = track_desc->copy();
statistic_ = new SrsRtcTrackStatistic();
if (is_audio) { if (is_audio) {
rtp_queue_ = new SrsRtpRingBuffer(100); rtp_queue_ = new SrsRtpRingBuffer(100);
nack_receiver_ = new SrsRtpNackForReceiver(rtp_queue_, 100 * 2 / 3); nack_receiver_ = new SrsRtpNackForReceiver(rtp_queue_, 100 * 2 / 3);
@ -1522,22 +1543,17 @@ SrsRtcRecvTrack::~SrsRtcRecvTrack()
srs_freep(rtp_queue_); srs_freep(rtp_queue_);
srs_freep(nack_receiver_); srs_freep(nack_receiver_);
srs_freep(track_desc_); srs_freep(track_desc_);
srs_freep(statistic_);
} }
bool SrsRtcRecvTrack::has_ssrc(uint32_t ssrc) bool SrsRtcRecvTrack::has_ssrc(uint32_t ssrc)
{ {
if (track_desc_) { return track_desc_->has_ssrc(ssrc);
return track_desc_->has_ssrc(ssrc);
}
return false;
} }
void SrsRtcRecvTrack::update_rtt(int rtt) 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) 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; srs_error_t err = srs_success;
if (session_) { uint32_t ssrc = track_desc_->ssrc_;
return session_->send_rtcp_rr(track_desc_->ssrc_, rtp_queue_, last_sender_report_sys_time, last_sender_report_ntp); 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; return err;
@ -1561,13 +1579,30 @@ srs_error_t SrsRtcRecvTrack::send_rtcp_xr_rrtr()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
if (track_desc_) { if ((err = session_->send_rtcp_xr_rrtr(track_desc_->ssrc_)) != srs_success) {
return session_->send_rtcp_xr_rrtr(track_desc_->ssrc_); return srs_error_wrap(err, "ssrc=%u", track_desc_->ssrc_);
} }
return err; 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 SrsRtcRecvTrack::on_nack(SrsRtpPacket2* pkt)
{ {
srs_error_t err = srs_success; 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 // insert into video_queue and audio_queue
rtp_queue_->set(seq, pkt->copy()); rtp_queue_->set(seq, pkt->copy());
// send_nack // 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; return err;
} }
srs_error_t SrsRtcRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket2* pkt)
{
return srs_success;
}
SrsRtcAudioRecvTrack::SrsRtcAudioRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc) SrsRtcAudioRecvTrack::SrsRtcAudioRecvTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc)
: SrsRtcRecvTrack(session, track_desc, true) : 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; srs_error_t err = srs_success;
if (source) { // connection level statistic
if ((err = source->on_rtp(pkt)) != srs_success) { session_->stat_->nn_in_audios++;
return srs_error_wrap(err, "source on rtp");
} // 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. // 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"); return srs_error_wrap(err, "on nack");
} }
session_->stat_->nn_in_audios++;
return err; return err;
} }
@ -1651,20 +1687,28 @@ srs_error_t SrsRtcVideoRecvTrack::on_rtp(SrsRtcStream* source, SrsRtpPacket2* pk
{ {
srs_error_t err = srs_success; 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; pkt->frame_type = SrsFrameTypeVideo;
if (source) { if ((err = source->on_rtp(pkt)) != srs_success) {
if ((err = source->on_rtp(pkt)) != srs_success) { return srs_error_wrap(err, "source on rtp");
return srs_error_wrap(err, "source on rtp");
}
} }
// TODO: FIXME: add rtp process // TODO: FIXME: add rtp process
if (request_key_frame_) { if (request_key_frame_) {
// TODO: FIXME: add coroutine to request key frame. // TODO: FIXME: add coroutine to request key frame.
request_key_frame_ = false; 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. // 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"); return srs_error_wrap(err, "on nack");
} }
session_->stat_->nn_in_videos++;
return err; return err;
} }
@ -1687,25 +1729,25 @@ SrsRtcSendTrack::SrsRtcSendTrack(SrsRtcConnection* session, SrsRtcTrackDescripti
{ {
session_ = session; session_ = session;
track_desc_ = track_desc->copy(); track_desc_ = track_desc->copy();
statistic_ = new SrsRtcTrackStatistic();
if (is_audio) { if (is_audio) {
rtp_queue_ = new SrsRtpRingBuffer(100); rtp_queue_ = new SrsRtpRingBuffer(100);
} else { } else {
rtp_queue_ = new SrsRtpRingBuffer(1000); rtp_queue_ = new SrsRtpRingBuffer(1000);
} }
} }
SrsRtcSendTrack::~SrsRtcSendTrack() SrsRtcSendTrack::~SrsRtcSendTrack()
{ {
srs_freep(rtp_queue_); srs_freep(rtp_queue_);
srs_freep(track_desc_); srs_freep(track_desc_);
srs_freep(statistic_);
} }
bool SrsRtcSendTrack::has_ssrc(uint32_t ssrc) bool SrsRtcSendTrack::has_ssrc(uint32_t ssrc)
{ {
if (track_desc_) { return track_desc_->has_ssrc(ssrc);
return track_desc_->has_ssrc(ssrc);
}
return false;
} }
SrsRtpPacket2* SrsRtcSendTrack::fetch_rtp_packet(uint16_t seq) 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. // 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; track_desc_->is_active_ = active;
return previous_status;
}
bool SrsRtcSendTrack::get_track_status()
{
return track_desc_->is_active_;
} }
std::string SrsRtcSendTrack::get_track_id() std::string SrsRtcSendTrack::get_track_id()
@ -1728,14 +1777,11 @@ std::string SrsRtcSendTrack::get_track_id()
return track_desc_->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) statistic->nacks++;
{
return srs_success;
} }
SrsRtcAudioSendTrack::SrsRtcAudioSendTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc) SrsRtcAudioSendTrack::SrsRtcAudioSendTrack(SrsRtcConnection* session, SrsRtcTrackDescription* track_desc)
@ -1755,7 +1801,6 @@ srs_error_t SrsRtcAudioSendTrack::on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamSta
return err; return err;
} }
std::vector<SrsRtpPacket2*> pkts;
pkt->header.set_ssrc(track_desc_->ssrc_); pkt->header.set_ssrc(track_desc_->ssrc_);
// Put rtp packet to NACK/ARQ queue // 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); rtp_queue_->set(nack->header.get_sequence(), nack);
} }
pkts.push_back(pkt);
// Update stats. // Update stats.
info.nn_bytes += pkt->nb_bytes(); info.nn_bytes += pkt->nb_bytes();
info.nn_audios++; info.nn_audios++;
session_->stat_->nn_out_audios++; session_->stat_->nn_out_audios++;
if ((err = session_->do_send_packets(pkts, info)) != srs_success) { // track level statistic
return srs_error_wrap(err, "raw send"); 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; return err;
@ -1801,24 +1853,33 @@ srs_error_t SrsRtcVideoSendTrack::on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamSta
if (!track_desc_->is_active_) { if (!track_desc_->is_active_) {
return err; return err;
} }
std::vector<SrsRtpPacket2*> pkts; SrsRtcTrackStatistic* statistic = statistic_;
pkt->header.set_ssrc(track_desc_->ssrc_); pkt->header.set_ssrc(track_desc_->ssrc_);
// Put rtp packet to NACK/ARQ queue // Put rtp packet to NACK/ARQ queue
if (true) { if (true) {
SrsRtpPacket2* nack = pkt->copy(); SrsRtpPacket2* nack = pkt->copy();
rtp_queue_->set(nack->header.get_sequence(), nack); rtp_queue_->set(nack->header.get_sequence(), nack);
} }
pkts.push_back(pkt);
// Update stats. // Update stats.
info.nn_bytes += pkt->nb_bytes(); info.nn_bytes += pkt->nb_bytes();
info.nn_videos++; info.nn_videos++;
session_->stat_->nn_out_videos++; session_->stat_->nn_out_videos++;
if ((err = session_->do_send_packets(pkts, info)) != srs_success) { // track level statistic
return srs_error_wrap(err, "raw send"); 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; return err;

View file

@ -404,10 +404,50 @@ public:
SrsRtcTrackDescription* find_track_description_by_ssrc(uint32_t ssrc); 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 class SrsRtcRecvTrack
{ {
protected: protected:
SrsRtcTrackDescription* track_desc_; SrsRtcTrackDescription* track_desc_;
SrsRtcTrackStatistic* statistic_;
SrsRtcConnection* session_; SrsRtcConnection* session_;
SrsRtpRingBuffer* rtp_queue_; SrsRtpRingBuffer* rtp_queue_;
@ -425,10 +465,13 @@ public:
void update_send_report_time(const SrsNtp& ntp); void update_send_report_time(const SrsNtp& ntp);
srs_error_t send_rtcp_rr(); srs_error_t send_rtcp_rr();
srs_error_t send_rtcp_xr_rrtr(); srs_error_t send_rtcp_xr_rrtr();
bool set_track_status(bool active);
bool get_track_status();
std::string get_track_id();
protected: protected:
srs_error_t on_nack(SrsRtpPacket2* pkt); srs_error_t on_nack(SrsRtpPacket2* pkt);
public: 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 class SrsRtcAudioRecvTrack : public SrsRtcRecvTrack
@ -458,7 +501,9 @@ class SrsRtcSendTrack
protected: protected:
// send track description // send track description
SrsRtcTrackDescription* track_desc_; SrsRtcTrackDescription* track_desc_;
SrsRtcTrackStatistic* statistic_;
// The owner connection for this track.
SrsRtcConnection* session_; SrsRtcConnection* session_;
// NACK ARQ ring buffer. // NACK ARQ ring buffer.
SrsRtpRingBuffer* rtp_queue_; SrsRtpRingBuffer* rtp_queue_;
@ -468,11 +513,13 @@ public:
public: public:
bool has_ssrc(uint32_t ssrc); bool has_ssrc(uint32_t ssrc);
SrsRtpPacket2* fetch_rtp_packet(uint16_t seq); 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(); std::string get_track_id();
public: public:
virtual srs_error_t on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamStatistic& info); virtual srs_error_t on_rtp(SrsRtpPacket2* pkt, SrsRtcPlayStreamStatistic& info) = 0;
virtual srs_error_t on_rtcp(SrsRtpPacket2* pkt); virtual srs_error_t on_rtcp(SrsRtpPacket2* pkt) = 0;
virtual void on_recv_nack();
}; };
class SrsRtcAudioSendTrack : public SrsRtcSendTrack 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)); 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]; string srs_string_dumps_hex(const char* str, int length, int limit)
tmp_buf[0] = '\n'; {
int len = 1; return srs_string_dumps_hex(str, length, limit, ' ', 128, '\n');
}
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]); string srs_string_dumps_hex(const char* str, int length, int limit, char seperator, int line_limit, char newline)
if (nb <= 0) {
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; 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) { if (newline && line_limit && i > 0 && ((i + 1) % line_limit) == 0 && len < LIMIT) {
tmp_buf[len++] = '\n'; buf[len++] = newline;
ret.append(tmp_buf, len); }
len = 0; }
} }
}
tmp_buf[len] = '\0';
ret.append(tmp_buf, len);
return ret;
// Empty string.
if (len <= 0) {
return "";
}
return string(buf, len);
} }
string srs_getenv(string key) 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); extern void srs_api_dump_summaries(SrsJsonObject* obj);
// Dump string(str in length) to hex, it will process min(limit, length) chars. // 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); // Append seperator between each elem, and newline when exceed line_limit, '\0' to ignore.
extern std::string srs_string_dumps_hex(const char* str, const int length, const int& limit = INT_MAX); 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 $. // Get ENV variable, which may starts with $.
// srs_getenv("EIP") === srs_getenv("$EIP") // 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_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_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_SERVER RTMP_SIG_SRS_KEY "/" RTMP_SIG_SRS_VERSION "(" RTMP_SIG_SRS_CODE ")"
#define RTMP_SIG_SRS_DOMAIN "ossrs.net"
// The current stable release. // The current stable release.
#define VERSION_STABLE 3 #define VERSION_STABLE 3

View file

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

View file

@ -356,6 +356,7 @@
#define ERROR_RTC_STREM_STARTED 5025 #define ERROR_RTC_STREM_STARTED 5025
#define ERROR_RTC_STREAM_DESC 5026 #define ERROR_RTC_STREAM_DESC 5026
#define ERROR_RTC_TRACK_CODEC 5027 #define ERROR_RTC_TRACK_CODEC 5027
#define ERROR_RTC_NO_PLAYER 5028
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// GB28181 API error. // 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; return header_.type;
} }
@ -90,27 +90,27 @@ SrsRtcpApp::~SrsRtcpApp()
{ {
} }
const uint8_t SrsRtcpApp::type() const uint8_t SrsRtcpApp::type() const
{ {
return SrsRtcpType_app; return SrsRtcpType_app;
} }
const uint32_t SrsRtcpApp::get_ssrc() const uint32_t SrsRtcpApp::get_ssrc() const
{ {
return ssrc_; return ssrc_;
} }
const uint8_t SrsRtcpApp::get_subtype() const uint8_t SrsRtcpApp::get_subtype() const
{ {
return header_.rc; return header_.rc;
} }
const string SrsRtcpApp::get_name() const string SrsRtcpApp::get_name() const
{ {
return string((char*)name_); 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_; len = payload_len_;
payload = payload_; payload = payload_;
@ -213,37 +213,37 @@ SrsRtcpSR::~SrsRtcpSR()
{ {
} }
const uint8_t SrsRtcpSR::get_rc() const uint8_t SrsRtcpSR::get_rc() const
{ {
return header_.rc; return header_.rc;
} }
const uint8_t SrsRtcpSR::type() const uint8_t SrsRtcpSR::type() const
{ {
return SrsRtcpType_sr; return SrsRtcpType_sr;
} }
const uint32_t SrsRtcpSR::get_sender_ssrc() const uint32_t SrsRtcpSR::get_sender_ssrc() const
{ {
return sender_ssrc_; return sender_ssrc_;
} }
const uint64_t SrsRtcpSR::get_ntp() const uint64_t SrsRtcpSR::get_ntp() const
{ {
return ntp_; return ntp_;
} }
const uint32_t SrsRtcpSR::get_rtp_ts() const uint32_t SrsRtcpSR::get_rtp_ts() const
{ {
return rtp_ts_; return rtp_ts_;
} }
const uint32_t SrsRtcpSR::get_rtp_send_packets() const uint32_t SrsRtcpSR::get_rtp_send_packets() const
{ {
return send_rtp_packets_; 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_; return send_rtp_bytes_;
} }
@ -334,42 +334,42 @@ SrsRtcpRR::~SrsRtcpRR()
{ {
} }
const uint8_t SrsRtcpRR::type() const uint8_t SrsRtcpRR::type() const
{ {
return SrsRtcpType_rr; return SrsRtcpType_rr;
} }
const uint32_t SrsRtcpRR::get_rb_ssrc() const uint32_t SrsRtcpRR::get_rb_ssrc() const
{ {
return rb_.ssrc; return rb_.ssrc;
} }
const float SrsRtcpRR::get_lost_rate() const float SrsRtcpRR::get_lost_rate() const
{ {
return rb_.fraction_lost / 256; return rb_.fraction_lost / 256;
} }
const uint32_t SrsRtcpRR::get_lost_packets() const uint32_t SrsRtcpRR::get_lost_packets() const
{ {
return rb_.lost_packets; return rb_.lost_packets;
} }
const uint32_t SrsRtcpRR::get_highest_sn() const uint32_t SrsRtcpRR::get_highest_sn() const
{ {
return rb_.highest_sn; return rb_.highest_sn;
} }
const uint32_t SrsRtcpRR::get_jitter() const uint32_t SrsRtcpRR::get_jitter() const
{ {
return rb_.jitter; return rb_.jitter;
} }
const uint32_t SrsRtcpRR::get_lsr() const uint32_t SrsRtcpRR::get_lsr() const
{ {
return rb_.lsr; return rb_.lsr;
} }
const uint32_t SrsRtcpRR::get_dlsr() const uint32_t SrsRtcpRR::get_dlsr() const
{ {
return rb_.dlsr; return rb_.dlsr;
} }
@ -493,36 +493,36 @@ void SrsRtcpTWCC::clear()
recv_sns_.clear(); recv_sns_.clear();
} }
const uint32_t SrsRtcpTWCC::get_media_ssrc() const uint32_t SrsRtcpTWCC::get_media_ssrc() const
{ {
return media_ssrc_; return media_ssrc_;
} }
const uint16_t SrsRtcpTWCC::get_base_sn() const uint16_t SrsRtcpTWCC::get_base_sn() const
{ {
return base_sn_; return base_sn_;
} }
const uint32_t SrsRtcpTWCC::get_reference_time() const uint32_t SrsRtcpTWCC::get_reference_time() const
{ {
return reference_time_; return reference_time_;
} }
const uint8_t SrsRtcpTWCC::get_feedback_count() const uint8_t SrsRtcpTWCC::get_feedback_count() const
{ {
return fb_pkt_count_; return fb_pkt_count_;
} }
const uint16_t SrsRtcpTWCC::get_packet_status_count() const uint16_t SrsRtcpTWCC::get_packet_status_count() const
{ {
return packet_count_; return packet_count_;
} }
const vector<uint16_t> SrsRtcpTWCC::get_packet_chucks() const vector<uint16_t> SrsRtcpTWCC::get_packet_chucks() const
{ {
return encoded_chucks_; return encoded_chucks_;
} }
const vector<uint16_t> SrsRtcpTWCC::get_recv_deltas() const vector<uint16_t> SrsRtcpTWCC::get_recv_deltas() const
{ {
return pkt_deltas_; 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_; return media_ssrc_;
} }
const vector<uint16_t> SrsRtcpNack::get_lost_sns() const vector<uint16_t> SrsRtcpNack::get_lost_sns() const
{ {
vector<uint16_t> sn; vector<uint16_t> sn;
for(set<uint16_t, SrsSeqCompareLess>::iterator it = lost_sns_.begin(); it != lost_sns_.end(); ++it) { for(set<uint16_t, SrsSeqCompareLess>::iterator it = lost_sns_.begin(); it != lost_sns_.end(); ++it) {

View file

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

View file

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

View file

@ -43,7 +43,7 @@ srs_utime_t _srs_tmp_timeout = (100 * SRS_UTIME_MILLISECONDS);
ISrsLog* _srs_log = new MockEmptyLog(SrsLogLevelDisabled); ISrsLog* _srs_log = new MockEmptyLog(SrsLogLevelDisabled);
ISrsContext* _srs_context = new SrsThreadContext(); ISrsContext* _srs_context = new SrsThreadContext();
// app module. // app module.
SrsConfig* _srs_config = NULL; SrsConfig* _srs_config = new SrsConfig();
SrsServer* _srs_server = NULL; SrsServer* _srs_server = NULL;
bool _srs_in_docker = false; 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 #ifndef SRS_UTEST_PUBLIC_SHARED_HPP
#define 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. // Public all private and protected members.
#define private public #define private public
#define protected 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 <srs_core.hpp>
#include "gtest/gtest.h"
#include <string> #include <string>
using namespace std; 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_kernel_error.hpp>
#include <srs_core_autofree.hpp> #include <srs_core_autofree.hpp>
#include <srs_app_rtc_queue.hpp> #include <srs_app_rtc_queue.hpp>
#include <srs_app_utility.hpp>
#include <srs_kernel_rtc_rtp.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) 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());
}
}