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

merge upstream feature/rtc, solve conflict

This commit is contained in:
xiaozhihong 2020-04-19 15:02:21 +08:00
commit 749503a12e
28 changed files with 1173 additions and 439 deletions

View file

@ -22,6 +22,8 @@ The branch [srs](https://github.com/ossrs/state-threads/tree/srs) will be patche
- [x] Patch [st.osx10.14.build.patch](https://github.com/ossrs/srs/blob/2.0release/trunk/3rdparty/patches/6.st.osx10.14.build.patch), for osx 10.14 build.
- [x] Support macro `MD_ST_NO_ASM` to disable ASM, [#8](https://github.com/ossrs/state-threads/issues/8).
- [x] Merge patch [srs#1282](https://github.com/ossrs/srs/issues/1282#issuecomment-445539513) to support aarch64, [#9](https://github.com/ossrs/state-threads/issues/9).
- [x] Support OSX for Apple Darwin, macOS, [#11](https://github.com/ossrs/state-threads/issues/11).
- [x] Support sendmmsg for UDP, [#12](https://github.com/ossrs/state-threads/issues/12).
## Docs
@ -85,4 +87,10 @@ Important cli options:
1. `--track-origins=<yes|no> [default: no]`, Controls whether Memcheck tracks the origin of uninitialised values. By default, it does not, which means that although it can tell you that an uninitialised value is being used in a dangerous way, it cannot tell you where the uninitialised value came from. This often makes it difficult to track down the root problem.
1. `--show-reachable=<yes|no> , --show-possibly-lost=<yes|no>`, to show the using memory.
## Analysis
1. About setjmp and longjmp, read [setjmp](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-setjmp.jpg).
1. About the stack structure, read [stack](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-stack.jpg)
1. About asm code comments, read [#91d530e](https://github.com/ossrs/state-threads/commit/91d530e#diff-ed9428b14ff6afda0e9ab04cc91d4445R25).
Winlin 2016

View file

@ -84,15 +84,6 @@ function Ubuntu_prepare()
echo "The unzip is installed."
fi
if [[ $SRS_NASM == YES ]]; then
nasm -v >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "Installing nasm."
require_sudoer "sudo apt-get install -y --force-yes nasm"
sudo apt-get install -y --force-yes nasm; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
echo "The nasm is installed."
fi
fi
if [[ $SRS_VALGRIND == YES ]]; then
valgrind --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "Installing valgrind."
@ -171,13 +162,6 @@ function Centos_prepare()
echo "The unzip is installed."
fi
nasm -v >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "Installing nasm."
require_sudoer "sudo yum install -y nasm"
sudo yum install -y nasm; ret=$?; if [[ 0 -ne $ret ]]; then return $ret; fi
echo "The nasm is installed."
fi
if [[ $SRS_VALGRIND == YES ]]; then
valgrind --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "Installing valgrind."
@ -230,15 +214,16 @@ function OSX_prepare()
fi
OS_IS_OSX=YES
echo "OSX detected, install tools if needed"
# requires the osx when os
if [ $OS_IS_OSX = YES ]; then
if [ $SRS_OSX = NO ]; then
echo "OSX detected, must specifies the --osx"
echo "OSX detected, please use: ./configure --osx"
exit 1
fi
fi
echo "OSX detected, install tools if needed"
brew --help >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "install brew"
echo "ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\""
@ -281,6 +266,10 @@ function OSX_prepare()
echo "install unzip success"
fi
pkg-config --version >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
echo "Please install pkg-config"; exit -1;
fi
echo "OSX install tools success"
return 0
}
@ -614,9 +603,16 @@ fi
#####################################################################################
if [[ $SRS_EXPORT_LIBRTMP_PROJECT == NO && $SRS_RTC == YES ]]; then
FFMPEG_OPTIONS=""
# If disable nasm, disable all ASMs.
if [[ $SRS_NASM == NO ]]; then
FFMPEG_OPTIONS="--disable-asm --disable-x86asm --disable-inline-asm"
fi
# If no nasm, we disable the x86asm.
nasm -v >/dev/null 2>&1; ret=$?; if [[ 0 -ne $ret ]]; then
FFMPEG_OPTIONS="--disable-x86asm"
fi
if [[ -f ${SRS_OBJS}/${SRS_PLATFORM}/ffmpeg/lib/libavcodec.a ]]; then
echo "The ffmpeg-4.2-fit is ok.";
else

View file

@ -569,6 +569,7 @@ function apply_user_detail_options() {
# Detect whether has sendmmsg.
# @see http://man7.org/linux/man-pages/man2/sendmmsg.2.html
mkdir -p ${SRS_OBJS} &&
echo "#include <sys/socket.h>" > ${SRS_OBJS}/_tmp_sendmmsg_detect.c
echo "int main(int argc, char** argv) {" >> ${SRS_OBJS}/_tmp_sendmmsg_detect.c
echo " struct mmsghdr hdr;" >> ${SRS_OBJS}/_tmp_sendmmsg_detect.c

View file

@ -443,6 +443,21 @@ rtc_server {
# @remark Linux 4.18+ only, for other OS always disabled.
# default: on
gso on;
# Whether pad first packet for GSO for padding bytes.
# If 0, disable padding for GSO.
# @remark The max padding size is 0x7f(127).
# default: 127
padding 127;
# Whether enable the perf stat at http://localhost:1985/api/v1/perf
# default: on
perf_stat on;
# The queue length, in number of mmsghdr, in messages.
# For example, 30 means we will cache 30K messages at most.
# If exceed, we will drop messages.
# @remark Each reuseport use a dedicated queue, if queue is 2000, reuseport is 4,
# then system queue is 2000*4 = 8k, user can incrase reuseport to incrase the queue.
# default: 2000
queue_length 2000;
}
vhost rtc.vhost.srs.com {
@ -469,12 +484,17 @@ vhost rtc.vhost.srs.com {
stun_strict_check on;
}
# whether enable min delay mode for vhost.
# For RTC, we recommend to set to on.
# default: on, for RTC.
min_latency on;
play {
# set the MW(merged-write) latency in ms.
# For RTC, we recommend lower value, such as 0.
# @remark For WebRTC, we enable pass-timestamp mode, so we ignore this config.
# default: 0 (For WebRTC)
mw_latency 0;
# Set the MW(merged-write) min messages.
# default: 0 (For Real-Time, min_latency on)
# default: 1 (For WebRTC, min_latency off)
mw_msgs 0;
}
}
@ -701,10 +721,17 @@ vhost play.srs.com {
# SRS always set mw on, so we just set the latency value.
# the latency of stream >= mw_latency + mr_latency
# the value recomment is [300, 1800]
# default: 350 (for RTMP/HTTP-FLV)
# default: 0 (for WebRTC)
# @remark For WebRTC, we enable pass-timestamp mode, so we ignore this config.
# default: 350 (For RTMP/HTTP-FLV)
# default: 0 (For WebRTC)
mw_latency 350;
# Set the MW(merged-write) min messages.
# default: 0 (For Real-Time, min_latency on)
# default: 1 (For WebRTC, min_latency off)
# default: 8 (For RTMP/HTTP-FLV, min_latency off).
mw_msgs 8;
# the minimal packets send interval in ms,
# used to control the ndiff of stream by srs_rtmp_dump,
# for example, some device can only accept some stream which
@ -761,6 +788,7 @@ vhost mrw.srs.com {
# @see play.srs.com
play {
mw_latency 350;
mw_msgs 8;
}
# @see publish.srs.com
@ -780,6 +808,7 @@ vhost min.delay.com {
# @see play.srs.com
play {
mw_latency 100;
mw_msgs 4;
gop_cache off;
queue_length 10;
}
@ -808,6 +837,7 @@ vhost stream.control.com {
# @see play.srs.com
play {
mw_latency 100;
mw_msgs 4;
queue_length 10;
send_min_interval 10.0;
reduce_sequence_header on;

View file

@ -41,8 +41,15 @@ print "Repsonse %s"%(s)
obj = json.loads(s)
print ""
p = obj['data']['dropped']
print('Frame-Dropped: %.1f%s'%(10000.0 * p['rtc_dropeed'] / p['rtc_frames'], '%%'))
p = obj['data']['bytes']
print('Padding-Overload: %.1f%s %dMB'%(10000.0 * p['rtc_padding'] / p['rtc_bytes'], '%%', p['rtc_padding']/1024/1024))
# 2, 3, 5, 9, 16, 32, 64, 128, 256
keys = ['lt_2', 'lt_3', 'lt_5', 'lt_9', 'lt_16', 'lt_32', 'lt_64', 'lt_128', 'lt_256', 'gt_256']
print("\n----------- 1 2 [3,4] [5,8] [9,15] [16,31] [32,63] [64,127] [128,255] [256,+) Packets"),
print ""
print("AV---Frames"),

View file

@ -3618,7 +3618,8 @@ srs_error_t SrsConfig::check_normal_config()
for (int i = 0; conf && i < (int)conf->directives.size(); i++) {
string n = conf->at(i)->name;
if (n != "enabled" && n != "listen" && n != "dir" && n != "candidate" && n != "ecdsa"
&& n != "sendmmsg" && n != "encrypt" && n != "reuseport" && n != "gso" && n != "merge_nalus") {
&& n != "sendmmsg" && n != "encrypt" && n != "reuseport" && n != "gso" && n != "merge_nalus"
&& n != "padding" && n != "perf_stat" && n != "queue_length") {
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal rtc_server.%s", n.c_str());
}
}
@ -3784,7 +3785,8 @@ srs_error_t SrsConfig::check_normal_config()
for (int j = 0; j < (int)conf->directives.size(); j++) {
string m = conf->at(j)->name;
if (m != "time_jitter" && m != "mix_correct" && m != "atc" && m != "atc_auto" && m != "mw_latency"
&& m != "gop_cache" && m != "queue_length" && m != "send_min_interval" && m != "reduce_sequence_header") {
&& m != "gop_cache" && m != "queue_length" && m != "send_min_interval" && m != "reduce_sequence_header"
&& m != "mw_msgs") {
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.play.%s of %s", m.c_str(), vhost->arg0().c_str());
}
}
@ -4840,6 +4842,57 @@ bool SrsConfig::get_rtc_server_gso2()
return SRS_CONF_PERFER_TRUE(conf->arg0());
}
int SrsConfig::get_rtc_server_padding()
{
static int DEFAULT = 127;
SrsConfDirective* conf = root->get("rtc_server");
if (!conf) {
return DEFAULT;
}
conf = conf->get("padding");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return srs_min(127, ::atoi(conf->arg0().c_str()));
}
bool SrsConfig::get_rtc_server_perf_stat()
{
static bool DEFAULT = true;
SrsConfDirective* conf = root->get("rtc_server");
if (!conf) {
return DEFAULT;
}
conf = conf->get("perf_stat");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return SRS_CONF_PERFER_TRUE(conf->arg0());
}
int SrsConfig::get_rtc_server_queue_length()
{
static int DEFAULT = 2000;
SrsConfDirective* conf = root->get("rtc_server");
if (!conf) {
return DEFAULT;
}
conf = conf->get("queue_length");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return ::atoi(conf->arg0().c_str());
}
SrsConfDirective* SrsConfig::get_rtc(string vhost)
{
SrsConfDirective* conf = get_vhost(vhost);
@ -5367,8 +5420,48 @@ srs_utime_t SrsConfig::get_mw_sleep(string vhost, bool is_rtc)
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
int v = ::atoi(conf->arg0().c_str());
if (is_rtc && v > 0) {
srs_warn("For RTC, we ignore mw_latency");
return 0;
}
return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_MILLISECONDS);
return (srs_utime_t)(v * SRS_UTIME_MILLISECONDS);
}
int SrsConfig::get_mw_msgs(string vhost, bool is_realtime, bool is_rtc)
{
int DEFAULT = SRS_PERF_MW_MIN_MSGS;
if (is_rtc) {
DEFAULT = SRS_PERF_MW_MIN_MSGS_FOR_RTC;
}
if (is_realtime) {
DEFAULT = SRS_PERF_MW_MIN_MSGS_REALTIME;
}
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return DEFAULT;
}
conf = conf->get("play");
if (!conf) {
return DEFAULT;
}
conf = conf->get("mw_msgs");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
int v = ::atoi(conf->arg0().c_str());
if (v > SRS_PERF_MW_MSGS) {
srs_warn("reset mw_msgs %d to max %d", v, SRS_PERF_MW_MSGS);
v = SRS_PERF_MW_MSGS;
}
return v;
}
bool SrsConfig::get_realtime_enabled(string vhost, bool is_rtc)

View file

@ -535,6 +535,11 @@ public:
virtual bool get_rtc_server_gso();
private:
virtual bool get_rtc_server_gso2();
public:
virtual int get_rtc_server_padding();
virtual bool get_rtc_server_perf_stat();
virtual int get_rtc_server_queue_length();
public:
SrsConfDirective* get_rtc(std::string vhost);
bool get_rtc_enabled(std::string vhost);
@ -625,6 +630,10 @@ public:
// @param vhost, the vhost to get the mw sleep time.
// TODO: FIXME: add utest for mw config.
virtual srs_utime_t get_mw_sleep(std::string vhost, bool is_rtc = false);
// Get the mw_msgs, mw wait time in packets for vhost.
// @param vhost, the vhost to get the mw sleep msgs.
// TODO: FIXME: add utest for mw config.
virtual int get_mw_msgs(std::string vhost, bool is_realtime, bool is_rtc = false);
// Whether min latency mode enabled.
// @param vhost, the vhost to get the min_latency.
// TODO: FIXME: add utest for min_latency.

View file

@ -869,6 +869,7 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
api = prop->to_str();
}
// TODO: FIXME: Parse vhost.
// Parse app and stream from streamurl.
string app;
string stream_name;
@ -909,6 +910,13 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
request.app = app;
request.stream = stream_name;
// TODO: FIXME: Parse vhost.
// discovery vhost, resolve the vhost from config
SrsConfDirective* parsed_vhost = _srs_config->get_vhost("");
if (parsed_vhost) {
request.vhost = parsed_vhost->arg0();
}
// TODO: FIXME: Maybe need a better name?
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
SrsRtcSession* rtc_session = rtc_server->create_rtc_session(request, remote_sdp, local_sdp, eip);
@ -1615,14 +1623,23 @@ srs_error_t SrsGoApiPerf::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage*
SrsStatistic* stat = SrsStatistic::instance();
string target = r->query_get("target");
srs_trace("query target=%s", target.c_str());
string reset = r->query_get("reset");
srs_trace("query target=%s, reset=%s, rtc_stat_enabled=%d", target.c_str(), reset.c_str(),
_srs_config->get_rtc_server_perf_stat());
if (true) {
SrsJsonObject* p = SrsJsonAny::object();
data->set("query", p);
p->set("target", SrsJsonAny::str(target.c_str()));
p->set("help", SrsJsonAny::str("?target=avframes|rtc|rtp|gso|writev_iovs|sendmmsg"));
p->set("reset", SrsJsonAny::str(reset.c_str()));
p->set("help", SrsJsonAny::str("?target=avframes|rtc|rtp|gso|writev_iovs|sendmmsg|bytes|dropped"));
p->set("help2", SrsJsonAny::str("?reset=all"));
}
if (!reset.empty()) {
stat->reset_perf();
return srs_api_response(w, r, obj->dumps());
}
if (target.empty() || target == "avframes") {
@ -1679,6 +1696,24 @@ srs_error_t SrsGoApiPerf::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage*
}
}
if (target.empty() || target == "bytes") {
SrsJsonObject* p = SrsJsonAny::object();
data->set("bytes", p);
if ((err = stat->dumps_perf_bytes(p)) != srs_success) {
int code = srs_error_code(err); srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
}
if (target.empty() || target == "dropped") {
SrsJsonObject* p = SrsJsonAny::object();
data->set("dropped", p);
if ((err = stat->dumps_perf_dropped(p)) != srs_success) {
int code = srs_error_code(err); srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
}
return srs_api_response(w, r, obj->dumps());
}

View file

@ -660,7 +660,8 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess
if ((err = consumer->dump_packets(&msgs, count)) != srs_success) {
return srs_error_wrap(err, "consumer dump packets");
}
// TODO: FIXME: Support merged-write wait.
if (count <= 0) {
// Directly use sleep, donot use consumer wait, because we couldn't awake consumer.
srs_usleep(mw_sleep);

View file

@ -141,6 +141,11 @@ public:
virtual srs_error_t fetch(mmsghdr** pphdr) = 0;
// Notify the sender to send out the msg.
virtual srs_error_t sendmmsg(mmsghdr* hdr) = 0;
// Whether sender exceed the max queue, that is, overflow.
virtual bool overflow() = 0;
// Set the queue extra ratio, for example, when mw_msgs > 0, we need larger queue.
// For r, 100 means x1, 200 means x2.
virtual void set_extra_ratio(int r) = 0;
};
class SrsUdpMuxSocket

View file

@ -195,15 +195,19 @@ srs_error_t SrsRtpOpusMuxer::transcode(SrsSharedPtrMessage* shared_audio, char*
return err;
}
int nn_max_extra_payload = 0;
SrsSample samples[nn_opus_packets];
for (int i = 0; i < nn_opus_packets; i++) {
SrsSample* p = samples + i;
p->size = opus_sizes[i];
p->bytes = new char[p->size];
memcpy(p->bytes, opus_payloads[i], p->size);
nn_max_extra_payload = srs_max(nn_max_extra_payload, p->size);
}
shared_audio->set_extra_payloads(samples, nn_opus_packets);
shared_audio->set_max_extra_payload(nn_max_extra_payload);
return err;
}

File diff suppressed because it is too large Load diff

View file

@ -50,6 +50,7 @@ class SrsRtcSession;
class SrsSharedPtrMessage;
class SrsSource;
class SrsRtpPacket2;
class ISrsUdpSender;
const uint8_t kSR = 200;
const uint8_t kRR = 201;
@ -124,8 +125,18 @@ public:
bool use_gso;
bool should_merge_nalus;
public:
// The total bytes of RTP packets.
#if defined(SRS_DEBUG)
// Debug id.
uint32_t debug_id;
#endif
public:
// The total bytes of AVFrame packets.
int nn_bytes;
// The total bytes of RTP packets.
int nn_rtp_bytes;
// The total padded bytes.
int nn_padding_bytes;
public:
// The RTP packets send out by sendmmsg or sendmsg. Note that if many packets group to
// one msghdr by GSO, it's only one RTP packet, because we only send once.
int nn_rtp_pkts;
@ -138,11 +149,24 @@ public:
int nn_audios;
// The original video messages.
int nn_videos;
// The number of padded packet.
int nn_paddings;
// The number of dropped messages.
int nn_dropped;
private:
int cursor;
int nn_cache;
SrsRtpPacket2* cache;
public:
std::vector<SrsRtpPacket2*> packets;
public:
SrsRtcPackets(bool gso, bool merge_nalus);
SrsRtcPackets(int nn_cache_max);
virtual ~SrsRtcPackets();
public:
void reset(bool gso, bool merge_nalus);
SrsRtpPacket2* fetch();
SrsRtpPacket2* back();
int size();
int capacity();
SrsRtpPacket2* at(int index);
};
class SrsRtcSenderThread : virtual public ISrsCoroutineHandler, virtual public ISrsReloadHandler
@ -164,8 +188,16 @@ private:
uint16_t video_sequence;
public:
SrsUdpMuxSocket* sendonly_ukt;
private:
ISrsUdpSender* sender;
private:
bool merge_nalus;
bool gso;
int max_padding;
private:
srs_utime_t mw_sleep;
int mw_msgs;
bool realtime;
public:
SrsRtcSenderThread(SrsRtcSession* s, SrsUdpMuxSocket* u, int parent_cid);
virtual ~SrsRtcSenderThread();
@ -174,6 +206,8 @@ public:
// interface ISrsReloadHandler
public:
virtual srs_error_t on_reload_rtc_server();
virtual srs_error_t on_reload_vhost_play(std::string vhost);
virtual srs_error_t on_reload_vhost_realtime(std::string vhost);
public:
virtual int cid();
public:
@ -185,17 +219,17 @@ public:
public:
virtual srs_error_t cycle();
private:
srs_error_t send_messages(SrsUdpMuxSocket* skt, SrsSource* source, SrsSharedPtrMessage** msgs, int nb_msgs, SrsRtcPackets& packets);
srs_error_t send_messages(SrsSource* source, SrsSharedPtrMessage** msgs, int nb_msgs, SrsRtcPackets& packets);
srs_error_t messages_to_packets(SrsSource* source, SrsSharedPtrMessage** msgs, int nb_msgs, SrsRtcPackets& packets);
srs_error_t send_packets(SrsUdpMuxSocket* skt, SrsRtcPackets& packets);
srs_error_t send_packets_gso(SrsUdpMuxSocket* skt, SrsRtcPackets& packets);
srs_error_t send_packets(SrsRtcPackets& packets);
srs_error_t send_packets_gso(SrsRtcPackets& packets);
private:
srs_error_t packet_opus(SrsSample* sample, SrsRtpPacket2** ppacket);
srs_error_t packet_opus(SrsSample* sample, SrsRtcPackets& packets, int nn_max_payload);
private:
srs_error_t packet_fu_a(SrsSharedPtrMessage* msg, SrsSample* sample, int fu_payload_size, SrsRtcPackets& packets);
srs_error_t packet_nalus(SrsSharedPtrMessage* msg, SrsRtcPackets& packets);
srs_error_t packet_single_nalu(SrsSharedPtrMessage* msg, SrsSample* sample, SrsRtpPacket2** ppacket);
srs_error_t packet_stap_a(SrsSource* source, SrsSharedPtrMessage* msg, SrsRtpPacket2** ppacket);
srs_error_t packet_single_nalu(SrsSharedPtrMessage* msg, SrsSample* sample, SrsRtcPackets& packets);
srs_error_t packet_stap_a(SrsSource* source, SrsSharedPtrMessage* msg, SrsRtcPackets& packets);
};
class SrsRtcSession
@ -274,6 +308,9 @@ private:
private:
srs_cond_t cond;
bool waiting_msgs;
bool gso;
int nn_senders;
private:
// Hotspot msgs, we are working on it.
// @remark We will wait util all messages are ready.
std::vector<mmsghdr> hotspot;
@ -282,16 +319,24 @@ private:
int cache_pos;
// The max number of messages for sendmmsg. If 1, we use sendmsg to send.
int max_sendmmsg;
// The total queue length, for each sender.
int queue_length;
// The extra queue ratio.
int extra_ratio;
int extra_queue;
public:
SrsUdpMuxSender(SrsRtcServer* s);
virtual ~SrsUdpMuxSender();
public:
virtual srs_error_t initialize(srs_netfd_t fd);
virtual srs_error_t initialize(srs_netfd_t fd, int senders);
private:
void free_mhdrs(std::vector<mmsghdr>& mhdrs);
public:
virtual srs_error_t fetch(mmsghdr** pphdr);
virtual srs_error_t sendmmsg(mmsghdr* hdr);
virtual bool overflow();
virtual void set_extra_ratio(int r);
public:
virtual srs_error_t cycle();
// interface ISrsReloadHandler
public:

View file

@ -116,7 +116,7 @@ SrsRtmpConn::SrsRtmpConn(SrsServer* svr, srs_netfd_t c, string cip) : SrsConnect
wakable = NULL;
mw_sleep = SRS_PERF_MW_SLEEP;
mw_enabled = false;
mw_msgs = 0;
realtime = SRS_PERF_MIN_LATENCY_ENABLED;
send_min_interval = 0;
tcp_nodelay = false;
@ -264,6 +264,10 @@ srs_error_t SrsRtmpConn::on_reload_vhost_play(string vhost)
send_min_interval = v;
}
}
mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime);
mw_sleep = _srs_config->get_mw_sleep(req->vhost);
set_socket_buffer(mw_sleep);
return err;
}
@ -298,6 +302,10 @@ srs_error_t SrsRtmpConn::on_reload_vhost_realtime(string vhost)
srs_trace("realtime changed %d=>%d", realtime, realtime_enabled);
realtime = realtime_enabled;
}
mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime);
mw_sleep = _srs_config->get_mw_sleep(req->vhost);
set_socket_buffer(mw_sleep);
return err;
}
@ -689,18 +697,19 @@ srs_error_t SrsRtmpConn::do_playing(SrsSource* source, SrsConsumer* consumer, Sr
SrsMessageArray msgs(SRS_PERF_MW_MSGS);
bool user_specified_duration_to_stop = (req->duration > 0);
int64_t starttime = -1;
// setup the realtime.
realtime = _srs_config->get_realtime_enabled(req->vhost);
// setup the mw config.
// when mw_sleep changed, resize the socket send buffer.
mw_enabled = true;
change_mw_sleep(_srs_config->get_mw_sleep(req->vhost));
mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime);
mw_sleep = _srs_config->get_mw_sleep(req->vhost);
set_socket_buffer(mw_sleep);
// initialize the send_min_interval
send_min_interval = _srs_config->get_send_min_interval(req->vhost);
srs_trace("start play smi=%dms, mw_sleep=%d, mw_enabled=%d, realtime=%d, tcp_nodelay=%d",
srsu2msi(send_min_interval), srsu2msi(mw_sleep), mw_enabled, realtime, tcp_nodelay);
srs_trace("start play smi=%dms, mw_sleep=%d, mw_msgs=%d, realtime=%d, tcp_nodelay=%d",
srsu2msi(send_min_interval), srsu2msi(mw_sleep), mw_msgs, realtime, tcp_nodelay);
while (true) {
// when source is set to expired, disconnect it.
@ -730,13 +739,7 @@ srs_error_t SrsRtmpConn::do_playing(SrsSource* source, SrsConsumer* consumer, Sr
// wait for message to incoming.
// @see https://github.com/ossrs/srs/issues/251
// @see https://github.com/ossrs/srs/issues/257
if (realtime) {
// for realtime, min required msgs is 0, send when got one+ msgs.
consumer->wait(SRS_PERF_MW_MIN_MSGS_REALTIME, mw_sleep);
} else {
// for no-realtime, got some msgs then send.
consumer->wait(SRS_PERF_MW_MIN_MSGS, mw_sleep);
}
consumer->wait(mw_msgs, mw_sleep);
#endif
// get messages from consumer.
@ -750,9 +753,9 @@ srs_error_t SrsRtmpConn::do_playing(SrsSource* source, SrsConsumer* consumer, Sr
// reportable
if (pprint->can_print()) {
kbps->sample();
srs_trace("-> " SRS_CONSTS_LOG_PLAY " time=%d, msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d, mw=%d",
srs_trace("-> " SRS_CONSTS_LOG_PLAY " time=%d, msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d, mw=%d/%d",
(int)pprint->age(), count, kbps->get_send_kbps(), kbps->get_send_kbps_30s(), kbps->get_send_kbps_5m(),
kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m(), srsu2msi(mw_sleep));
kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m(), srsu2msi(mw_sleep), mw_msgs);
}
if (count <= 0) {
@ -1114,16 +1117,6 @@ srs_error_t SrsRtmpConn::process_play_control_msg(SrsConsumer* consumer, SrsComm
return err;
}
void SrsRtmpConn::change_mw_sleep(srs_utime_t sleep_v)
{
if (!mw_enabled) {
return;
}
set_socket_buffer(sleep_v);
mw_sleep = sleep_v;
}
void SrsRtmpConn::set_sock_options()
{
SrsRequest* req = info->req;

View file

@ -102,8 +102,7 @@ private:
srs_utime_t duration;
// The MR(merged-write) sleep time in srs_utime_t.
srs_utime_t mw_sleep;
// The MR(merged-write) only enabled for play.
int mw_enabled;
int mw_msgs;
// For realtime
// @see https://github.com/ossrs/srs/issues/257
bool realtime;
@ -149,7 +148,6 @@ private:
virtual srs_error_t handle_publish_message(SrsSource* source, SrsCommonMessage* msg);
virtual srs_error_t process_publish_message(SrsSource* source, SrsCommonMessage* msg);
virtual srs_error_t process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg);
virtual void change_mw_sleep(srs_utime_t sleep_v);
virtual void set_sock_options();
private:
virtual srs_error_t check_edge_token_traverse_auth();

View file

@ -269,9 +269,17 @@ void SrsMessageQueue::set_queue_size(srs_utime_t queue_size)
max_queue_size = queue_size;
}
srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow)
srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow, bool pass_timestamp)
{
srs_error_t err = srs_success;
msgs.push_back(msg);
// For RTC, we never care about the timestamp and duration, so we never shrink queue here,
// but we will drop messages in each consumer coroutine.
if (pass_timestamp) {
return err;
}
if (msg->is_av()) {
if (av_start_time == -1) {
@ -281,8 +289,6 @@ srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow
av_end_time = srs_utime_t(msg->timestamp * SRS_UTIME_MILLISECONDS);
}
msgs.push_back(msg);
while (av_end_time - av_start_time > max_queue_size) {
// notice the caller queue already overflow and shrinked.
if (is_overflow) {
@ -295,7 +301,7 @@ srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow
return err;
}
srs_error_t SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count)
srs_error_t SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count, bool pass_timestamp)
{
srs_error_t err = srs_success;
@ -308,13 +314,15 @@ srs_error_t SrsMessageQueue::dump_packets(int max_count, SrsSharedPtrMessage** p
count = srs_min(max_count, nb_msgs);
SrsSharedPtrMessage** omsgs = msgs.data();
for (int i = 0; i < count; i++) {
pmsgs[i] = omsgs[i];
memcpy(pmsgs, omsgs, count * sizeof(SrsSharedPtrMessage*));
// For RTC, we enable pass_timestamp mode, which never care about the timestamp and duration,
// so we do not have to update the start time here.
if (!pass_timestamp) {
SrsSharedPtrMessage* last = omsgs[count - 1];
av_start_time = srs_utime_t(last->timestamp * SRS_UTIME_MILLISECONDS);
}
SrsSharedPtrMessage* last = omsgs[count - 1];
av_start_time = srs_utime_t(last->timestamp * SRS_UTIME_MILLISECONDS);
if (count >= nb_msgs) {
// the pmsgs is big enough and clear msgs at most time.
msgs.clear();
@ -433,6 +441,8 @@ SrsConsumer::SrsConsumer(SrsSource* s, SrsConnection* c)
mw_duration = 0;
mw_waiting = false;
#endif
pass_timestamp = false;
}
SrsConsumer::~SrsConsumer()
@ -466,20 +476,35 @@ srs_error_t SrsConsumer::enqueue(SrsSharedPtrMessage* shared_msg, bool atc, SrsR
srs_error_t err = srs_success;
SrsSharedPtrMessage* msg = shared_msg->copy();
if (!atc) {
// For RTC, we enable pass_timestamp mode, which never correct or depends on monotonic increasing of
// timestamp. And in RTC, the audio and video timebase can be different, so we ignore time_jitter here.
if (!pass_timestamp && !atc) {
if ((err = jitter->correct(msg, ag)) != srs_success) {
return srs_error_wrap(err, "consume message");
}
}
if ((err = queue->enqueue(msg, NULL)) != srs_success) {
// Put message in queue, here we may enable pass_timestamp mode.
if ((err = queue->enqueue(msg, NULL, pass_timestamp)) != srs_success) {
return srs_error_wrap(err, "enqueue message");
}
#ifdef SRS_PERF_QUEUE_COND_WAIT
// fire the mw when msgs is enough.
if (mw_waiting) {
// For RTC, we use pass_timestamp mode, we don't care about the timestamp in queue,
// so we only check the messages in queue.
if (pass_timestamp) {
if (queue->size() > mw_min_msgs) {
srs_cond_signal(mw_wait);
mw_waiting = false;
return err;
}
return err;
}
// For RTMP, we wait for messages and duration.
srs_utime_t duration = queue->duration();
bool match_min_msgs = queue->size() > mw_min_msgs;
@ -529,7 +554,7 @@ srs_error_t SrsConsumer::dump_packets(SrsMessageArray* msgs, int& count)
}
// pump msgs from queue.
if ((err = queue->dump_packets(max, msgs->msgs, count)) != srs_success) {
if ((err = queue->dump_packets(max, msgs->msgs, count, pass_timestamp)) != srs_success) {
return srs_error_wrap(err, "dump packets");
}

View file

@ -151,12 +151,13 @@ public:
// Enqueue the message, the timestamp always monotonically.
// @param msg, the msg to enqueue, user never free it whatever the return code.
// @param is_overflow, whether overflow and shrinked. NULL to ignore.
virtual srs_error_t enqueue(SrsSharedPtrMessage* msg, bool* is_overflow = NULL);
// @remark If pass_timestamp, we never shrink and never care about the timestamp or duration.
virtual srs_error_t enqueue(SrsSharedPtrMessage* msg, bool* is_overflow = NULL, bool pass_timestamp = false);
// Get packets in consumer queue.
// @pmsgs SrsSharedPtrMessage*[], used to store the msgs, user must alloc it.
// @count the count in array, output param.
// @max_count the max count to dequeue, must be positive.
virtual srs_error_t dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count);
virtual srs_error_t dump_packets(int max_count, SrsSharedPtrMessage** pmsgs, int& count, bool pass_timestamp = false);
// Dumps packets to consumer, use specified args.
// @remark the atc/tba/tbv/ag are same to SrsConsumer.enqueue().
virtual srs_error_t dump_packets(SrsConsumer* consumer, bool atc, SrsRtmpJitterAlgorithm ag);
@ -203,10 +204,17 @@ private:
int mw_min_msgs;
srs_utime_t mw_duration;
#endif
private:
// For RTC, we never use jitter to correct timestamp.
// But we should not change the atc or time_jitter for source or RTMP.
// @remark In this mode, we also never check the queue by timstamp, but only by count.
bool pass_timestamp;
public:
SrsConsumer(SrsSource* s, SrsConnection* c);
virtual ~SrsConsumer();
public:
// Use pass timestamp mode.
void enable_pass_timestamp() { pass_timestamp = true; }
// Set the size of queue.
virtual void set_queue_size(srs_utime_t queue_size);
// when source id changed, notice client to print.

View file

@ -271,6 +271,8 @@ SrsStatistic::SrsStatistic()
perf_gso = new SrsStatisticCategory();
perf_rtp = new SrsStatisticCategory();
perf_rtc = new SrsStatisticCategory();
perf_bytes = new SrsStatisticCategory();
perf_dropped = new SrsStatisticCategory();
}
SrsStatistic::~SrsStatistic()
@ -311,6 +313,8 @@ SrsStatistic::~SrsStatistic()
srs_freep(perf_gso);
srs_freep(perf_rtp);
srs_freep(perf_rtc);
srs_freep(perf_bytes);
srs_freep(perf_dropped);
}
SrsStatistic* SrsStatistic::instance()
@ -641,7 +645,7 @@ srs_error_t SrsStatistic::dumps_perf_writev_iovs(SrsJsonObject* obj)
return dumps_perf(perf_iovs, obj);
}
void SrsStatistic::perf_sendmmsg_on_packets(int nb_packets)
void SrsStatistic::perf_on_sendmmsg_packets(int nb_packets)
{
perf_on_packets(perf_sendmmsg, nb_packets);
}
@ -651,6 +655,73 @@ srs_error_t SrsStatistic::dumps_perf_sendmmsg(SrsJsonObject* obj)
return dumps_perf(perf_sendmmsg, obj);
}
void SrsStatistic::perf_on_rtc_bytes(int nn_bytes, int nn_rtp_bytes, int nn_padding)
{
// a: AVFrame bytes.
// b: RTC bytes.
// c: RTC paddings.
perf_bytes->a += nn_bytes;
perf_bytes->b += nn_rtp_bytes;
perf_bytes->c += nn_padding;
perf_bytes->nn += nn_rtp_bytes;
}
srs_error_t SrsStatistic::dumps_perf_bytes(SrsJsonObject* obj)
{
obj->set("avframe_bytes", SrsJsonAny::integer(perf_bytes->a));
obj->set("rtc_bytes", SrsJsonAny::integer(perf_bytes->b));
obj->set("rtc_padding", SrsJsonAny::integer(perf_bytes->c));
obj->set("nn", SrsJsonAny::integer(perf_bytes->nn));
return srs_success;
}
void SrsStatistic::perf_on_dropped(int nn_msgs, int nn_rtc, int nn_dropped)
{
// a: System AVFrames.
// b: RTC frames.
// c: Dropped frames.
perf_dropped->a += nn_msgs;
perf_dropped->b += nn_rtc;
perf_dropped->c += nn_dropped;
perf_dropped->nn += nn_dropped;
}
srs_error_t SrsStatistic::dumps_perf_dropped(SrsJsonObject* obj)
{
obj->set("avframes", SrsJsonAny::integer(perf_dropped->a));
obj->set("rtc_frames", SrsJsonAny::integer(perf_dropped->b));
obj->set("rtc_dropeed", SrsJsonAny::integer(perf_dropped->c));
obj->set("nn", SrsJsonAny::integer(perf_dropped->nn));
return srs_success;
}
void SrsStatistic::reset_perf()
{
srs_freep(perf_iovs);
srs_freep(perf_msgs);
srs_freep(perf_sendmmsg);
srs_freep(perf_gso);
srs_freep(perf_rtp);
srs_freep(perf_rtc);
srs_freep(perf_bytes);
srs_freep(perf_dropped);
perf_iovs = new SrsStatisticCategory();
perf_msgs = new SrsStatisticCategory();
perf_sendmmsg = new SrsStatisticCategory();
perf_gso = new SrsStatisticCategory();
perf_rtp = new SrsStatisticCategory();
perf_rtc = new SrsStatisticCategory();
perf_bytes = new SrsStatisticCategory();
perf_dropped = new SrsStatisticCategory();
}
void SrsStatistic::perf_on_packets(SrsStatisticCategory* p, int nb_msgs)
{
// The range for stat:

View file

@ -174,6 +174,8 @@ private:
SrsStatisticCategory* perf_gso;
SrsStatisticCategory* perf_rtp;
SrsStatisticCategory* perf_rtc;
SrsStatisticCategory* perf_bytes;
SrsStatisticCategory* perf_dropped;
private:
SrsStatistic();
virtual ~SrsStatistic();
@ -245,13 +247,11 @@ public:
// Stat for packets merged written, nb_packets is the number of RTP packets.
// For example, a RTC/opus packet maybe package to three RTP packets.
virtual void perf_on_rtp_packets(int nb_packets);
// Dumps the perf statistic data for RTP packets, for performance analysis.
virtual srs_error_t dumps_perf_rtp_packets(SrsJsonObject* obj);
public:
// Stat for packets UDP GSO, nb_packets is the merged RTP packets.
// For example, three RTP/audio packets maybe GSO to one msghdr.
virtual void perf_on_gso_packets(int nb_packets);
// Dumps the perf statistic data for UDP GSO, for performance analysis.
virtual srs_error_t dumps_perf_gso(SrsJsonObject* obj);
public:
// Stat for TCP writev, nb_iovs is the total number of iovec.
@ -259,9 +259,19 @@ public:
virtual srs_error_t dumps_perf_writev_iovs(SrsJsonObject* obj);
public:
// Stat for packets UDP sendmmsg, nb_packets is the vlen for sendmmsg.
virtual void perf_sendmmsg_on_packets(int nb_packets);
// Dumps the perf statistic data for UDP sendmmsg, for performance analysis.
virtual void perf_on_sendmmsg_packets(int nb_packets);
virtual srs_error_t dumps_perf_sendmmsg(SrsJsonObject* obj);
public:
// Stat for bytes, nn_bytes is the size of bytes, nb_padding is padding bytes.
virtual void perf_on_rtc_bytes(int nn_bytes, int nn_rtp_bytes, int nn_padding);
virtual srs_error_t dumps_perf_bytes(SrsJsonObject* obj);
public:
// Stat for rtc messages, nn_rtc is rtc messages, nn_dropped is dropped messages.
virtual void perf_on_dropped(int nn_msgs, int nn_rtc, int nn_dropped);
virtual srs_error_t dumps_perf_dropped(SrsJsonObject* obj);
public:
// Reset all perf stat data.
virtual void reset_perf();
private:
virtual void perf_on_packets(SrsStatisticCategory* p, int nb_msgs);
virtual srs_error_t dumps_perf(SrsStatisticCategory* p, SrsJsonObject* obj);

View file

@ -129,10 +129,10 @@
#ifdef SRS_PERF_QUEUE_COND_WAIT
// For RTMP, use larger wait queue.
#define SRS_PERF_MW_MIN_MSGS 8
#define SRS_PERF_MW_MIN_MSGS_REALTIME 0
// For RTC, use smaller wait queue.
#define SRS_PERF_MW_MIN_MSGS_FOR_RTC 2
#define SRS_PERF_MW_MIN_MSGS_FOR_RTC_REALTIME 0
#define SRS_PERF_MW_MIN_MSGS_FOR_RTC 1
// For Real-Time, never wait messages.
#define SRS_PERF_MW_MIN_MSGS_REALTIME 0
#endif
/**
* the default value of vhost for
@ -192,5 +192,27 @@
#define SRS_PERF_GLIBC_MEMORY_CHECK
#undef SRS_PERF_GLIBC_MEMORY_CHECK
// For RTC, how many iovs we alloc for each mmsghdr for GSO.
// Assume that there are 3300 clients, say, 10000 msgs in queue to send, the memory is:
// 2 # We have two queue, cache and hotspot.
// * 4 # We have reuseport, each have msg cache queue.
// * (64 + 16*SRS_PERF_RTC_GSO_MAX + SRS_PERF_RTC_GSO_IOVS * 1500) # Each message size.
// * 10000 # Total messages.
// = 197MB # For SRS_PERF_RTC_GSO_IOVS = 1
// = 311MB # For SRS_PERF_RTC_GSO_IOVS = 2
// = 540MB # For SRS_PERF_RTC_GSO_IOVS = 4
// = 998MB # For SRS_PERF_RTC_GSO_IOVS = 8
#if defined(__linux__)
#define SRS_PERF_RTC_GSO_IOVS 4
#else
#define SRS_PERF_RTC_GSO_IOVS 1
#endif
// For RTC, the max iovs in msghdr, the max packets sent in a msghdr.
#define SRS_PERF_RTC_GSO_MAX 64
// For RTC, the max count of RTP packets we process in one loop.
#define SRS_PERF_RTC_RTP_PACKETS 1024
#endif

View file

@ -67,11 +67,6 @@ SrsBuffer::~SrsBuffer()
{
}
char* SrsBuffer::data()
{
return bytes;
}
int SrsBuffer::size()
{
return nb_bytes;

View file

@ -113,7 +113,8 @@ public:
* get data of stream, set by initialize.
* current bytes = data() + pos()
*/
virtual char* data();
inline char* data() { return bytes; }
inline char* head() { return p; }
/**
* the total stream size, set by initialize.
* left bytes = size() - pos().

View file

@ -422,7 +422,7 @@ public:
};
// Error helpers, should use these functions to new or wrap an error.
#define srs_success SrsCplxError::success()
#define srs_success 0 // SrsCplxError::success()
#define srs_error_new(ret, fmt, ...) SrsCplxError::create(__FUNCTION__, __FILE__, __LINE__, ret, fmt, ##__VA_ARGS__)
#define srs_error_wrap(err, fmt, ...) SrsCplxError::wrap(__FUNCTION__, __FILE__, __LINE__, err, fmt, ##__VA_ARGS__)
#define srs_error_copy(err) SrsCplxError::copy(err)

View file

@ -218,6 +218,7 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::SrsSharedPtrPayload()
extra_payloads = NULL;
nn_extra_payloads = 0;
nn_max_extra_payloads = 0;
#endif
}
@ -227,9 +228,10 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload()
srs_memory_unwatch(payload);
#endif
srs_freepa(payload);
srs_freepa(samples);
#ifdef SRS_AUTO_RTC
srs_freepa(samples);
for (int i = 0; i < nn_extra_payloads; i++) {
SrsSample* p = extra_payloads + i;
srs_freep(p->bytes);

View file

@ -310,10 +310,14 @@ private:
int nn_samples;
// For RTC video, whether NALUs has IDR.
bool has_idr;
public:
// For RTC audio, we may need to transcode AAC to opus,
// so there must be an extra payloads, which is transformed from payload.
SrsSample* extra_payloads;
int nn_extra_payloads;
// The max size payload in extras.
// @remark For GSO to fast guess the best padding.
int nn_max_extra_payloads;
#endif
public:
SrsSharedPtrPayload();
@ -363,6 +367,9 @@ public:
void set_extra_payloads(SrsSample* payloads, int nn_payloads);
int nn_extra_payloads() { return ptr->nn_extra_payloads; }
SrsSample* extra_payloads() { return ptr->extra_payloads; }
// The max extra payload size.
void set_max_extra_payload(int v) { ptr->nn_max_extra_payloads = v; }
int nn_max_extra_payloads() { return ptr->nn_max_extra_payloads; }
// Whether samples has idr.
bool has_idr() { return ptr->has_idr; }
void set_has_idr(bool v) { ptr->has_idr = v; }

View file

@ -55,27 +55,15 @@ SrsRtpHeader::SrsRtpHeader()
extension_length = 0;
}
SrsRtpHeader::SrsRtpHeader(const SrsRtpHeader& rhs)
void SrsRtpHeader::reset()
{
operator=(rhs);
}
SrsRtpHeader& SrsRtpHeader::operator=(const SrsRtpHeader& rhs)
{
padding = rhs.padding;
extension = rhs.extension;
cc = rhs.cc;
marker = rhs.marker;
payload_type = rhs.payload_type;
sequence = rhs.sequence;
timestamp = rhs.timestamp;
ssrc = rhs.ssrc;
for (size_t i = 0; i < cc; ++i) {
csrc[i] = rhs.csrc[i];
}
extension_length = rhs.extension_length;
return *this;
// We only reset the optional fields, the required field such as ssrc
// will always be set by user.
padding = false;
extension = false;
cc = 0;
marker = false;
extension_length = 0;
}
SrsRtpHeader::~SrsRtpHeader()
@ -95,32 +83,63 @@ srs_error_t SrsRtpHeader::encode(SrsBuffer* stream)
{
srs_error_t err = srs_success;
// Encode the RTP fix header, 12bytes.
// @see https://tools.ietf.org/html/rfc1889#section-5.1
char* op = stream->head();
char* p = op;
// The version, padding, extension and cc, total 1 byte.
uint8_t v = 0x80 | cc;
if (padding) {
v |= 0x40;
v |= 0x20;
}
if (extension) {
v |= 0x10;
}
stream->write_1bytes(v);
*p++ = v;
// The marker and payload type, total 1 byte.
v = payload_type;
if (marker) {
v |= kRtpMarker;
}
stream->write_1bytes(v);
*p++ = v;
stream->write_2bytes(sequence);
stream->write_4bytes(timestamp);
stream->write_4bytes(ssrc);
// The sequence number, 2 bytes.
char* pp = (char*)&sequence;
*p++ = pp[1];
*p++ = pp[0];
// The timestamp, 4 bytes.
pp = (char*)&timestamp;
*p++ = pp[3];
*p++ = pp[2];
*p++ = pp[1];
*p++ = pp[0];
// The SSRC, 4 bytes.
pp = (char*)&ssrc;
*p++ = pp[3];
*p++ = pp[2];
*p++ = pp[1];
*p++ = pp[0];
// The CSRC list: 0 to 15 items, each is 4 bytes.
for (size_t i = 0; i < cc; ++i) {
stream->write_4bytes(csrc[i]);
pp = (char*)&csrc[i];
*p++ = pp[3];
*p++ = pp[2];
*p++ = pp[1];
*p++ = pp[0];
}
// TODO: Write exteinsion field.
if (extension) {
}
// Consume the data.
stream->skip(p - op);
return err;
}
@ -129,51 +148,77 @@ size_t SrsRtpHeader::header_size()
return kRtpHeaderFixedSize + cc * 4 + (extension ? (extension_length + 1) * 4 : 0);
}
void SrsRtpHeader::set_marker(bool marker)
{
this->marker = marker;
}
void SrsRtpHeader::set_payload_type(uint8_t payload_type)
{
this->payload_type = payload_type;
}
void SrsRtpHeader::set_sequence(uint16_t sequence)
{
this->sequence = sequence;
}
void SrsRtpHeader::set_timestamp(int64_t timestamp)
{
this->timestamp = timestamp;
}
void SrsRtpHeader::set_ssrc(uint32_t ssrc)
{
this->ssrc = ssrc;
}
SrsRtpPacket2::SrsRtpPacket2()
{
payload = NULL;
padding = 0;
cache_raw = new SrsRtpRawPayload();
cache_fua = new SrsRtpFUAPayload2();
cache_payload = 0;
}
SrsRtpPacket2::~SrsRtpPacket2()
{
// We may use the cache as payload.
if (payload == cache_raw || payload == cache_fua) {
payload = NULL;
}
srs_freep(payload);
srs_freep(cache_raw);
srs_freep(cache_fua);
}
void SrsRtpPacket2::set_padding(int size)
{
rtp_header.set_padding(size > 0);
if (cache_payload) {
cache_payload += size - padding;
}
padding = size;
}
void SrsRtpPacket2::add_padding(int size)
{
rtp_header.set_padding(padding + size > 0);
if (cache_payload) {
cache_payload += size;
}
padding += size;
}
void SrsRtpPacket2::reset()
{
rtp_header.reset();
padding = 0;
cache_payload = 0;
// We may use the cache as payload.
if (payload == cache_raw || payload == cache_fua) {
payload = NULL;
}
srs_freep(payload);
}
SrsRtpRawPayload* SrsRtpPacket2::reuse_raw()
{
payload = cache_raw;
return cache_raw;
}
SrsRtpFUAPayload2* SrsRtpPacket2::reuse_fua()
{
payload = cache_fua;
return cache_fua;
}
int SrsRtpPacket2::nb_bytes()
{
return rtp_header.header_size() + (payload? payload->nb_bytes():0) + padding;
if (!cache_payload) {
cache_payload = rtp_header.header_size() + (payload? payload->nb_bytes():0) + padding;
}
return cache_payload;
}
srs_error_t SrsRtpPacket2::encode(SrsBuffer* buf)
@ -188,11 +233,11 @@ srs_error_t SrsRtpPacket2::encode(SrsBuffer* buf)
return srs_error_wrap(err, "encode payload");
}
if (padding) {
if (padding > 0) {
if (!buf->require(padding)) {
return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", padding);
}
memset(buf->data(), padding, padding);
memset(buf->data() + buf->pos(), padding, padding);
buf->skip(padding);
}
@ -237,12 +282,13 @@ SrsRtpRawNALUs::SrsRtpRawNALUs()
SrsRtpRawNALUs::~SrsRtpRawNALUs()
{
vector<SrsSample*>::iterator it;
for (it = nalus.begin(); it != nalus.end(); ++it) {
SrsSample* p = *it;
srs_freep(p);
if (true) {
int nn_nalus = (int)nalus.size();
for (int i = 0; i < nn_nalus; i++) {
SrsSample* p = nalus[i];
srs_freep(p);
}
}
nalus.clear();
}
void SrsRtpRawNALUs::push_back(SrsSample* sample)
@ -270,19 +316,19 @@ uint8_t SrsRtpRawNALUs::skip_first_byte()
return uint8_t(nalus[0]->bytes[0]);
}
srs_error_t SrsRtpRawNALUs::read_samples(vector<SrsSample*>& samples, int size)
srs_error_t SrsRtpRawNALUs::read_samples(vector<SrsSample*>& samples, int packet_size)
{
if (cursor + size < 0 || cursor + size > nn_bytes) {
return srs_error_new(ERROR_RTC_RTP_MUXER, "cursor=%d, max=%d, size=%d", cursor, nn_bytes, size);
if (cursor + packet_size < 0 || cursor + packet_size > nn_bytes) {
return srs_error_new(ERROR_RTC_RTP_MUXER, "cursor=%d, max=%d, size=%d", cursor, nn_bytes, packet_size);
}
int pos = cursor;
cursor += size;
int left = size;
cursor += packet_size;
int left = packet_size;
vector<SrsSample*>::iterator it;
for (it = nalus.begin(); it != nalus.end() && left > 0; ++it) {
SrsSample* p = *it;
int nn_nalus = (int)nalus.size();
for (int i = 0; left > 0 && i < nn_nalus; i++) {
SrsSample* p = nalus[i];
// Ignore previous consumed samples.
if (pos && pos - p->size >= 0) {
@ -295,9 +341,10 @@ srs_error_t SrsRtpRawNALUs::read_samples(vector<SrsSample*>& samples, int size)
srs_assert(nn > 0);
SrsSample* sample = new SrsSample();
samples.push_back(sample);
sample->bytes = p->bytes + pos;
sample->size = nn;
samples.push_back(sample);
left -= nn;
pos = 0;
@ -310,9 +357,9 @@ int SrsRtpRawNALUs::nb_bytes()
{
int size = 0;
vector<SrsSample*>::iterator it;
for (it = nalus.begin(); it != nalus.end(); ++it) {
SrsSample* p = *it;
int nn_nalus = (int)nalus.size();
for (int i = 0; i < nn_nalus; i++) {
SrsSample* p = nalus[i];
size += p->size;
}
@ -321,9 +368,9 @@ int SrsRtpRawNALUs::nb_bytes()
srs_error_t SrsRtpRawNALUs::encode(SrsBuffer* buf)
{
vector<SrsSample*>::iterator it;
for (it = nalus.begin(); it != nalus.end(); ++it) {
SrsSample* p = *it;
int nn_nalus = (int)nalus.size();
for (int i = 0; i < nn_nalus; i++) {
SrsSample* p = nalus[i];
if (!buf->require(p->size)) {
return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", p->size);
@ -342,21 +389,20 @@ SrsRtpSTAPPayload::SrsRtpSTAPPayload()
SrsRtpSTAPPayload::~SrsRtpSTAPPayload()
{
vector<SrsSample*>::iterator it;
for (it = nalus.begin(); it != nalus.end(); ++it) {
SrsSample* p = *it;
int nn_nalus = (int)nalus.size();
for (int i = 0; i < nn_nalus; i++) {
SrsSample* p = nalus[i];
srs_freep(p);
}
nalus.clear();
}
int SrsRtpSTAPPayload::nb_bytes()
{
int size = 1;
vector<SrsSample*>::iterator it;
for (it = nalus.begin(); it != nalus.end(); ++it) {
SrsSample* p = *it;
int nn_nalus = (int)nalus.size();
for (int i = 0; i < nn_nalus; i++) {
SrsSample* p = nalus[i];
size += 2 + p->size;
}
@ -376,9 +422,10 @@ srs_error_t SrsRtpSTAPPayload::encode(SrsBuffer* buf)
buf->write_1bytes(v);
// NALUs.
vector<SrsSample*>::iterator it;
for (it = nalus.begin(); it != nalus.end(); ++it) {
SrsSample* p = *it;
int nn_nalus = (int)nalus.size();
for (int i = 0; i < nn_nalus; i++) {
SrsSample* p = nalus[i];
if (!buf->require(2 + p->size)) {
return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", 2 + p->size);
}
@ -398,21 +445,20 @@ SrsRtpFUAPayload::SrsRtpFUAPayload()
SrsRtpFUAPayload::~SrsRtpFUAPayload()
{
vector<SrsSample*>::iterator it;
for (it = nalus.begin(); it != nalus.end(); ++it) {
SrsSample* p = *it;
int nn_nalus = (int)nalus.size();
for (int i = 0; i < nn_nalus; i++) {
SrsSample* p = nalus[i];
srs_freep(p);
}
nalus.clear();
}
int SrsRtpFUAPayload::nb_bytes()
{
int size = 2;
vector<SrsSample*>::iterator it;
for (it = nalus.begin(); it != nalus.end(); ++it) {
SrsSample* p = *it;
int nn_nalus = (int)nalus.size();
for (int i = 0; i < nn_nalus; i++) {
SrsSample* p = nalus[i];
size += p->size;
}
@ -441,9 +487,10 @@ srs_error_t SrsRtpFUAPayload::encode(SrsBuffer* buf)
buf->write_1bytes(fu_header);
// FU payload, @see https://tools.ietf.org/html/rfc6184#section-5.8
vector<SrsSample*>::iterator it;
for (it = nalus.begin(); it != nalus.end(); ++it) {
SrsSample* p = *it;
int nn_nalus = (int)nalus.size();
for (int i = 0; i < nn_nalus; i++) {
SrsSample* p = nalus[i];
if (!buf->require(p->size)) {
return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", p->size);
}
@ -454,6 +501,57 @@ srs_error_t SrsRtpFUAPayload::encode(SrsBuffer* buf)
return srs_success;
}
SrsRtpFUAPayload2::SrsRtpFUAPayload2()
{
start = end = false;
nri = nalu_type = (SrsAvcNaluType)0;
payload = NULL;
size = 0;
}
SrsRtpFUAPayload2::~SrsRtpFUAPayload2()
{
}
int SrsRtpFUAPayload2::nb_bytes()
{
return 2 + size;
}
srs_error_t SrsRtpFUAPayload2::encode(SrsBuffer* buf)
{
if (!buf->require(2 + size)) {
return srs_error_new(ERROR_RTC_RTP_MUXER, "requires %d bytes", 1);
}
// Fast encoding.
char* p = buf->head();
// FU indicator, @see https://tools.ietf.org/html/rfc6184#section-5.8
uint8_t fu_indicate = kFuA;
fu_indicate |= (nri & (~kNalTypeMask));
*p++ = fu_indicate;
// FU header, @see https://tools.ietf.org/html/rfc6184#section-5.8
uint8_t fu_header = nalu_type;
if (start) {
fu_header |= kStart;
}
if (end) {
fu_header |= kEnd;
}
*p++ = fu_header;
// FU payload, @see https://tools.ietf.org/html/rfc6184#section-5.8
memcpy(p, payload, size);
// Consume bytes.
buf->skip(2 + size);
return srs_success;
}
SrsRtpSharedPacket::SrsRtpSharedPacketPayload::SrsRtpSharedPacketPayload()
{
payload = NULL;

View file

@ -38,6 +38,8 @@ const uint8_t kRtpMarker = 0x80;
const uint8_t kNalTypeMask = 0x1F;
class SrsBuffer;
class SrsRtpRawPayload;
class SrsRtpFUAPayload2;
class SrsRtpHeader
{
@ -48,7 +50,7 @@ private:
bool marker;
uint8_t payload_type;
uint16_t sequence;
int64_t timestamp;
int32_t timestamp;
uint32_t ssrc;
uint32_t csrc[15];
uint16_t extension_length;
@ -56,25 +58,24 @@ private:
public:
SrsRtpHeader();
virtual ~SrsRtpHeader();
SrsRtpHeader(const SrsRtpHeader& rhs);
SrsRtpHeader& operator=(const SrsRtpHeader& rhs);
void reset();
public:
srs_error_t decode(SrsBuffer* stream);
srs_error_t encode(SrsBuffer* stream);
public:
size_t header_size();
public:
void set_marker(bool marker);
inline void set_marker(bool v) { marker = v; }
bool get_marker() const { return marker; }
void set_payload_type(uint8_t payload_type);
inline void set_payload_type(uint8_t v) { payload_type = v; }
uint8_t get_payload_type() const { return payload_type; }
void set_sequence(uint16_t sequence);
inline void set_sequence(uint16_t v) { sequence = v; }
uint16_t get_sequence() const { return sequence; }
void set_timestamp(int64_t timestamp);
inline void set_timestamp(int64_t v) { timestamp = (uint32_t)v; }
int64_t get_timestamp() const { return timestamp; }
void set_ssrc(uint32_t ssrc);
inline void set_ssrc(uint32_t v) { ssrc = v; }
uint32_t get_ssrc() const { return ssrc; }
void set_padding(bool v) { padding = v; }
inline void set_padding(bool v) { padding = v; }
};
class SrsRtpPacket2
@ -83,12 +84,24 @@ public:
SrsRtpHeader rtp_header;
ISrsEncoder* payload;
int padding;
private:
SrsRtpRawPayload* cache_raw;
SrsRtpFUAPayload2* cache_fua;
int cache_payload;
public:
SrsRtpPacket2();
virtual ~SrsRtpPacket2();
public:
// Append size of bytes as padding.
virtual void set_padding(int size);
// Set the padding of RTP packet.
void set_padding(int size);
// Increase the padding of RTP packet.
void add_padding(int size);
// Reset RTP packet.
void reset();
// Reuse the cached raw message as payload.
SrsRtpRawPayload* reuse_raw();
// Reuse the cached fua message as payload.
SrsRtpFUAPayload2* reuse_fua();
// interface ISrsEncoder
public:
virtual int nb_bytes();
@ -99,7 +112,8 @@ public:
class SrsRtpRawPayload : public ISrsEncoder
{
public:
// @remark We only refer to the memory, user must free it.
// The RAW payload, directly point to the shared memory.
// @remark We only refer to the memory, user must free its bytes.
char* payload;
int nn_payload;
public:
@ -115,6 +129,7 @@ public:
class SrsRtpRawNALUs : public ISrsEncoder
{
private:
// We will manage the samples, but the sample itself point to the shared memory.
std::vector<SrsSample*> nalus;
int nn_bytes;
int cursor;
@ -125,7 +140,8 @@ public:
void push_back(SrsSample* sample);
public:
uint8_t skip_first_byte();
srs_error_t read_samples(std::vector<SrsSample*>& samples, int size);
// We will manage the returned samples, if user want to manage it, please copy it.
srs_error_t read_samples(std::vector<SrsSample*>& samples, int packet_size);
// interface ISrsEncoder
public:
virtual int nb_bytes();
@ -138,7 +154,7 @@ class SrsRtpSTAPPayload : public ISrsEncoder
public:
// The NRI in NALU type.
SrsAvcNaluType nri;
// The NALU samples.
// The NALU samples, we will manage the samples.
// @remark We only refer to the memory, user must free its bytes.
std::vector<SrsSample*> nalus;
public:
@ -151,6 +167,7 @@ public:
};
// FU-A, for one NALU with multiple fragments.
// With more than one payload.
class SrsRtpFUAPayload : public ISrsEncoder
{
public:
@ -160,7 +177,7 @@ public:
bool start;
bool end;
SrsAvcNaluType nalu_type;
// The NALU samples.
// The NALU samples, we manage the samples.
// @remark We only refer to the memory, user must free its bytes.
std::vector<SrsSample*> nalus;
public:
@ -172,6 +189,29 @@ public:
virtual srs_error_t encode(SrsBuffer* buf);
};
// FU-A, for one NALU with multiple fragments.
// With only one payload.
class SrsRtpFUAPayload2 : public ISrsEncoder
{
public:
// The NRI in NALU type.
SrsAvcNaluType nri;
// The FUA header.
bool start;
bool end;
SrsAvcNaluType nalu_type;
// The payload and size,
char* payload;
int size;
public:
SrsRtpFUAPayload2();
virtual ~SrsRtpFUAPayload2();
// interface ISrsEncoder
public:
virtual int nb_bytes();
virtual srs_error_t encode(SrsBuffer* buf);
};
class SrsRtpSharedPacket
{
private:

View file

@ -436,6 +436,8 @@ int srs_sendmmsg(srs_netfd_t stfd, struct mmsghdr *msgvec, unsigned int vlen, in
}
msgvec->msg_len = r0;
#else
msgvec->msg_len = 0;
int tolen = (int)msgvec->msg_hdr.msg_namelen;
const struct sockaddr* to = (const struct sockaddr*)msgvec->msg_hdr.msg_name;
for (int i = 0; i < (int)msgvec->msg_hdr.msg_iovlen; i++) {