mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
Refine SRT code, with StateThread adpater
This commit is contained in:
parent
3ed4aed824
commit
94fa0ff7bd
25 changed files with 4131 additions and 68 deletions
|
@ -432,12 +432,6 @@ function apply_auto_options() {
|
|||
SRS_TOOL_LD=$SRS_TOOL_CC
|
||||
fi
|
||||
|
||||
# The SRT code in SRS requires c++11, although we build libsrt without c++11.
|
||||
# TODO: FIXME: Remove c++11 code in SRT of SRS.
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
SRS_CXX11=YES
|
||||
fi
|
||||
|
||||
# Enable FFmpeg fit for RTC to transcode audio from AAC to OPUS, if user enabled it.
|
||||
if [[ $SRS_RTC == YES && $SRS_FFMPEG_FIT == RESERVED ]]; then
|
||||
SRS_FFMPEG_FIT=YES
|
||||
|
|
|
@ -18,6 +18,11 @@ http_server {
|
|||
|
||||
srt_server {
|
||||
enabled on;
|
||||
tsbpdmode off;
|
||||
tlpktdrop off;
|
||||
latency 0;
|
||||
sendbuf 2000000;
|
||||
recvbuf 2000000;
|
||||
listen 10080;
|
||||
maxbw 1000000000;
|
||||
connect_timeout 4000;
|
||||
|
|
32
trunk/configure
vendored
32
trunk/configure
vendored
|
@ -164,7 +164,6 @@ fi
|
|||
|
||||
# srt code path
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
SrsSRTRoot="${SRS_WORKDIR}/src/srt"
|
||||
LibSRTRoot="${SRS_OBJS_DIR}/srt/include"; LibSRTfile="${SRS_OBJS_DIR}/srt/lib/libsrt.a"
|
||||
if [[ $SRS_SHARED_SRT == YES ]]; then LibSRTfile="-L${SRS_OBJS_DIR}/srt/lib -lsrt"; fi
|
||||
fi
|
||||
|
@ -228,6 +227,10 @@ MODULE_FILES=("srs_protocol_amf0" "srs_protocol_io" "srs_protocol_conn" "srs_pro
|
|||
"srs_protocol_raw_avc" "srs_protocol_rtsp_stack" "srs_protocol_http_stack" "srs_protocol_kbps" "srs_protocol_json"
|
||||
"srs_protocol_format" "srs_protocol_log" "srs_protocol_st" "srs_protocol_http_client"
|
||||
"srs_protocol_http_conn" "srs_protocol_rtmp_conn")
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
MODULE_FILES+=("srs_service_st_srt")
|
||||
ModuleLibIncs+=(${LibSRTRoot})
|
||||
fi
|
||||
if [[ $SRS_RTC == YES ]]; then
|
||||
MODULE_FILES+=("srs_protocol_rtc_stun")
|
||||
ModuleLibIncs+=(${LibSrtpRoot})
|
||||
|
@ -237,16 +240,6 @@ if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
|||
fi
|
||||
PROTOCOL_INCS="src/protocol"; MODULE_DIR=${PROTOCOL_INCS} . auto/modules.sh
|
||||
PROTOCOL_OBJS="${MODULE_OBJS[@]}"
|
||||
#
|
||||
#srt protocol features.
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
MODULE_ID="SRT"
|
||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP")
|
||||
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot} ${LibSRTRoot})
|
||||
MODULE_FILES=("srt_server" "srt_handle" "srt_conn" "srt_to_rtmp" "ts_demux" "srt_data" "srt_log")
|
||||
SRT_INCS=(${LibSRTRoot} ${SrsSRTRoot}); MODULE_DIR=${SrsSRTRoot} . auto/modules.sh
|
||||
SRT_OBJS="${MODULE_OBJS[@]}"
|
||||
fi
|
||||
|
||||
#
|
||||
#App Module, for SRS server only.
|
||||
|
@ -262,6 +255,9 @@ fi
|
|||
if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
||||
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
|
||||
fi
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
ModuleLibIncs+=("${LibSRTRoot[*]}")
|
||||
fi
|
||||
MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source"
|
||||
"srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream"
|
||||
"srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config"
|
||||
|
@ -273,6 +269,9 @@ MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_sourc
|
|||
"srs_app_caster_flv" "srs_app_latest_version" "srs_app_uuid" "srs_app_process" "srs_app_ng_exec"
|
||||
"srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr"
|
||||
"srs_app_coworkers" "srs_app_hybrid" "srs_app_threads")
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
MODULE_FILES+=("srs_app_srt_server" "srs_app_srt_listener" "srs_app_srt_conn" "srs_app_srt_utility" "srs_app_srt_source")
|
||||
fi
|
||||
if [[ $SRS_RTC == YES ]]; then
|
||||
MODULE_FILES+=("srs_app_rtc_conn" "srs_app_rtc_dtls" "srs_app_rtc_sdp"
|
||||
"srs_app_rtc_queue" "srs_app_rtc_server" "srs_app_rtc_source" "srs_app_rtc_api")
|
||||
|
@ -294,9 +293,6 @@ APP_OBJS="${MODULE_OBJS[@]}"
|
|||
#Server Module, for SRS only.
|
||||
MODULE_ID="SERVER"
|
||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP")
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
MODULE_DEPENDS+=("SRT")
|
||||
fi
|
||||
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibGperfRoot} ${LibSSLRoot})
|
||||
if [[ $SRS_RTC == YES ]]; then
|
||||
ModuleLibIncs+=(${LibSrtpRoot})
|
||||
|
@ -306,7 +302,6 @@ if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
|||
fi
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
ModuleLibIncs+=(${LibSRTRoot})
|
||||
ModuleLibIncs+=("${SrsSRTRoot[*]}")
|
||||
fi
|
||||
MODULE_FILES=("srs_main_server")
|
||||
SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . auto/modules.sh
|
||||
|
@ -324,7 +319,6 @@ if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
|||
fi
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
ModuleLibIncs+=(${LibSRTRoot})
|
||||
ModuleLibIncs+=("${SrsSRTRoot[*]}")
|
||||
fi
|
||||
MODULE_FILES=()
|
||||
DEFINES=""
|
||||
|
@ -370,7 +364,6 @@ if [[ $SRS_FFMPEG_FIT == YES ]]; then
|
|||
fi
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
ModuleLibIncs+=(${LibSRTRoot})
|
||||
ModuleLibIncs+=("${SrsSRTRoot[*]}")
|
||||
MODULE_OBJS="${MODULE_OBJS} ${SRT_OBJS[@]}"
|
||||
fi
|
||||
LINK_OPTIONS="${SrsLinkOptions}${SrsGprofLink}${SrsGperfLink}"
|
||||
|
@ -416,7 +409,7 @@ if [ $SRS_UTEST = YES ]; then
|
|||
ModuleLibIncs+=("${LibFfmpegRoot[*]}")
|
||||
fi
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
ModuleLibIncs+=("${SrsSRTRoot[*]}")
|
||||
ModuleLibIncs+=("${LibSRTRoot[*]}")
|
||||
fi
|
||||
ModuleLibFiles=(${LibSTfile} ${LibSSLfile})
|
||||
if [[ $SRS_RTC == YES ]]; then
|
||||
|
@ -429,9 +422,6 @@ if [ $SRS_UTEST = YES ]; then
|
|||
ModuleLibFiles+=("${LibSRTfile[*]}")
|
||||
fi
|
||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "APP")
|
||||
if [[ $SRS_SRT == YES ]]; then
|
||||
MODULE_DEPENDS+=("SRT")
|
||||
fi
|
||||
MODULE_OBJS="${CORE_OBJS[@]} ${KERNEL_OBJS[@]} ${PROTOCOL_OBJS[@]} ${APP_OBJS[@]} ${SRT_OBJS[@]}"
|
||||
LINK_OPTIONS="-lpthread ${SrsLinkOptions}" MODULE_DIR="src/utest" APP_NAME="srs_utest" . auto/utest.sh
|
||||
fi
|
||||
|
|
|
@ -2546,8 +2546,9 @@ srs_error_t SrsConfig::check_normal_config()
|
|||
&& n != "mss" && n != "latency" && n != "recvlatency"
|
||||
&& n != "peerlatency" && n != "tlpkdrop" && n != "connect_timeout"
|
||||
&& n != "sendbuf" && n != "recvbuf" && n != "payloadsize"
|
||||
&& n != "default_app" && n != "mix_correct" && n != "sei_filter") {
|
||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal srt_stream.%s", n.c_str());
|
||||
&& n != "default_app" && n != "mix_correct" && n != "sei_filter"
|
||||
&& n != "tlpktdrop" && n != "tsbpdmode") {
|
||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal srt_server.%s", n.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6798,6 +6799,20 @@ int SrsConfig::get_srto_mss() {
|
|||
return atoi(conf->arg0().c_str());
|
||||
}
|
||||
|
||||
bool SrsConfig::get_srto_tsbpdmode() {
|
||||
static bool DEFAULT = false;
|
||||
SrsConfDirective* conf = root->get("srt_server");
|
||||
if (!conf) {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
conf = conf->get("tsbpdmode");
|
||||
if (!conf || conf->arg0().empty()) {
|
||||
return DEFAULT;
|
||||
}
|
||||
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||
}
|
||||
|
||||
int SrsConfig::get_srto_latency() {
|
||||
static int DEFAULT = 120;
|
||||
SrsConfDirective* conf = root->get("srt_server");
|
||||
|
@ -6854,14 +6869,18 @@ bool SrsConfig::get_srt_sei_filter() {
|
|||
return SRS_CONF_PERFER_TRUE(conf->arg0());
|
||||
}
|
||||
|
||||
bool SrsConfig::get_srto_tlpkdrop() {
|
||||
bool SrsConfig::get_srto_tlpktdrop() {
|
||||
static bool DEFAULT = true;
|
||||
SrsConfDirective* conf = root->get("srt_server");
|
||||
if (!conf) {
|
||||
SrsConfDirective* srt_server_conf = root->get("srt_server");
|
||||
if (!srt_server_conf) {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
conf = conf->get("tlpkdrop");
|
||||
SrsConfDirective* conf = srt_server_conf->get("tlpkdrop");
|
||||
if (! conf) {
|
||||
// make it compatible tlpkdrop and tlpktdrop opt.
|
||||
conf = srt_server_conf->get("tlpktdrop");
|
||||
}
|
||||
if (!conf || conf->arg0().empty()) {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
@ -6882,6 +6901,20 @@ int SrsConfig::get_srto_conntimeout() {
|
|||
return atoi(conf->arg0().c_str());
|
||||
}
|
||||
|
||||
int SrsConfig::get_srto_peeridletimeout() {
|
||||
static int DEFAULT = 10000;
|
||||
SrsConfDirective* conf = root->get("srt_server");
|
||||
if (!conf) {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
conf = conf->get("peer_idle_timeout");
|
||||
if (!conf || conf->arg0().empty()) {
|
||||
return DEFAULT;
|
||||
}
|
||||
return atoi(conf->arg0().c_str());
|
||||
}
|
||||
|
||||
int SrsConfig::get_srto_sendbuf() {
|
||||
static int64_t DEFAULT = 8192 * (1500-28);
|
||||
SrsConfDirective* conf = root->get("srt_server");
|
||||
|
|
|
@ -636,6 +636,8 @@ public:
|
|||
virtual int get_srto_maxbw();
|
||||
// Get the srt SRTO_MSS, Maximum Segment Size, default is 1500.
|
||||
virtual int get_srto_mss();
|
||||
// Get the srt SRTO_TSBPDMODE, timestamp base packet delivery mode, default is false.
|
||||
virtual bool get_srto_tsbpdmode();
|
||||
// Get the srt SRTO_LATENCY, latency, default is 0 which means peer/recv latency is 120ms.
|
||||
virtual int get_srto_latency();
|
||||
// Get the srt SRTO_RCVLATENCY, recv latency, default is 120ms.
|
||||
|
@ -644,10 +646,12 @@ public:
|
|||
virtual int get_srto_peer_latency();
|
||||
// Get the srt h264 sei filter, default is on, it will drop h264 sei packet.
|
||||
virtual bool get_srt_sei_filter();
|
||||
// Get the srt SRTO_TLPKDROP, Too-late Packet Drop, default is true.
|
||||
virtual bool get_srto_tlpkdrop();
|
||||
// Get the srt SRTO_TLPKTDROP, Too-late Packet Drop, default is true.
|
||||
virtual bool get_srto_tlpktdrop();
|
||||
// Get the srt SRTO_CONNTIMEO, connection timeout, default is 3000ms.
|
||||
virtual int get_srto_conntimeout();
|
||||
// Get the srt SRTO_PEERIDLETIMEO, peer idle timeout, default is 10000ms.
|
||||
virtual int get_srto_peeridletimeout();
|
||||
// Get the srt SRTO_SNDBUF, send buffer, default is 8192 × (1500-28).
|
||||
virtual int get_srto_sendbuf();
|
||||
// Get the srt SRTO_RCVBUF, recv buffer, default is 8192 × (1500-28).
|
||||
|
|
|
@ -219,6 +219,13 @@ SrsPithyPrint::SrsPithyPrint(int _stage_id)
|
|||
// for the rtc recv
|
||||
#define SRS_CONSTS_STAGE_RTC_RECV 14
|
||||
|
||||
#ifdef SRS_SRT
|
||||
// the pithy stage for srt play clients.
|
||||
#define SRS_CONSTS_STAGE_SRT_PLAY 15
|
||||
// the pithy stage for srt publish clients.
|
||||
#define SRS_CONSTS_STAGE_SRT_PUBLISH 16
|
||||
#endif
|
||||
|
||||
SrsPithyPrint* SrsPithyPrint::create_rtmp_play()
|
||||
{
|
||||
return new SrsPithyPrint(SRS_CONSTS_STAGE_PLAY_USER);
|
||||
|
@ -289,6 +296,18 @@ SrsPithyPrint* SrsPithyPrint::create_rtc_recv(int fd)
|
|||
return new SrsPithyPrint(fd<<16 | SRS_CONSTS_STAGE_RTC_RECV);
|
||||
}
|
||||
|
||||
#ifdef SRS_SRT
|
||||
SrsPithyPrint* SrsPithyPrint::create_srt_play()
|
||||
{
|
||||
return new SrsPithyPrint(SRS_CONSTS_STAGE_SRT_PLAY);
|
||||
}
|
||||
|
||||
SrsPithyPrint* SrsPithyPrint::create_srt_publish()
|
||||
{
|
||||
return new SrsPithyPrint(SRS_CONSTS_STAGE_SRT_PUBLISH);
|
||||
}
|
||||
#endif
|
||||
|
||||
SrsPithyPrint::~SrsPithyPrint()
|
||||
{
|
||||
leave_stage();
|
||||
|
|
|
@ -130,6 +130,10 @@ public:
|
|||
// For RTC sender and receiver, we create printer for each fd.
|
||||
static SrsPithyPrint* create_rtc_send(int fd);
|
||||
static SrsPithyPrint* create_rtc_recv(int fd);
|
||||
#ifdef SRS_SRT
|
||||
static SrsPithyPrint* create_srt_play();
|
||||
static SrsPithyPrint* create_srt_publish();
|
||||
#endif
|
||||
virtual ~SrsPithyPrint();
|
||||
private:
|
||||
// Enter the specified stage, return the client id.
|
||||
|
|
678
trunk/src/app/srs_app_srt_conn.cpp
Normal file
678
trunk/src/app/srs_app_srt_conn.cpp
Normal file
|
@ -0,0 +1,678 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_app_srt_conn.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_buffer.hpp>
|
||||
#include <srs_kernel_flv.hpp>
|
||||
#include <srs_kernel_stream.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
#include <srs_rtmp_stack.hpp>
|
||||
#include <srs_service_st_srt.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_app_http_hooks.hpp>
|
||||
#include <srs_app_pithy_print.hpp>
|
||||
#include <srs_app_srt_server.hpp>
|
||||
#include <srs_app_srt_source.hpp>
|
||||
|
||||
SrsSrtConnection::SrsSrtConnection(SRTSOCKET srt_fd)
|
||||
{
|
||||
srt_fd_ = srt_fd;
|
||||
srt_skt_ = new SrsSrtSocket(_srt_eventloop->get_srt_poller(), srt_fd_);
|
||||
}
|
||||
|
||||
SrsSrtConnection::~SrsSrtConnection()
|
||||
{
|
||||
srs_freep(srt_skt_);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtConnection::initialize()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsSrtConnection::set_recv_timeout(srs_utime_t tm)
|
||||
{
|
||||
srt_skt_->set_recv_timeout(tm);
|
||||
}
|
||||
|
||||
srs_utime_t SrsSrtConnection::get_recv_timeout()
|
||||
{
|
||||
return srt_skt_->get_recv_timeout();
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtConnection::read_fully(void* buf, size_t size, ssize_t* nread)
|
||||
{
|
||||
return srs_error_new(ERROR_SRT_CONN, "unsupport method");
|
||||
}
|
||||
|
||||
int64_t SrsSrtConnection::get_recv_bytes()
|
||||
{
|
||||
return srt_skt_->get_recv_bytes();
|
||||
}
|
||||
|
||||
int64_t SrsSrtConnection::get_send_bytes()
|
||||
{
|
||||
return srt_skt_->get_send_bytes();
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtConnection::read(void* buf, size_t size, ssize_t* nread)
|
||||
{
|
||||
return srt_skt_->recvmsg(buf, size, nread);
|
||||
}
|
||||
|
||||
void SrsSrtConnection::set_send_timeout(srs_utime_t tm)
|
||||
{
|
||||
srt_skt_->set_send_timeout(tm);
|
||||
}
|
||||
|
||||
srs_utime_t SrsSrtConnection::get_send_timeout()
|
||||
{
|
||||
return srt_skt_->get_send_timeout();
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtConnection::write(void* buf, size_t size, ssize_t* nwrite)
|
||||
{
|
||||
return srt_skt_->sendmsg(buf, size, nwrite);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtConnection::writev(const iovec *iov, int iov_size, ssize_t* nwrite)
|
||||
{
|
||||
return srs_error_new(ERROR_SRT_CONN, "unsupport method");
|
||||
}
|
||||
|
||||
SrsMpegtsSrtConn::SrsMpegtsSrtConn(SrsSrtServer* srt_server, SRTSOCKET srt_fd, std::string ip, int port)
|
||||
{
|
||||
// Create a identify for this client.
|
||||
_srs_context->set_id(_srs_context->generate_id());
|
||||
|
||||
srt_server_ = srt_server;
|
||||
|
||||
srt_fd_ = srt_fd;
|
||||
srt_conn_ = new SrsSrtConnection(srt_fd_);
|
||||
clock_ = new SrsWallClock();
|
||||
kbps_ = new SrsKbps(clock_);
|
||||
kbps_->set_io(srt_conn_, srt_conn_);
|
||||
ip_ = ip;
|
||||
port_ = port;
|
||||
|
||||
trd_ = new SrsSTCoroutine("ts-srt", this, _srs_context->get_id());
|
||||
|
||||
srt_source_ = NULL;
|
||||
req_ = new SrsRequest();
|
||||
mode_ = SrtModePull;
|
||||
}
|
||||
|
||||
SrsMpegtsSrtConn::~SrsMpegtsSrtConn()
|
||||
{
|
||||
srs_freep(trd_);
|
||||
|
||||
srs_freep(kbps_);
|
||||
srs_freep(clock_);
|
||||
|
||||
srs_freep(srt_conn_);
|
||||
|
||||
srs_freep(req_);
|
||||
}
|
||||
|
||||
std::string SrsMpegtsSrtConn::desc()
|
||||
{
|
||||
return "srt-ts-conn";
|
||||
}
|
||||
|
||||
void SrsMpegtsSrtConn::remark(int64_t* in, int64_t* out)
|
||||
{
|
||||
// TODO: FIXME: no impl currently.
|
||||
kbps_->remark(in, out);
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::start()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = trd_->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "coroutine");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
std::string SrsMpegtsSrtConn::remote_ip()
|
||||
{
|
||||
return ip_;
|
||||
}
|
||||
|
||||
const SrsContextId& SrsMpegtsSrtConn::get_id()
|
||||
{
|
||||
return trd_->cid();
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
err = do_cycle();
|
||||
|
||||
// Notify manager to remove it.
|
||||
// Note that we create this object, so we use manager to remove it.
|
||||
srt_server_->remove(this);
|
||||
|
||||
// success.
|
||||
if (err == srs_success) {
|
||||
srs_trace("srt client finished.");
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error("srt serve error %s", srs_error_desc(err).c_str());
|
||||
srs_freep(err);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::do_cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err != fetch_or_create_source()) != srs_success) {
|
||||
return srs_error_wrap(err, "fetch or create srt source");
|
||||
}
|
||||
|
||||
if ((err = http_hooks_on_connect()) != srs_success) {
|
||||
return srs_error_wrap(err, "on connect");
|
||||
}
|
||||
|
||||
if (mode_ == SrtModePush) {
|
||||
err = publishing();
|
||||
} else if (mode_ == SrtModePull) {
|
||||
err = playing();
|
||||
}
|
||||
|
||||
http_hooks_on_close();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::fetch_or_create_source()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
string streamid = "";
|
||||
if ((err = srs_srt_get_streamid(srt_fd_, streamid)) != srs_success) {
|
||||
return srs_error_wrap(err, "get srt streamid");
|
||||
}
|
||||
|
||||
// Must have streamid, because srt ts packet will convert to rtmp or rtc.
|
||||
if (streamid.empty()) {
|
||||
return srs_error_new(ERROR_SRT_CONN, "empty srt streamid");
|
||||
}
|
||||
|
||||
// Detect streamid of srt to request.
|
||||
if (! srs_srt_streamid_to_request(streamid, mode_, req_)) {
|
||||
return srs_error_new(ERROR_SRT_CONN, "invalid srt streamid=%s", streamid.c_str());
|
||||
}
|
||||
|
||||
srs_trace("@srt, streamid=%s, stream_url=%s, vhost=%s, app=%s, stream=%s, param=%s",
|
||||
streamid.c_str(), req_->get_stream_url().c_str(), req_->vhost.c_str(), req_->app.c_str(), req_->stream.c_str(), req_->param.c_str());
|
||||
|
||||
if ((err = _srs_srt_sources->fetch_or_create(req_, &srt_source_)) != srs_success) {
|
||||
return srs_error_wrap(err, "fetch srt source");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::publishing()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = http_hooks_on_publish()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt: callback on publish");
|
||||
}
|
||||
|
||||
if ((err = acquire_publish()) == srs_success) {
|
||||
err = do_publishing();
|
||||
release_publish();
|
||||
}
|
||||
|
||||
http_hooks_on_unpublish();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::playing()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = http_hooks_on_play()) != srs_success) {
|
||||
return srs_error_wrap(err, "rtmp: callback on play");
|
||||
}
|
||||
|
||||
err = do_playing();
|
||||
http_hooks_on_stop();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::acquire_publish()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Check srt stream is busy.
|
||||
if (! srt_source_->can_publish()) {
|
||||
return srs_error_new(ERROR_SRT_SOURCE_BUSY, "srt stream %s busy", req_->get_stream_url().c_str());
|
||||
}
|
||||
|
||||
// Check rtmp stream is busy.
|
||||
SrsLiveSource *live_source = _srs_sources->fetch(req_);
|
||||
if (live_source && !live_source->can_publish(false)) {
|
||||
return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "live_source stream %s busy", req_->get_stream_url().c_str());
|
||||
}
|
||||
|
||||
if ((err = _srs_sources->fetch_or_create(req_, _srs_hybrid->srs()->instance(), &live_source)) != srs_success) {
|
||||
return srs_error_wrap(err, "create source");
|
||||
}
|
||||
|
||||
SrsRtmpFromTsBridge *bridger = new SrsRtmpFromTsBridge(live_source);
|
||||
if ((err = bridger->initialize(req_)) != srs_success) {
|
||||
srs_freep(bridger);
|
||||
return srs_error_wrap(err, "create bridger");
|
||||
}
|
||||
|
||||
srt_source_->set_bridger(bridger);
|
||||
|
||||
if ((err = srt_source_->on_publish()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt source publish");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsMpegtsSrtConn::release_publish()
|
||||
{
|
||||
srt_source_->on_unpublish();
|
||||
}
|
||||
|
||||
/*
|
||||
srs_error_t SrsMpegtsSrtConn::do_cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
string streamid = "";
|
||||
if ((err = srs_srt_get_streamid(srt_fd_, streamid)) != srs_success) {
|
||||
return srs_error_wrap(err, "get srt streamid");
|
||||
}
|
||||
|
||||
// Must have streamid, because srt ts packet will convert to rtmp or rtc.
|
||||
if (streamid.empty()) {
|
||||
return srs_error_new(ERROR_SRT_CONN, "empty srt streamid");
|
||||
}
|
||||
|
||||
// Detect streamid of srt to request.
|
||||
if (! srs_srt_streamid_to_request(streamid, mode_, req_)) {
|
||||
return srs_error_new(ERROR_SRT_CONN, "invalid srt streamid=%s", streamid.c_str());
|
||||
}
|
||||
|
||||
srs_trace("@srt, streamid=%s, stream_url=%s, vhost=%s, app=%s, stream=%s, param=%s",
|
||||
streamid.c_str(), req_->get_stream_url().c_str(), req_->vhost.c_str(), req_->app.c_str(), req_->stream.c_str(), req_->param.c_str());
|
||||
|
||||
if ((err = _srs_srt_sources->fetch_or_create(req_, &srt_source_)) != srs_success) {
|
||||
return srs_error_wrap(err, "fetch srt source");
|
||||
}
|
||||
|
||||
if (mode_ == SrtModePush) {
|
||||
if ((err = http_hooks_on_publish()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt: callback on publish");
|
||||
}
|
||||
// Do srt publish.
|
||||
if (! srt_source_->can_publish()) {
|
||||
return srs_error_new(ERROR_SRT_SOURCE_BUSY, "srt stream %s busy", req_->get_stream_url().c_str());
|
||||
}
|
||||
|
||||
SrsLiveSource *live_source = _srs_sources->fetch(req_);
|
||||
if (live_source && !live_source->can_publish(false)) {
|
||||
return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "live_source stream %s busy", req_->get_stream_url().c_str());
|
||||
}
|
||||
|
||||
if ((err = _srs_sources->fetch_or_create(req_, _srs_hybrid->srs()->instance(), &live_source)) != srs_success) {
|
||||
return srs_error_wrap(err, "create source");
|
||||
}
|
||||
|
||||
SrsRtmpFromTsBridge *bridger = new SrsRtmpFromTsBridge(live_source);
|
||||
if ((err = bridger->initialize(req_)) != srs_success) {
|
||||
srs_freep(bridger);
|
||||
return srs_error_wrap(err, "create bridger");
|
||||
}
|
||||
|
||||
srt_source_->set_bridger(bridger);
|
||||
|
||||
if ((err = srt_source_->on_publish()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt source publish");
|
||||
}
|
||||
|
||||
err = do_publish_cycle();
|
||||
|
||||
srt_source_->on_unpublish();
|
||||
http_hooks_on_unpublish();
|
||||
} else if (mode_ == SrtModePull) {
|
||||
if ((err = http_hooks_on_play()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt: callback on play");
|
||||
}
|
||||
// Do srt play.
|
||||
err = do_play_cycle();
|
||||
|
||||
http_hooks_on_stop();
|
||||
} else {
|
||||
srs_assert(false);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
*/
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::do_publishing()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsPithyPrint* pprint = SrsPithyPrint::create_srt_publish();
|
||||
SrsAutoFree(SrsPithyPrint, pprint);
|
||||
|
||||
int nb_packets = 0;
|
||||
|
||||
// Max udp packet size equal to 1500.
|
||||
char buf[1500];
|
||||
while (true) {
|
||||
if ((err = trd_->pull()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt: thread quit");
|
||||
}
|
||||
|
||||
pprint->elapse();
|
||||
|
||||
// reportable
|
||||
if (pprint->can_print()) {
|
||||
kbps_->sample();
|
||||
srs_trace("<- " SRS_CONSTS_LOG_SRT_PUBLISH " time=%d, packets=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d",
|
||||
(int)pprint->age(), nb_packets, 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());
|
||||
nb_packets = 0;
|
||||
}
|
||||
|
||||
ssize_t nb = 0;
|
||||
if ((err = srt_conn_->read(buf, sizeof(buf), &nb)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt: recvmsg");
|
||||
}
|
||||
|
||||
++nb_packets;
|
||||
|
||||
if ((err = on_srt_packet(buf, nb)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt: process packet");
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::do_playing()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsSrtConsumer* consumer = NULL;
|
||||
SrsAutoFree(SrsSrtConsumer, consumer);
|
||||
if ((err = srt_source_->create_consumer(consumer)) != srs_success) {
|
||||
return srs_error_wrap(err, "create consumer, ts source=%s", req_->get_stream_url().c_str());
|
||||
}
|
||||
|
||||
srs_assert(consumer);
|
||||
|
||||
// TODO: FIXME: Dumps the SPS/PPS from gop cache, without other frames.
|
||||
if ((err = srt_source_->consumer_dumps(consumer)) != srs_success) {
|
||||
return srs_error_wrap(err, "dumps consumer, url=%s", req_->get_stream_url().c_str());
|
||||
}
|
||||
|
||||
SrsPithyPrint* pprint = SrsPithyPrint::create_srt_play();
|
||||
SrsAutoFree(SrsPithyPrint, pprint);
|
||||
|
||||
int nb_packets = 0;
|
||||
|
||||
while (true) {
|
||||
if ((err = trd_->pull()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt play thread");
|
||||
}
|
||||
|
||||
pprint->elapse();
|
||||
|
||||
// Wait for amount of packets.
|
||||
SrsSrtPacket* pkt = NULL;
|
||||
SrsAutoFree(SrsSrtPacket, pkt);
|
||||
consumer->dump_packet(&pkt);
|
||||
if (!pkt) {
|
||||
// TODO: FIXME: We should check the quit event.
|
||||
consumer->wait(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// reportable
|
||||
if (pprint->can_print()) {
|
||||
kbps_->sample();
|
||||
srs_trace("-> " SRS_CONSTS_LOG_SRT_PLAY " time=%d, packets=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d",
|
||||
(int)pprint->age(), nb_packets, 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());
|
||||
nb_packets = 0;
|
||||
}
|
||||
|
||||
++nb_packets;
|
||||
|
||||
ssize_t nb_write = 0;
|
||||
if ((err = srt_conn_->write(pkt->data(), pkt->size(), &nb_write)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt send, size=%d", pkt->size());
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::on_srt_packet(char* buf, int nb_buf)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Check srt payload, mpegts must be N times of SRS_TS_PACKET_SIZE, and the first byte must be 0x47
|
||||
if ((nb_buf <= 0) || (nb_buf % SRS_TS_PACKET_SIZE != 0) || (buf[0] != 0x47)) {
|
||||
return srs_error_new(ERROR_SRT_CONN, "invalid ts packet");
|
||||
}
|
||||
|
||||
SrsSrtPacket* packet = new SrsSrtPacket();
|
||||
SrsAutoFree(SrsSrtPacket, packet);
|
||||
packet->wrap(buf, nb_buf);
|
||||
|
||||
if ((err = srt_source_->on_packet(packet)) != srs_success) {
|
||||
return srs_error_wrap(err, "on srt packet");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::http_hooks_on_connect()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// the http hooks will cause context switch,
|
||||
// so we must copy all hooks for the on_connect may freed.
|
||||
// @see https://github.com/ossrs/srs/issues/475
|
||||
vector<string> hooks;
|
||||
|
||||
if (true) {
|
||||
SrsConfDirective* conf = _srs_config->get_vhost_on_connect(req_->vhost);
|
||||
|
||||
if (!conf) {
|
||||
return err;
|
||||
}
|
||||
|
||||
hooks = conf->args;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)hooks.size(); i++) {
|
||||
std::string url = hooks.at(i);
|
||||
if ((err = SrsHttpHooks::on_connect(url, req_)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt on_connect %s", url.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsMpegtsSrtConn::http_hooks_on_close()
|
||||
{
|
||||
if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the http hooks will cause context switch,
|
||||
// so we must copy all hooks for the on_connect may freed.
|
||||
// @see https://github.com/ossrs/srs/issues/475
|
||||
vector<string> hooks;
|
||||
|
||||
if (true) {
|
||||
SrsConfDirective* conf = _srs_config->get_vhost_on_close(req_->vhost);
|
||||
|
||||
if (!conf) {
|
||||
return;
|
||||
}
|
||||
|
||||
hooks = conf->args;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)hooks.size(); i++) {
|
||||
std::string url = hooks.at(i);
|
||||
SrsHttpHooks::on_close(url, req_, kbps_->get_send_bytes(), kbps_->get_recv_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::http_hooks_on_publish()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// the http hooks will cause context switch,
|
||||
// so we must copy all hooks for the on_connect may freed.
|
||||
// @see https://github.com/ossrs/srs/issues/475
|
||||
vector<string> hooks;
|
||||
|
||||
if (true) {
|
||||
SrsConfDirective* conf = _srs_config->get_vhost_on_publish(req_->vhost);
|
||||
|
||||
if (!conf) {
|
||||
return err;
|
||||
}
|
||||
|
||||
hooks = conf->args;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)hooks.size(); i++) {
|
||||
std::string url = hooks.at(i);
|
||||
if ((err = SrsHttpHooks::on_publish(url, req_)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt on_publish %s", url.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsMpegtsSrtConn::http_hooks_on_unpublish()
|
||||
{
|
||||
if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the http hooks will cause context switch,
|
||||
// so we must copy all hooks for the on_connect may freed.
|
||||
// @see https://github.com/ossrs/srs/issues/475
|
||||
vector<string> hooks;
|
||||
|
||||
if (true) {
|
||||
SrsConfDirective* conf = _srs_config->get_vhost_on_unpublish(req_->vhost);
|
||||
|
||||
if (!conf) {
|
||||
return;
|
||||
}
|
||||
|
||||
hooks = conf->args;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)hooks.size(); i++) {
|
||||
std::string url = hooks.at(i);
|
||||
SrsHttpHooks::on_unpublish(url, req_);
|
||||
}
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsSrtConn::http_hooks_on_play()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// the http hooks will cause context switch,
|
||||
// so we must copy all hooks for the on_connect may freed.
|
||||
// @see https://github.com/ossrs/srs/issues/475
|
||||
vector<string> hooks;
|
||||
|
||||
if (true) {
|
||||
SrsConfDirective* conf = _srs_config->get_vhost_on_play(req_->vhost);
|
||||
|
||||
if (!conf) {
|
||||
return err;
|
||||
}
|
||||
|
||||
hooks = conf->args;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)hooks.size(); i++) {
|
||||
std::string url = hooks.at(i);
|
||||
if ((err = SrsHttpHooks::on_play(url, req_)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt on_play %s", url.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsMpegtsSrtConn::http_hooks_on_stop()
|
||||
{
|
||||
if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the http hooks will cause context switch,
|
||||
// so we must copy all hooks for the on_connect may freed.
|
||||
// @see https://github.com/ossrs/srs/issues/475
|
||||
vector<string> hooks;
|
||||
|
||||
if (true) {
|
||||
SrsConfDirective* conf = _srs_config->get_vhost_on_stop(req_->vhost);
|
||||
|
||||
if (!conf) {
|
||||
return;
|
||||
}
|
||||
|
||||
hooks = conf->args;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)hooks.size(); i++) {
|
||||
std::string url = hooks.at(i);
|
||||
SrsHttpHooks::on_stop(url, req_);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
109
trunk/src/app/srs_app_srt_conn.hpp
Normal file
109
trunk/src/app/srs_app_srt_conn.hpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#ifndef SRS_APP_SRT_CONN_HPP
|
||||
#define SRS_APP_SRT_CONN_HPP
|
||||
|
||||
#include <srs_core.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <srs_service_st_srt.hpp>
|
||||
#include <srs_app_st.hpp>
|
||||
#include <srs_app_conn.hpp>
|
||||
#include <srs_app_srt_utility.hpp>
|
||||
|
||||
class SrsBuffer;
|
||||
class SrsLiveSource;
|
||||
class SrsSrtSource;
|
||||
class SrsSrtServer;
|
||||
|
||||
// The basic connection of SRS, for SRT based protocols,
|
||||
// all srt connections accept from srt listener must extends from this base class,
|
||||
// srt server will add the connection to manager, and delete it when remove.
|
||||
class SrsSrtConnection : public ISrsProtocolReadWriter
|
||||
{
|
||||
public:
|
||||
SrsSrtConnection(SRTSOCKET srt_fd);
|
||||
virtual ~SrsSrtConnection();
|
||||
public:
|
||||
virtual srs_error_t initialize();
|
||||
// Interface ISrsProtocolReadWriter
|
||||
public:
|
||||
virtual void set_recv_timeout(srs_utime_t tm);
|
||||
virtual srs_utime_t get_recv_timeout();
|
||||
virtual srs_error_t read_fully(void* buf, size_t size, ssize_t* nread);
|
||||
virtual int64_t get_recv_bytes();
|
||||
virtual int64_t get_send_bytes();
|
||||
virtual srs_error_t read(void* buf, size_t size, ssize_t* nread);
|
||||
virtual void set_send_timeout(srs_utime_t tm);
|
||||
virtual srs_utime_t get_send_timeout();
|
||||
virtual srs_error_t write(void* buf, size_t size, ssize_t* nwrite);
|
||||
virtual srs_error_t writev(const iovec *iov, int iov_size, ssize_t* nwrite);
|
||||
private:
|
||||
// The underlayer srt fd handler.
|
||||
SRTSOCKET srt_fd_;
|
||||
// The underlayer srt socket.
|
||||
SrsSrtSocket* srt_skt_;
|
||||
};
|
||||
|
||||
class SrsMpegtsSrtConn : public ISrsStartableConneciton, public ISrsCoroutineHandler
|
||||
{
|
||||
public:
|
||||
SrsMpegtsSrtConn(SrsSrtServer* srt_server, SRTSOCKET srt_fd, std::string ip, int port);
|
||||
virtual ~SrsMpegtsSrtConn();
|
||||
// Interface ISrsResource.
|
||||
public:
|
||||
virtual std::string desc();
|
||||
// Interface ISrsKbpsDelta
|
||||
public:
|
||||
virtual void remark(int64_t* in, int64_t* out);
|
||||
public:
|
||||
virtual srs_error_t start();
|
||||
// Interface ISrsConnection.
|
||||
public:
|
||||
virtual std::string remote_ip();
|
||||
virtual const SrsContextId& get_id();
|
||||
// Interface ISrsCoroutineHandler
|
||||
public:
|
||||
virtual srs_error_t cycle();
|
||||
protected:
|
||||
virtual srs_error_t do_cycle();
|
||||
private:
|
||||
srs_error_t fetch_or_create_source();
|
||||
srs_error_t publishing();
|
||||
srs_error_t playing();
|
||||
srs_error_t acquire_publish();
|
||||
void release_publish();
|
||||
srs_error_t do_publishing();
|
||||
srs_error_t do_playing();
|
||||
private:
|
||||
srs_error_t on_srt_packet(char* buf, int nb_buf);
|
||||
private:
|
||||
srs_error_t http_hooks_on_connect();
|
||||
void http_hooks_on_close();
|
||||
srs_error_t http_hooks_on_publish();
|
||||
void http_hooks_on_unpublish();
|
||||
srs_error_t http_hooks_on_play();
|
||||
void http_hooks_on_stop();
|
||||
private:
|
||||
SrsSrtServer* srt_server_;
|
||||
SRTSOCKET srt_fd_;
|
||||
SrsSrtConnection* srt_conn_;
|
||||
SrsWallClock* clock_;
|
||||
SrsKbps* kbps_;
|
||||
std::string ip_;
|
||||
int port_;
|
||||
SrsCoroutine* trd_;
|
||||
|
||||
SrsRequest* req_;
|
||||
SrtMode mode_;
|
||||
SrsSrtSource* srt_source_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
100
trunk/src/app/srs_app_srt_listener.cpp
Normal file
100
trunk/src/app/srs_app_srt_listener.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_app_srt_listener.hpp>
|
||||
|
||||
#include <st.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_app_srt_server.hpp>
|
||||
|
||||
ISrsSrtHandler::ISrsSrtHandler()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsSrtHandler::~ISrsSrtHandler()
|
||||
{
|
||||
}
|
||||
|
||||
SrsSrtListener::SrsSrtListener(ISrsSrtHandler* h, std::string i, int p)
|
||||
{
|
||||
handler_ = h;
|
||||
ip_ = i;
|
||||
port_ = p;
|
||||
|
||||
lfd_ = SRT_INVALID_SOCK;
|
||||
srt_skt_ = NULL;
|
||||
|
||||
trd_ = new SrsDummyCoroutine();
|
||||
}
|
||||
|
||||
SrsSrtListener::~SrsSrtListener()
|
||||
{
|
||||
srs_freep(trd_);
|
||||
srs_freep(srt_skt_);
|
||||
srt_close(lfd_);
|
||||
}
|
||||
|
||||
int SrsSrtListener::fd()
|
||||
{
|
||||
return lfd_;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtListener::create_socket()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
if ((err = srs_srt_socket(&lfd_)) != srs_success) {
|
||||
return srs_error_wrap(err, "create_socket");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtListener::listen()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = srs_srt_listen(lfd_, ip_, port_)) != srs_success) {
|
||||
return srs_error_wrap(err, "srs_srt_listen");
|
||||
}
|
||||
|
||||
srt_skt_ = new SrsSrtSocket(_srt_eventloop->get_srt_poller(), lfd_);
|
||||
// Accept never timeout.
|
||||
srt_skt_->set_recv_timeout(ST_UTIME_NO_TIMEOUT);
|
||||
srt_skt_->set_send_timeout(ST_UTIME_NO_TIMEOUT);
|
||||
|
||||
srs_freep(trd_);
|
||||
trd_ = new SrsSTCoroutine("srt_listener", this);
|
||||
if ((err = trd_->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "start coroutine");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtListener::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
while (true) {
|
||||
if ((err = trd_->pull()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt listener");
|
||||
}
|
||||
|
||||
SRTSOCKET client_srt_fd = SRT_INVALID_SOCK;
|
||||
if ((err = srt_skt_->accept(&client_srt_fd)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt accept");
|
||||
}
|
||||
|
||||
// TODO: FIXME: print some log and client srt options.
|
||||
|
||||
if ((err = handler_->on_srt_client(client_srt_fd)) != srs_success) {
|
||||
return srs_error_wrap(err, "handle srt fd=%d", client_srt_fd);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
53
trunk/src/app/srs_app_srt_listener.hpp
Normal file
53
trunk/src/app/srs_app_srt_listener.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#ifndef SRS_APP_SRT_LISTENER_HPP
|
||||
#define SRS_APP_SRT_LISTENER_HPP
|
||||
|
||||
#include <srs_core.hpp>
|
||||
#include <srs_app_st.hpp>
|
||||
#include <srs_service_st_srt.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
// The srt connection handler.
|
||||
class ISrsSrtHandler
|
||||
{
|
||||
public:
|
||||
ISrsSrtHandler();
|
||||
virtual ~ISrsSrtHandler();
|
||||
public:
|
||||
// When got srt client.
|
||||
virtual srs_error_t on_srt_client(SRTSOCKET srt_fd) = 0;
|
||||
};
|
||||
|
||||
// Bind and listen SRT(udp) port, use handler to process the client.
|
||||
class SrsSrtListener : public ISrsCoroutineHandler
|
||||
{
|
||||
private:
|
||||
SRTSOCKET lfd_;
|
||||
SrsSrtSocket* srt_skt_;
|
||||
SrsCoroutine* trd_;
|
||||
private:
|
||||
ISrsSrtHandler* handler_;
|
||||
std::string ip_;
|
||||
int port_;
|
||||
public:
|
||||
SrsSrtListener(ISrsSrtHandler* h, std::string i, int p);
|
||||
virtual ~SrsSrtListener();
|
||||
public:
|
||||
virtual SRTSOCKET fd();
|
||||
public:
|
||||
// Create srt socket, separate this step because of srt have some option must set before listen.
|
||||
virtual srs_error_t create_socket();
|
||||
virtual srs_error_t listen();
|
||||
// Interface ISrsReusableThreadHandler.
|
||||
public:
|
||||
virtual srs_error_t cycle();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
392
trunk/src/app/srs_app_srt_server.cpp
Normal file
392
trunk/src/app/srs_app_srt_server.cpp
Normal file
|
@ -0,0 +1,392 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_app_srt_server.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_service_log.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_app_srt_conn.hpp>
|
||||
|
||||
std::string srs_srt_listener_type2string(SrsSrtListenerType type)
|
||||
{
|
||||
switch (type) {
|
||||
case SrsSrtListenerMpegts:
|
||||
return "SRT-MPEGTS";
|
||||
default:
|
||||
return "UNKONWN";
|
||||
}
|
||||
}
|
||||
|
||||
SrsSrtAcceptor::SrsSrtAcceptor(SrsSrtServer* srt_server, SrsSrtListenerType t)
|
||||
{
|
||||
port_ = 0;
|
||||
srt_server_ = srt_server;
|
||||
type_ = t;
|
||||
}
|
||||
|
||||
SrsSrtAcceptor::~SrsSrtAcceptor()
|
||||
{
|
||||
}
|
||||
|
||||
SrsSrtListenerType SrsSrtAcceptor::listen_type()
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
SrsSrtMessageAcceptor::SrsSrtMessageAcceptor(SrsSrtServer* srt_server, SrsSrtListenerType listen_type)
|
||||
: SrsSrtAcceptor(srt_server, listen_type)
|
||||
{
|
||||
listener_ = NULL;
|
||||
}
|
||||
|
||||
SrsSrtMessageAcceptor::~SrsSrtMessageAcceptor()
|
||||
{
|
||||
srs_freep(listener_);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtMessageAcceptor::listen(std::string ip, int port)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
ip_ = ip;
|
||||
port_ = port;
|
||||
|
||||
srs_freep(listener_);
|
||||
listener_ = new SrsSrtListener(this, ip_, port_);
|
||||
|
||||
// Create srt socket.
|
||||
if ((err = listener_->create_socket()) != srs_success) {
|
||||
return srs_error_wrap(err, "message srt acceptor");
|
||||
}
|
||||
|
||||
// Set all the srt option from config.
|
||||
if ((err = set_srt_opt()) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
// Start listen srt socket, this function will set the socket in async mode.
|
||||
if ((err = listener_->listen()) != srs_success) {
|
||||
return srs_error_wrap(err, "message srt acceptor");
|
||||
}
|
||||
|
||||
string v = srs_srt_listener_type2string(type_);
|
||||
srs_trace("%s listen at srt://%s:%d, fd=%d", v.c_str(), ip_.c_str(), port_, listener_->fd());
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtMessageAcceptor::set_srt_opt()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = srs_srt_set_maxbw(listener_->fd(), _srs_config->get_srto_maxbw())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_mss(listener_->fd(), _srs_config->get_srto_mss())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_tsbpdmode(listener_->fd(), _srs_config->get_srto_tsbpdmode())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_latency(listener_->fd(), _srs_config->get_srto_latency())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_rcv_latency(listener_->fd(), _srs_config->get_srto_recv_latency())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_peer_latency(listener_->fd(), _srs_config->get_srto_peer_latency())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_tlpktdrop(listener_->fd(), _srs_config->get_srto_tlpktdrop())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_connect_timeout(listener_->fd(), _srs_config->get_srto_conntimeout())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_peer_idle_timeout(listener_->fd(), _srs_config->get_srto_peeridletimeout())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_sndbuf(listener_->fd(), _srs_config->get_srto_sendbuf())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_rcvbuf(listener_->fd(), _srs_config->get_srto_recvbuf())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_payload_size(listener_->fd(), _srs_config->get_srto_payloadsize())) != srs_success) {
|
||||
return srs_error_wrap(err, "set opt");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtMessageAcceptor::on_srt_client(SRTSOCKET srt_fd)
|
||||
{
|
||||
// Notify srt server to accept srt client, and create new SrsSrtConn on it.
|
||||
srs_error_t err = srt_server_->accept_srt_client(type_, srt_fd);
|
||||
if (err != srs_success) {
|
||||
srs_warn("accept srt client failed, err is %s", srs_error_desc(err).c_str());
|
||||
srs_freep(err);
|
||||
}
|
||||
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
SrsSrtServer::SrsSrtServer()
|
||||
{
|
||||
conn_manager_ = new SrsResourceManager("SRT", true);
|
||||
}
|
||||
|
||||
SrsSrtServer::~SrsSrtServer()
|
||||
{
|
||||
srs_freep(conn_manager_);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtServer::initialize()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtServer::listen()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Listen mpegts over srt.
|
||||
if ((err = listen_srt_mpegts()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt mpegts listen");
|
||||
}
|
||||
|
||||
if ((err = conn_manager_->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt connection manager");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtServer::listen_srt_mpegts()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (! _srs_config->get_srt_enabled()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// TODO: FIXME: bad code, refine it.
|
||||
std::vector<std::string> ip_ports;
|
||||
std::stringstream ss;
|
||||
ss << _srs_config->get_srt_listen_port();
|
||||
ip_ports.push_back(ss.str());
|
||||
|
||||
close_listeners(SrsSrtListenerMpegts);
|
||||
|
||||
for (int i = 0; i < (int)ip_ports.size(); i++) {
|
||||
SrsSrtAcceptor* acceptor = new SrsSrtMessageAcceptor(this, SrsSrtListenerMpegts);
|
||||
acceptors_.push_back(acceptor);
|
||||
|
||||
int port; string ip;
|
||||
srs_parse_endpoint(ip_ports[i], ip, port);
|
||||
|
||||
if ((err = acceptor->listen(ip, port)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt listen %s:%d", ip.c_str(), port);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsSrtServer::close_listeners(SrsSrtListenerType type)
|
||||
{
|
||||
std::vector<SrsSrtAcceptor*>::iterator it;
|
||||
for (it = acceptors_.begin(); it != acceptors_.end();) {
|
||||
SrsSrtAcceptor* acceptor = *it;
|
||||
|
||||
if (acceptor->listen_type() != type) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
srs_freep(acceptor);
|
||||
it = acceptors_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtServer::accept_srt_client(SrsSrtListenerType type, SRTSOCKET srt_fd)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
ISrsStartableConneciton* conn = NULL;
|
||||
|
||||
if ((err = fd_to_resource(type, srt_fd, &conn)) != srs_success) {
|
||||
//close fd on conn error, otherwise will lead to fd leak -gs
|
||||
srt_close(srt_fd);
|
||||
return srs_error_wrap(err, "srt fd to resource");
|
||||
}
|
||||
srs_assert(conn);
|
||||
|
||||
// directly enqueue, the cycle thread will remove the client.
|
||||
conn_manager_->add(conn);
|
||||
|
||||
if ((err = conn->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "start srt conn coroutine");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtServer::fd_to_resource(SrsSrtListenerType type, SRTSOCKET srt_fd, ISrsStartableConneciton** pr)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
string ip = "";
|
||||
int port = 0;
|
||||
|
||||
if ((err = srs_srt_get_remote_ip_port(srt_fd, ip, port)) != srs_success) {
|
||||
return srs_error_wrap(err, "get srt ip port");
|
||||
}
|
||||
|
||||
srs_trace("accept srt client from %s:%d, fd=%d", ip.c_str(), port, srt_fd);
|
||||
|
||||
// TODO: FIXME: need to check max connection?
|
||||
|
||||
// The context id may change during creating the bellow objects.
|
||||
SrsContextRestore(_srs_context->get_id());
|
||||
|
||||
if (type == SrsSrtListenerMpegts) {
|
||||
*pr = new SrsMpegtsSrtConn(this, srt_fd, ip, port);
|
||||
} else {
|
||||
srs_warn("close for no service handler. srtfd=%d, ip=%s:%d", srt_fd, ip.c_str(), port);
|
||||
srt_close(srt_fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsSrtServer::remove(ISrsResource* c)
|
||||
{
|
||||
// TODO: FIXME: add some statistic of srt.
|
||||
// ISrsStartableConneciton* conn = dynamic_cast<ISrsStartableConneciton*>(c);
|
||||
|
||||
// SrsStatistic* stat = SrsStatistic::instance();
|
||||
// stat->kbps_add_delta(c->get_id().c_str(), conn);
|
||||
// stat->on_disconnect(c->get_id().c_str());
|
||||
|
||||
// use manager to free it async.
|
||||
conn_manager_->remove(c);
|
||||
}
|
||||
|
||||
SrsSrtServerAdapter::SrsSrtServerAdapter()
|
||||
{
|
||||
srt_server_ = new SrsSrtServer();
|
||||
}
|
||||
|
||||
SrsSrtServerAdapter::~SrsSrtServerAdapter()
|
||||
{
|
||||
srs_freep(srt_server_);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtServerAdapter::initialize()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtServerAdapter::run(SrsWaitGroup* wg)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Initialize the whole system, set hooks to handle server level events.
|
||||
if ((err = srt_server_->initialize()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt server initialize");
|
||||
}
|
||||
|
||||
if ((err = srt_server_->listen()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt listen");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsSrtServerAdapter::stop()
|
||||
{
|
||||
}
|
||||
|
||||
SrsSrtServer* SrsSrtServerAdapter::instance()
|
||||
{
|
||||
return srt_server_;
|
||||
}
|
||||
|
||||
SrsSrtEventLoop::SrsSrtEventLoop()
|
||||
{
|
||||
srt_poller_ = NULL;
|
||||
trd_ = NULL;
|
||||
}
|
||||
|
||||
SrsSrtEventLoop::~SrsSrtEventLoop()
|
||||
{
|
||||
srs_freep(trd_);
|
||||
srs_freep(srt_poller_);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtEventLoop::initialize()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
srt_poller_ = new SrsSrtPoller();
|
||||
|
||||
if ((err = srt_poller_->initialize()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt poller initialize");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtEventLoop::start()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
trd_ = new SrsSTCoroutine("srt_listener", this);
|
||||
if ((err = trd_->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "start coroutine");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtEventLoop::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
while (true) {
|
||||
if ((err = trd_->pull()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt listener");
|
||||
}
|
||||
|
||||
if ((err = srt_poller_->wait(0)) != srs_success) {
|
||||
srs_error("srt poll wait failed, err=%s", srs_error_desc(err).c_str());
|
||||
srs_error_reset(err);
|
||||
}
|
||||
|
||||
srs_usleep(10 * SRS_UTIME_MILLISECONDS);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
130
trunk/src/app/srs_app_srt_server.hpp
Normal file
130
trunk/src/app/srs_app_srt_server.hpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#ifndef SRS_APP_SRT_SERVER_HPP
|
||||
#define SRS_APP_SRT_SERVER_HPP
|
||||
|
||||
#include <srs_core.hpp>
|
||||
|
||||
#include <srs_service_st_srt.hpp>
|
||||
#include <srs_app_server.hpp>
|
||||
#include <srs_app_srt_listener.hpp>
|
||||
|
||||
class SrsSrtServer;
|
||||
|
||||
enum SrsSrtListenerType
|
||||
{
|
||||
SrsSrtListenerMpegts = 1,
|
||||
};
|
||||
|
||||
// A common srt acceptor, for SRT server.
|
||||
class SrsSrtAcceptor
|
||||
{
|
||||
protected:
|
||||
SrsSrtListenerType type_;
|
||||
protected:
|
||||
std::string ip_;
|
||||
int port_;
|
||||
SrsSrtServer* srt_server_;
|
||||
public:
|
||||
SrsSrtAcceptor(SrsSrtServer* srt_server, SrsSrtListenerType listen_type);
|
||||
virtual ~SrsSrtAcceptor();
|
||||
public:
|
||||
virtual SrsSrtListenerType listen_type();
|
||||
virtual srs_error_t listen(std::string ip, int port) = 0;
|
||||
};
|
||||
|
||||
// A srt messge acceptor.
|
||||
class SrsSrtMessageAcceptor : public SrsSrtAcceptor, public ISrsSrtHandler
|
||||
{
|
||||
private:
|
||||
SrsSrtListener* listener_;
|
||||
public:
|
||||
SrsSrtMessageAcceptor(SrsSrtServer* srt_server, SrsSrtListenerType listen_type);
|
||||
virtual ~SrsSrtMessageAcceptor();
|
||||
public:
|
||||
virtual srs_error_t listen(std::string i, int p);
|
||||
virtual srs_error_t set_srt_opt();
|
||||
// Interface ISrsSrtHandler
|
||||
public:
|
||||
virtual srs_error_t on_srt_client(SRTSOCKET srt_fd);
|
||||
};
|
||||
|
||||
// SRS SRT server, initialize and listen, start connection service thread, destroy client.
|
||||
class SrsSrtServer : public ISrsResourceManager
|
||||
{
|
||||
private:
|
||||
SrsResourceManager* conn_manager_;
|
||||
private:
|
||||
std::vector<SrsSrtAcceptor*> acceptors_;
|
||||
public:
|
||||
SrsSrtServer();
|
||||
virtual ~SrsSrtServer();
|
||||
public:
|
||||
virtual srs_error_t initialize();
|
||||
virtual srs_error_t listen();
|
||||
private:
|
||||
// listen at specified srt protocol.
|
||||
virtual srs_error_t listen_srt_mpegts();
|
||||
// Close the listeners for specified type,
|
||||
// Remove the listen object from manager.
|
||||
virtual void close_listeners(SrsSrtListenerType type);
|
||||
// For internal only
|
||||
public:
|
||||
// When listener got a fd, notice server to accept it.
|
||||
// @param type, the client type, used to create concrete connection,
|
||||
// for instance SRT connection to serve client.
|
||||
// @param srt_fd, the client fd in srt boxed, the underlayer fd.
|
||||
virtual srs_error_t accept_srt_client(SrsSrtListenerType type, SRTSOCKET srt_fd);
|
||||
private:
|
||||
virtual srs_error_t fd_to_resource(SrsSrtListenerType type, SRTSOCKET srt_fd, ISrsStartableConneciton** pr);
|
||||
// Interface ISrsResourceManager
|
||||
public:
|
||||
// A callback for connection to remove itself.
|
||||
// When connection thread cycle terminated, callback this to delete connection.
|
||||
virtual void remove(ISrsResource* c);
|
||||
};
|
||||
|
||||
// The srt server adapter, the master server.
|
||||
class SrsSrtServerAdapter : public ISrsHybridServer
|
||||
{
|
||||
private:
|
||||
SrsSrtServer* srt_server_;
|
||||
public:
|
||||
SrsSrtServerAdapter();
|
||||
virtual ~SrsSrtServerAdapter();
|
||||
public:
|
||||
virtual srs_error_t initialize();
|
||||
virtual srs_error_t run(SrsWaitGroup* wg);
|
||||
virtual void stop();
|
||||
public:
|
||||
virtual SrsSrtServer* instance();
|
||||
};
|
||||
|
||||
// The srt event loop, run srt poller and wait event happeed.
|
||||
class SrsSrtEventLoop : public ISrsCoroutineHandler
|
||||
{
|
||||
public:
|
||||
SrsSrtEventLoop();
|
||||
virtual ~SrsSrtEventLoop();
|
||||
public:
|
||||
SrsSrtPoller* get_srt_poller() { return srt_poller_; }
|
||||
public:
|
||||
srs_error_t initialize();
|
||||
srs_error_t start();
|
||||
// Interface ISrsCoroutineHandler.
|
||||
public:
|
||||
virtual srs_error_t cycle();
|
||||
private:
|
||||
SrsSrtPoller* srt_poller_;
|
||||
SrsCoroutine* trd_;
|
||||
};
|
||||
|
||||
// SrsSrtEventLoop is global singleton instance.
|
||||
extern SrsSrtEventLoop* _srt_eventloop;
|
||||
|
||||
#endif
|
||||
|
798
trunk/src/app/srs_app_srt_source.cpp
Normal file
798
trunk/src/app/srs_app_srt_source.cpp
Normal file
|
@ -0,0 +1,798 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_app_srt_source.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_flv.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_kernel_buffer.hpp>
|
||||
#include <srs_kernel_stream.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
#include <srs_raw_avc.hpp>
|
||||
#include <srs_rtmp_stack.hpp>
|
||||
#include <srs_app_source.hpp>
|
||||
#include <srs_app_statistic.hpp>
|
||||
|
||||
SrsSrtPacket::SrsSrtPacket()
|
||||
{
|
||||
shared_buffer_ = NULL;
|
||||
actual_buffer_size_ = 0;
|
||||
}
|
||||
|
||||
SrsSrtPacket::~SrsSrtPacket()
|
||||
{
|
||||
srs_freep(shared_buffer_);
|
||||
}
|
||||
|
||||
char* SrsSrtPacket::wrap(int size)
|
||||
{
|
||||
// The buffer size is larger or equals to the size of packet.
|
||||
actual_buffer_size_ = size;
|
||||
|
||||
// If the buffer is large enough, reuse it.
|
||||
if (shared_buffer_ && shared_buffer_->size >= size) {
|
||||
return shared_buffer_->payload;
|
||||
}
|
||||
|
||||
// Create a large enough message, with under-layer buffer.
|
||||
srs_freep(shared_buffer_);
|
||||
shared_buffer_ = new SrsSharedPtrMessage();
|
||||
|
||||
char* buf = new char[size];
|
||||
shared_buffer_->wrap(buf, size);
|
||||
|
||||
return shared_buffer_->payload;
|
||||
}
|
||||
|
||||
char* SrsSrtPacket::wrap(char* data, int size)
|
||||
{
|
||||
char* buf = wrap(size);
|
||||
memcpy(buf, data, size);
|
||||
return buf;
|
||||
}
|
||||
|
||||
char* SrsSrtPacket::wrap(SrsSharedPtrMessage* msg)
|
||||
{
|
||||
// Generally, the wrap(msg) is used for RTMP to SRT, where the msg
|
||||
// is not generated by SRT.
|
||||
srs_freep(shared_buffer_);
|
||||
|
||||
// Copy from the new message.
|
||||
shared_buffer_ = msg->copy();
|
||||
// If we wrap a message, the size of packet equals to the message size.
|
||||
actual_buffer_size_ = shared_buffer_->size;
|
||||
|
||||
return msg->payload;
|
||||
}
|
||||
|
||||
SrsSrtPacket* SrsSrtPacket::copy()
|
||||
{
|
||||
SrsSrtPacket* cp = new SrsSrtPacket();
|
||||
|
||||
cp->shared_buffer_ = shared_buffer_? shared_buffer_->copy2() : NULL;
|
||||
cp->actual_buffer_size_ = actual_buffer_size_;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
char* SrsSrtPacket::data()
|
||||
{
|
||||
return shared_buffer_->payload;
|
||||
}
|
||||
|
||||
int SrsSrtPacket::size()
|
||||
{
|
||||
return shared_buffer_->size;
|
||||
}
|
||||
|
||||
SrsSrtSourceManager::SrsSrtSourceManager()
|
||||
{
|
||||
lock = srs_mutex_new();
|
||||
}
|
||||
|
||||
SrsSrtSourceManager::~SrsSrtSourceManager()
|
||||
{
|
||||
srs_mutex_destroy(lock);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSourceManager::fetch_or_create(SrsRequest* r, SrsSrtSource** pps)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Use lock to protect coroutine switch.
|
||||
// @bug https://github.com/ossrs/srs/issues/1230
|
||||
SrsLocker(lock);
|
||||
|
||||
SrsSrtSource* source = NULL;
|
||||
if ((source = fetch(r)) != NULL) {
|
||||
// we always update the request of resource,
|
||||
// for origin auth is on, the token in request maybe invalid,
|
||||
// and we only need to update the token of request, it's simple.
|
||||
source->update_auth(r);
|
||||
*pps = source;
|
||||
return err;
|
||||
}
|
||||
|
||||
string stream_url = r->get_stream_url();
|
||||
string vhost = r->vhost;
|
||||
|
||||
// should always not exists for create a source.
|
||||
srs_assert (pool.find(stream_url) == pool.end());
|
||||
|
||||
srs_trace("new ts source, stream_url=%s", stream_url.c_str());
|
||||
|
||||
source = new SrsSrtSource();
|
||||
if ((err = source->initialize(r)) != srs_success) {
|
||||
return srs_error_wrap(err, "init source %s", r->get_stream_url().c_str());
|
||||
}
|
||||
|
||||
pool[stream_url] = source;
|
||||
|
||||
*pps = source;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsSrtSource* SrsSrtSourceManager::fetch(SrsRequest* r)
|
||||
{
|
||||
SrsSrtSource* source = NULL;
|
||||
|
||||
string stream_url = r->get_stream_url();
|
||||
if (pool.find(stream_url) == pool.end()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
source = pool[stream_url];
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
SrsSrtSourceManager* _srs_srt_sources = NULL;
|
||||
|
||||
SrsSrtConsumer::SrsSrtConsumer(SrsSrtSource* s)
|
||||
{
|
||||
source = s;
|
||||
should_update_source_id = false;
|
||||
|
||||
mw_wait = srs_cond_new();
|
||||
mw_min_msgs = 0;
|
||||
mw_waiting = false;
|
||||
}
|
||||
|
||||
SrsSrtConsumer::~SrsSrtConsumer()
|
||||
{
|
||||
source->on_consumer_destroy(this);
|
||||
|
||||
vector<SrsSrtPacket*>::iterator it;
|
||||
for (it = queue.begin(); it != queue.end(); ++it) {
|
||||
SrsSrtPacket* pkt = *it;
|
||||
srs_freep(pkt);
|
||||
}
|
||||
|
||||
srs_cond_destroy(mw_wait);
|
||||
}
|
||||
|
||||
void SrsSrtConsumer::update_source_id()
|
||||
{
|
||||
should_update_source_id = true;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtConsumer::enqueue(SrsSrtPacket* packet)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
queue.push_back(packet);
|
||||
|
||||
if (mw_waiting) {
|
||||
if ((int)queue.size() > mw_min_msgs) {
|
||||
srs_cond_signal(mw_wait);
|
||||
mw_waiting = false;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtConsumer::dump_packet(SrsSrtPacket** ppkt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (should_update_source_id) {
|
||||
srs_trace("update source_id=%s/%s", source->source_id().c_str(), source->pre_source_id().c_str());
|
||||
should_update_source_id = false;
|
||||
}
|
||||
|
||||
// TODO: FIXME: Refine performance by ring buffer.
|
||||
if (!queue.empty()) {
|
||||
*ppkt = queue.front();
|
||||
queue.erase(queue.begin());
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsSrtConsumer::wait(int nb_msgs)
|
||||
{
|
||||
mw_min_msgs = nb_msgs;
|
||||
|
||||
// when duration ok, signal to flush.
|
||||
if ((int)queue.size() > mw_min_msgs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the enqueue will notify this cond.
|
||||
mw_waiting = true;
|
||||
|
||||
// use cond block wait for high performance mode.
|
||||
srs_cond_wait(mw_wait);
|
||||
}
|
||||
|
||||
ISrsTsSourceBridger::ISrsTsSourceBridger()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsTsSourceBridger::~ISrsTsSourceBridger()
|
||||
{
|
||||
}
|
||||
|
||||
SrsRtmpFromTsBridge::SrsRtmpFromTsBridge(SrsLiveSource* source)
|
||||
{
|
||||
ts_ctx_ = new SrsTsContext();
|
||||
|
||||
sps_pps_change_ = false;
|
||||
sps_ = "";
|
||||
pps_ = "";
|
||||
|
||||
live_source_ = source;
|
||||
req_ = NULL;
|
||||
}
|
||||
|
||||
SrsRtmpFromTsBridge::~SrsRtmpFromTsBridge()
|
||||
{
|
||||
srs_freep(ts_ctx_);
|
||||
srs_freep(req_);
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpFromTsBridge::on_publish()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = live_source_->on_publish()) != srs_success) {
|
||||
return srs_error_wrap(err, "on publish");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpFromTsBridge::on_packet(SrsSrtPacket *pkt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
char* buf = pkt->data();
|
||||
int nb_buf = pkt->size();
|
||||
|
||||
// use stream to parse ts packet.
|
||||
int nb_packet = nb_buf / SRS_TS_PACKET_SIZE;
|
||||
for (int i = 0; i < nb_packet; i++) {
|
||||
char* p = buf + (i * SRS_TS_PACKET_SIZE);
|
||||
|
||||
SrsBuffer* stream = new SrsBuffer(p, SRS_TS_PACKET_SIZE);
|
||||
SrsAutoFree(SrsBuffer, stream);
|
||||
|
||||
// process each ts packet
|
||||
if ((err = ts_ctx_->decode(stream, this)) != srs_success) {
|
||||
srs_warn("parse ts packet err=%s", srs_error_desc(err).c_str());
|
||||
srs_error_reset(err);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsRtmpFromTsBridge::on_unpublish()
|
||||
{
|
||||
live_source_->on_unpublish();
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpFromTsBridge::initialize(SrsRequest* req)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// TODO: FIXME: check srt2rtmp enable in config.
|
||||
req_ = req->copy();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpFromTsBridge::on_ts_message(SrsTsMessage* msg)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// When the audio SID is private stream 1, we use common audio.
|
||||
// @see https://github.com/ossrs/srs/issues/740
|
||||
if (msg->channel->apply == SrsTsPidApplyAudio && msg->sid == SrsTsPESStreamIdPrivateStream1) {
|
||||
msg->sid = SrsTsPESStreamIdAudioCommon;
|
||||
}
|
||||
|
||||
// when not audio/video, or not adts/annexb format, donot support.
|
||||
if (msg->stream_number() != 0) {
|
||||
return srs_error_new(ERROR_STREAM_CASTER_TS_ES, "ts: unsupported stream format, sid=%#x(%s-%d)",
|
||||
msg->sid, msg->is_audio()? "A":msg->is_video()? "V":"N", msg->stream_number());
|
||||
}
|
||||
|
||||
// check supported codec
|
||||
if (msg->channel->stream != SrsTsStreamVideoH264 && msg->channel->stream != SrsTsStreamAudioAAC) {
|
||||
return srs_error_new(ERROR_STREAM_CASTER_TS_CODEC, "ts: unsupported stream codec=%d", msg->channel->stream);
|
||||
}
|
||||
|
||||
// parse the stream.
|
||||
SrsBuffer avs(msg->payload->bytes(), msg->payload->length());
|
||||
|
||||
// publish audio or video.
|
||||
if (msg->channel->stream == SrsTsStreamVideoH264) {
|
||||
if ((err = on_ts_video(msg, &avs)) != srs_success) {
|
||||
return srs_error_wrap(err, "ts: consume video");
|
||||
}
|
||||
}
|
||||
if (msg->channel->stream == SrsTsStreamAudioAAC) {
|
||||
if ((err = on_ts_audio(msg, &avs)) != srs_success) {
|
||||
return srs_error_wrap(err, "ts: consume audio");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: FIXME: implements other codec?
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpFromTsBridge::on_ts_video(SrsTsMessage* msg, SrsBuffer* avs)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
vector<pair<char*, int> > ipb_frames;
|
||||
|
||||
SrsRawH264Stream* avc = new SrsRawH264Stream();
|
||||
SrsAutoFree(SrsRawH264Stream, avc);
|
||||
|
||||
// send each frame.
|
||||
while (!avs->empty()) {
|
||||
char* frame = NULL;
|
||||
int frame_size = 0;
|
||||
if ((err = avc->annexb_demux(avs, &frame, &frame_size)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux annexb");
|
||||
}
|
||||
|
||||
// 5bits, 7.3.1 NAL unit syntax,
|
||||
// ISO_IEC_14496-10-AVC-2003.pdf, page 44.
|
||||
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
|
||||
SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(frame[0] & 0x1f);
|
||||
|
||||
// ignore the nalu type sps(7), pps(8), aud(9)
|
||||
if (nal_unit_type == SrsAvcNaluTypeAccessUnitDelimiter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// for sps
|
||||
if (avc->is_sps(frame, frame_size)) {
|
||||
std::string sps;
|
||||
if ((err = avc->sps_demux(frame, frame_size, sps)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux sps");
|
||||
}
|
||||
|
||||
if (! sps.empty() && sps_ != sps) {
|
||||
sps_pps_change_ = true;
|
||||
}
|
||||
|
||||
sps_ = sps;
|
||||
continue;
|
||||
}
|
||||
|
||||
// for pps
|
||||
if (avc->is_pps(frame, frame_size)) {
|
||||
std::string pps;
|
||||
if ((err = avc->pps_demux(frame, frame_size, pps)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux pps");
|
||||
}
|
||||
|
||||
if (! pps.empty() && pps_ != pps) {
|
||||
sps_pps_change_ = true;
|
||||
}
|
||||
|
||||
pps_ = pps;
|
||||
continue;
|
||||
}
|
||||
|
||||
ipb_frames.push_back(make_pair(frame, frame_size));
|
||||
}
|
||||
|
||||
if ((err = check_sps_pps_change(msg)) != srs_success) {
|
||||
return srs_error_wrap(err, "check sps pps");
|
||||
}
|
||||
|
||||
return on_h264_frame(msg, ipb_frames);
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpFromTsBridge::check_sps_pps_change(SrsTsMessage* msg)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (! sps_pps_change_) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// sps/pps changed, generate new video sh frame and dispatch it.
|
||||
sps_pps_change_ = false;
|
||||
|
||||
// ts tbn to flv tbn.
|
||||
uint32_t dts = (uint32_t)(msg->dts / 90);
|
||||
|
||||
//type_codec1 + avc_type + composition time + fix header + count of sps + len of sps + sps + count of pps + len of pps + pps
|
||||
int nb_payload = 1 + 1 + 3 + 5 + 1 + 2 + sps_.size() + 1 + 2 + pps_.size();
|
||||
SrsCommonMessage rtmp;
|
||||
rtmp.header.initialize_video(nb_payload, dts, 1);
|
||||
rtmp.create_payload(nb_payload);
|
||||
rtmp.size = nb_payload;
|
||||
SrsBuffer payload(rtmp.payload, rtmp.size);
|
||||
//TODO: call api
|
||||
payload.write_1bytes(0x17);// type(4 bits): key frame; code(4bits): avc
|
||||
payload.write_1bytes(0x0); // avc_type: sequence header
|
||||
payload.write_1bytes(0x0); // composition time
|
||||
payload.write_1bytes(0x0);
|
||||
payload.write_1bytes(0x0);
|
||||
payload.write_1bytes(0x01); // version
|
||||
payload.write_1bytes(sps_[1]);
|
||||
payload.write_1bytes(sps_[2]);
|
||||
payload.write_1bytes(sps_[3]);
|
||||
payload.write_1bytes(0xff);
|
||||
payload.write_1bytes(0xe1);
|
||||
payload.write_2bytes(sps_.size());
|
||||
payload.write_bytes((char*)sps_.data(), sps_.size());
|
||||
payload.write_1bytes(0x01);
|
||||
payload.write_2bytes(pps_.size());
|
||||
payload.write_bytes((char*)pps_.data(), pps_.size());
|
||||
if ((err = live_source_->on_video(&rtmp)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt to rtmp sps/pps");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpFromTsBridge::on_h264_frame(SrsTsMessage* msg, vector<pair<char*, int> >& ipb_frames)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (ipb_frames.empty()) {
|
||||
return srs_error_new(ERROR_SRT_CONN, "empty frame");
|
||||
}
|
||||
|
||||
bool is_keyframe = false;
|
||||
|
||||
// ts tbn to flv tbn.
|
||||
uint32_t dts = (uint32_t)(msg->dts / 90);
|
||||
uint32_t pts = (uint32_t)(msg->pts / 90);
|
||||
int32_t cts = pts - dts;
|
||||
|
||||
int frame_size = 5; // 5bytes video tag header
|
||||
for (size_t i = 0; i != ipb_frames.size(); ++i) {
|
||||
// 4 bytes for nalu length.
|
||||
frame_size += 4 + ipb_frames[i].second;
|
||||
if (((SrsAvcNaluType)(ipb_frames[i].first[0] & 0x1f)) == SrsAvcNaluTypeIDR) {
|
||||
is_keyframe = true;
|
||||
}
|
||||
}
|
||||
|
||||
SrsCommonMessage rtmp;
|
||||
rtmp.header.initialize_video(frame_size, dts, 1/*streamid*/);
|
||||
rtmp.create_payload(frame_size);
|
||||
rtmp.size = frame_size;
|
||||
SrsBuffer payload(rtmp.payload, rtmp.size);
|
||||
// Write 5bytes video tag header.
|
||||
if (is_keyframe) {
|
||||
payload.write_1bytes(0x17); // type(4 bits): key frame; code(4bits): avc
|
||||
} else {
|
||||
payload.write_1bytes(0x27); // type(4 bits): inter frame; code(4bits): avc
|
||||
}
|
||||
payload.write_1bytes(0x01); // avc_type: nalu
|
||||
payload.write_3bytes(cts); // composition time
|
||||
|
||||
// Write video nalus.
|
||||
for (size_t i = 0; i != ipb_frames.size(); ++i) {
|
||||
char* nal = ipb_frames[i].first;
|
||||
int nal_size = ipb_frames[i].second;
|
||||
|
||||
// write 4 bytes of nalu length.
|
||||
payload.write_4bytes(nal_size);
|
||||
// write nalu
|
||||
payload.write_bytes(nal, nal_size);
|
||||
}
|
||||
|
||||
if ((err = live_source_->on_video(&rtmp)) != srs_success) {
|
||||
return srs_error_wrap(err ,"srt ts video to rtmp");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpFromTsBridge::on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRawAacStream* aac = new SrsRawAacStream();
|
||||
SrsAutoFree(SrsRawAacStream, aac);
|
||||
|
||||
// ts tbn to flv tbn.
|
||||
uint32_t pts = (uint32_t)(msg->pts / 90);
|
||||
|
||||
int frame_idx = 0;
|
||||
|
||||
// send each frame.
|
||||
while (!avs->empty()) {
|
||||
char* frame = NULL;
|
||||
int frame_size = 0;
|
||||
SrsRawAacStreamCodec codec;
|
||||
if ((err = aac->adts_demux(avs, &frame, &frame_size, codec)) != srs_success) {
|
||||
return srs_error_wrap(err, "demux adts");
|
||||
}
|
||||
|
||||
// ignore invalid frame,
|
||||
// * atleast 1bytes for aac to decode the data.
|
||||
if (frame_size <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string sh;
|
||||
if ((err = aac->mux_sequence_header(&codec, sh)) != srs_success) {
|
||||
return srs_error_wrap(err, "mux sequence header");
|
||||
}
|
||||
|
||||
if (! sh.empty() && sh != audio_sh_) {
|
||||
audio_sh_ = sh;
|
||||
audio_sh_change_ = true;
|
||||
}
|
||||
|
||||
// May have more than one aac frame in PES packet, and shared same timestamp,
|
||||
// so we must calculate each aac frame's timestamp.
|
||||
int sample_rate = 44100;
|
||||
switch (codec.sound_rate) {
|
||||
case SrsAudioSampleRate5512: sample_rate = 5512; break;
|
||||
case SrsAudioSampleRate11025: sample_rate = 11025; break;
|
||||
case SrsAudioSampleRate22050: sample_rate = 22050; break;
|
||||
case SrsAudioSampleRate44100:
|
||||
default: sample_rate = 44100; break;
|
||||
}
|
||||
uint32_t frame_pts = (double)pts + (frame_idx * (1024.0 * 1000.0 / sample_rate));
|
||||
++frame_idx;
|
||||
|
||||
if ((err = check_audio_sh_change(msg, frame_pts)) != srs_success) {
|
||||
return srs_error_wrap(err, "audio sh");
|
||||
}
|
||||
|
||||
if ((err = on_aac_frame(msg, frame_pts, frame, frame_size)) != srs_success) {
|
||||
return srs_error_wrap(err, "audio frame");
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpFromTsBridge::check_audio_sh_change(SrsTsMessage* msg, uint32_t pts)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (! audio_sh_change_) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// audio specific config changed, generate new audio sh and dispatch it.
|
||||
audio_sh_change_ = false;
|
||||
|
||||
int rtmp_len = audio_sh_.size() + 2;
|
||||
|
||||
SrsCommonMessage rtmp;
|
||||
rtmp.header.initialize_audio(rtmp_len, pts, 1);
|
||||
rtmp.create_payload(rtmp_len);
|
||||
rtmp.size = rtmp_len;
|
||||
|
||||
SrsBuffer stream(rtmp.payload, rtmp_len);
|
||||
uint8_t aac_flag = (SrsAudioCodecIdAAC << 4) | (SrsAudioSampleRate44100 << 2) | (SrsAudioSampleBits16bit << 1) | SrsAudioChannelsStereo;
|
||||
stream.write_1bytes(aac_flag);
|
||||
stream.write_1bytes(0);
|
||||
stream.write_bytes((char*)audio_sh_.data(), audio_sh_.size());
|
||||
|
||||
if ((err = live_source_->on_audio(&rtmp)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt to rtmp audio sh");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtmpFromTsBridge::on_aac_frame(SrsTsMessage* msg, uint32_t pts, char* frame, int frame_size)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
int rtmp_len = frame_size + 2/* 2 bytes of flv audio tag header*/;
|
||||
|
||||
SrsCommonMessage rtmp;
|
||||
rtmp.header.initialize_audio(rtmp_len, pts, 2/*streamid*/);
|
||||
rtmp.create_payload(rtmp_len);
|
||||
rtmp.size = rtmp_len;
|
||||
|
||||
SrsBuffer stream(rtmp.payload, rtmp_len);
|
||||
uint8_t aac_flag = (SrsAudioCodecIdAAC << 4) | (SrsAudioSampleRate44100 << 2) | (SrsAudioSampleBits16bit << 1) | SrsAudioChannelsStereo;
|
||||
// Write 2bytes audio tag header.
|
||||
stream.write_1bytes(aac_flag);
|
||||
stream.write_1bytes(1);
|
||||
// Write audio frame.
|
||||
stream.write_bytes(frame, frame_size);
|
||||
|
||||
if ((err = live_source_->on_audio(&rtmp)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt to rtmp audio sh");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsSrtSource::SrsSrtSource()
|
||||
{
|
||||
req = NULL;
|
||||
can_publish_ = true;
|
||||
bridger_ = NULL;
|
||||
}
|
||||
|
||||
SrsSrtSource::~SrsSrtSource()
|
||||
{
|
||||
// never free the consumers,
|
||||
// for all consumers are auto free.
|
||||
consumers.clear();
|
||||
|
||||
srs_freep(bridger_);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSource::initialize(SrsRequest* r)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
req = r->copy();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSource::on_source_id_changed(SrsContextId id)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (!_source_id.compare(id)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (_pre_source_id.empty()) {
|
||||
_pre_source_id = id;
|
||||
}
|
||||
_source_id = id;
|
||||
|
||||
// notice all consumer
|
||||
std::vector<SrsSrtConsumer*>::iterator it;
|
||||
for (it = consumers.begin(); it != consumers.end(); ++it) {
|
||||
SrsSrtConsumer* consumer = *it;
|
||||
consumer->update_source_id();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsContextId SrsSrtSource::source_id()
|
||||
{
|
||||
return _source_id;
|
||||
}
|
||||
|
||||
SrsContextId SrsSrtSource::pre_source_id()
|
||||
{
|
||||
return _pre_source_id;
|
||||
}
|
||||
|
||||
void SrsSrtSource::update_auth(SrsRequest* r)
|
||||
{
|
||||
req->update_auth(r);
|
||||
}
|
||||
|
||||
void SrsSrtSource::set_bridger(ISrsTsSourceBridger *bridger)
|
||||
{
|
||||
srs_freep(bridger_);
|
||||
bridger_ = bridger;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSource::create_consumer(SrsSrtConsumer*& consumer)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
consumer = new SrsSrtConsumer(this);
|
||||
consumers.push_back(consumer);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSource::consumer_dumps(SrsSrtConsumer* consumer)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// print status.
|
||||
srs_trace("create ts consumer, no gop cache");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsSrtSource::on_consumer_destroy(SrsSrtConsumer* consumer)
|
||||
{
|
||||
std::vector<SrsSrtConsumer*>::iterator it;
|
||||
it = std::find(consumers.begin(), consumers.end(), consumer);
|
||||
if (it != consumers.end()) {
|
||||
consumers.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
bool SrsSrtSource::can_publish()
|
||||
{
|
||||
return can_publish_;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSource::on_publish()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
can_publish_ = false;
|
||||
|
||||
if ((err = on_source_id_changed(_srs_context->get_id())) != srs_success) {
|
||||
return srs_error_wrap(err, "source id change");
|
||||
}
|
||||
|
||||
if (bridger_) {
|
||||
if ((err = bridger_->on_publish()) != srs_success) {
|
||||
return srs_error_wrap(err, "bridger on publish");
|
||||
}
|
||||
}
|
||||
|
||||
SrsStatistic* stat = SrsStatistic::instance();
|
||||
stat->on_stream_publish(req, _source_id.c_str());
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsSrtSource::on_unpublish()
|
||||
{
|
||||
// ignore when already unpublished.
|
||||
if (can_publish_) {
|
||||
return;
|
||||
}
|
||||
|
||||
can_publish_ = true;
|
||||
|
||||
if (bridger_) {
|
||||
bridger_->on_unpublish();
|
||||
srs_freep(bridger_);
|
||||
}
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSource::on_packet(SrsSrtPacket* packet)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
for (int i = 0; i < (int)consumers.size(); i++) {
|
||||
SrsSrtConsumer* consumer = consumers.at(i);
|
||||
if ((err = consumer->enqueue(packet->copy())) != srs_success) {
|
||||
return srs_error_wrap(err, "consume ts packet");
|
||||
}
|
||||
}
|
||||
|
||||
if (bridger_ && (err = bridger_->on_packet(packet)) != srs_success) {
|
||||
return srs_error_wrap(err, "bridger consume message");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
205
trunk/src/app/srs_app_srt_source.hpp
Normal file
205
trunk/src/app/srs_app_srt_source.hpp
Normal file
|
@ -0,0 +1,205 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#ifndef SRS_APP_SRT_SOURCE_HPP
|
||||
#define SRS_APP_SRT_SOURCE_HPP
|
||||
|
||||
#include <srs_core.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <srs_kernel_ts.hpp>
|
||||
#include <srs_service_st.hpp>
|
||||
|
||||
class SrsSharedPtrMessage;
|
||||
class SrsRequest;
|
||||
class SrsLiveSource;
|
||||
class SrsSrtSource;
|
||||
|
||||
// The SRT packet with shared message.
|
||||
class SrsSrtPacket
|
||||
{
|
||||
public:
|
||||
SrsSrtPacket();
|
||||
virtual ~SrsSrtPacket();
|
||||
public:
|
||||
// Wrap buffer to shared_message, which is managed by us.
|
||||
char* wrap(int size);
|
||||
char* wrap(char* data, int size);
|
||||
// Wrap the shared message, we copy it.
|
||||
char* wrap(SrsSharedPtrMessage* msg);
|
||||
// Copy the SRT packet.
|
||||
virtual SrsSrtPacket* copy();
|
||||
public:
|
||||
char* data();
|
||||
int size();
|
||||
private:
|
||||
SrsSharedPtrMessage* shared_buffer_;
|
||||
// The size of SRT packet or SRT payload.
|
||||
int actual_buffer_size_;
|
||||
};
|
||||
|
||||
class SrsSrtSourceManager
|
||||
{
|
||||
private:
|
||||
srs_mutex_t lock;
|
||||
std::map<std::string, SrsSrtSource*> pool;
|
||||
public:
|
||||
SrsSrtSourceManager();
|
||||
virtual ~SrsSrtSourceManager();
|
||||
public:
|
||||
// create source when fetch from cache failed.
|
||||
// @param r the client request.
|
||||
// @param pps the matched source, if success never be NULL.
|
||||
virtual srs_error_t fetch_or_create(SrsRequest* r, SrsSrtSource** pps);
|
||||
public:
|
||||
// Get the exists source, NULL when not exists.
|
||||
virtual SrsSrtSource* fetch(SrsRequest* r);
|
||||
};
|
||||
|
||||
// Global singleton instance.
|
||||
extern SrsSrtSourceManager* _srs_srt_sources;
|
||||
|
||||
class SrsSrtConsumer
|
||||
{
|
||||
public:
|
||||
SrsSrtConsumer(SrsSrtSource* source);
|
||||
virtual ~SrsSrtConsumer();
|
||||
private:
|
||||
SrsSrtSource* source;
|
||||
std::vector<SrsSrtPacket*> queue;
|
||||
// when source id changed, notice all consumers
|
||||
bool should_update_source_id;
|
||||
// The cond wait for mw.
|
||||
srs_cond_t mw_wait;
|
||||
bool mw_waiting;
|
||||
int mw_min_msgs;
|
||||
public:
|
||||
// When source id changed, notice client to print.
|
||||
void update_source_id();
|
||||
// Put SRT packet into queue.
|
||||
srs_error_t enqueue(SrsSrtPacket* packet);
|
||||
// For SRT, we only got one packet, because there is not many packets in queue.
|
||||
virtual srs_error_t dump_packet(SrsSrtPacket** ppkt);
|
||||
// Wait for at-least some messages incoming in queue.
|
||||
virtual void wait(int nb_msgs);
|
||||
};
|
||||
|
||||
class ISrsTsSourceBridger
|
||||
{
|
||||
public:
|
||||
ISrsTsSourceBridger();
|
||||
virtual ~ISrsTsSourceBridger();
|
||||
public:
|
||||
virtual srs_error_t on_publish() = 0;
|
||||
virtual srs_error_t on_packet(SrsSrtPacket *pkt) = 0;
|
||||
virtual void on_unpublish() = 0;
|
||||
};
|
||||
|
||||
class SrsRtmpFromTsBridge : public ISrsTsSourceBridger, public ISrsTsHandler
|
||||
{
|
||||
public:
|
||||
SrsRtmpFromTsBridge(SrsLiveSource* source);
|
||||
virtual ~SrsRtmpFromTsBridge();
|
||||
public:
|
||||
virtual srs_error_t on_publish();
|
||||
virtual srs_error_t on_packet(SrsSrtPacket *pkt);
|
||||
virtual void on_unpublish();
|
||||
public:
|
||||
srs_error_t initialize(SrsRequest* req);
|
||||
// Interface ISrsTsHandler
|
||||
public:
|
||||
virtual srs_error_t on_ts_message(SrsTsMessage* msg);
|
||||
private:
|
||||
srs_error_t on_ts_video(SrsTsMessage* msg, SrsBuffer* avs);
|
||||
srs_error_t on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs);
|
||||
srs_error_t check_sps_pps_change(SrsTsMessage* msg);
|
||||
srs_error_t on_h264_frame(SrsTsMessage* msg, std::vector<std::pair<char*, int> >& ipb_frames);
|
||||
srs_error_t check_audio_sh_change(SrsTsMessage* msg, uint32_t pts);
|
||||
srs_error_t on_aac_frame(SrsTsMessage* msg, uint32_t pts, char* frame, int frame_size);
|
||||
private:
|
||||
SrsTsContext* ts_ctx_;
|
||||
|
||||
// Record sps/pps had changed, if change, need to generate new video sh frame.
|
||||
bool sps_pps_change_;
|
||||
std::string sps_;
|
||||
std::string pps_;
|
||||
|
||||
// Record audio sepcific config had changed, if change, need to generate new audio sh frame.
|
||||
bool audio_sh_change_;
|
||||
std::string audio_sh_;
|
||||
|
||||
SrsRequest* req_;
|
||||
SrsLiveSource* live_source_;
|
||||
};
|
||||
|
||||
class SrsSrtSource
|
||||
{
|
||||
public:
|
||||
SrsSrtSource();
|
||||
virtual ~SrsSrtSource();
|
||||
public:
|
||||
virtual srs_error_t initialize(SrsRequest* r);
|
||||
public:
|
||||
// The source id changed.
|
||||
virtual srs_error_t on_source_id_changed(SrsContextId id);
|
||||
// Get current source id.
|
||||
virtual SrsContextId source_id();
|
||||
virtual SrsContextId pre_source_id();
|
||||
// Update the authentication information in request.
|
||||
virtual void update_auth(SrsRequest* r);
|
||||
public:
|
||||
void set_bridger(ISrsTsSourceBridger *bridger);
|
||||
public:
|
||||
// Create consumer
|
||||
// @param consumer, output the create consumer.
|
||||
virtual srs_error_t create_consumer(SrsSrtConsumer*& consumer);
|
||||
// Dumps packets in cache to consumer.
|
||||
virtual srs_error_t consumer_dumps(SrsSrtConsumer* consumer);
|
||||
virtual void on_consumer_destroy(SrsSrtConsumer* consumer);
|
||||
// Whether we can publish stream to the source, return false if it exists.
|
||||
virtual bool can_publish();
|
||||
// When start publish stream.
|
||||
virtual srs_error_t on_publish();
|
||||
// When stop publish stream.
|
||||
virtual void on_unpublish();
|
||||
public:
|
||||
srs_error_t on_packet(SrsSrtPacket* packet);
|
||||
private:
|
||||
// Source id.
|
||||
SrsContextId _source_id;
|
||||
// previous source id.
|
||||
SrsContextId _pre_source_id;
|
||||
SrsRequest* req;
|
||||
// To delivery packets to clients.
|
||||
std::vector<SrsSrtConsumer*> consumers;
|
||||
bool can_publish_;
|
||||
ISrsTsSourceBridger* bridger_;
|
||||
};
|
||||
|
||||
/*
|
||||
class SrsTsFromRtmpBridger : public ISrsLiveSourceBridger
|
||||
{
|
||||
private:
|
||||
SrsRequest* req;
|
||||
SrsSrtSource* source_;
|
||||
public:
|
||||
SrsTsFromRtmpBridger(SrsSrtSource* source);
|
||||
virtual ~SrsTsFromRtmpBridger();
|
||||
public:
|
||||
virtual srs_error_t initialize(SrsRequest* r);
|
||||
// Interface for ISrsLiveSourceBridger
|
||||
public:
|
||||
virtual srs_error_t on_publish();
|
||||
virtual void on_unpublish();
|
||||
virtual srs_error_t on_audio(SrsSharedPtrMessage* msg);
|
||||
virtual srs_error_t on_video(SrsSharedPtrMessage* msg);
|
||||
};
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
143
trunk/src/app/srs_app_srt_utility.cpp
Normal file
143
trunk/src/app/srs_app_srt_utility.cpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_app_srt_utility.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_protocol_utility.hpp>
|
||||
#include <srs_rtmp_stack.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
|
||||
// See streamid of https://github.com/ossrs/srs/issues/2893
|
||||
// TODO: FIMXE: We should parse SRT streamid to URL object, rather than a HTTP url subpath.
|
||||
bool srs_srt_streamid_info(const std::string& streamid, SrtMode& mode, std::string& vhost, std::string& url_subpath)
|
||||
{
|
||||
mode = SrtModePull;
|
||||
|
||||
size_t pos = streamid.find("#!::");
|
||||
if (pos != 0) {
|
||||
pos = streamid.find("/");
|
||||
if (pos == streamid.npos) {
|
||||
url_subpath = _srs_config->get_default_app_name() + "/" + streamid;
|
||||
return true;
|
||||
}
|
||||
url_subpath = streamid;
|
||||
return true;
|
||||
}
|
||||
|
||||
//SRT url supports multiple QueryStrings, which are passed to RTMP to realize authentication and other capabilities
|
||||
//@see https://github.com/ossrs/srs/issues/2893
|
||||
std::string params;
|
||||
std::string real_streamid;
|
||||
real_streamid = streamid.substr(4);
|
||||
|
||||
// Compatible with previous auth querystring, like this one:
|
||||
// srt://127.0.0.1:10080?streamid=#!::h=live/livestream?secret=xxx,m=publish
|
||||
real_streamid = srs_string_replace(real_streamid, "?", ",");
|
||||
|
||||
std::map<std::string, std::string> query;
|
||||
srs_parse_query_string(real_streamid, query);
|
||||
for (std::map<std::string, std::string>::iterator it = query.begin(); it != query.end(); ++it) {
|
||||
if (it->first == "h") {
|
||||
std::string host = it->second;
|
||||
|
||||
size_t r0 = host.find("/");
|
||||
size_t r1 = host.rfind("/");
|
||||
if (r0 != std::string::npos && r0 != std::string::npos) {
|
||||
// Compatible with previous style, see https://github.com/ossrs/srs/issues/2893#compatible
|
||||
// srt://127.0.0.1:10080?streamid=#!::h=live/livestream,m=publish
|
||||
// srt://127.0.0.1:10080?streamid=#!::h=live/livestream,m=request
|
||||
// srt://127.0.0.1:10080?streamid=#!::h=srs.srt.com.cn/live/livestream,m=publish
|
||||
if (r0 != r1) {
|
||||
// We got vhost in host.
|
||||
url_subpath = host.substr(r0 + 1);
|
||||
host = host.substr(0, r0);
|
||||
|
||||
params.append("vhost=");
|
||||
params.append(host);
|
||||
params.append("&");
|
||||
vhost = host;
|
||||
} else {
|
||||
// Only stream in host.
|
||||
url_subpath = host;
|
||||
}
|
||||
} else {
|
||||
// New URL style, see https://github.com/ossrs/srs/issues/2893#solution
|
||||
// srt://host.com:10080?streamid=#!::h=host.com,r=app/stream,key1=value1,key2=value2
|
||||
// srt://1.2.3.4:10080?streamid=#!::h=host.com,r=app/stream,key1=value1,key2=value2
|
||||
// srt://1.2.3.4:10080?streamid=#!::r=app/stream,key1=value1,key2=value2
|
||||
params.append("vhost=");
|
||||
params.append(host);
|
||||
params.append("&");
|
||||
vhost = host;
|
||||
}
|
||||
} else if (it->first == "r") {
|
||||
url_subpath = it->second;
|
||||
} else if (it->first == "m") {
|
||||
std::string mode_str = it->second; // support m=publish or m=request
|
||||
std::transform(it->second.begin(), it->second.end(), mode_str.begin(), ::tolower);
|
||||
if (mode_str == "publish") {
|
||||
mode = SrtModePush;
|
||||
} else if (mode_str == "request") {
|
||||
mode = SrtModePull;
|
||||
} else {
|
||||
srs_warn("unknown mode_str:%s", mode_str.c_str());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
params.append(it->first);
|
||||
params.append("=");
|
||||
params.append(it->second);
|
||||
params.append("&");
|
||||
}
|
||||
}
|
||||
|
||||
if (url_subpath.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!params.empty()) {
|
||||
url_subpath.append("?");
|
||||
url_subpath.append(params);
|
||||
url_subpath.pop_back(); // remove last '&'
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool srs_srt_streamid_to_request(const std::string& streamid, SrtMode& mode, SrsRequest* request)
|
||||
{
|
||||
string url_subpath = "";
|
||||
bool ret = srs_srt_streamid_info(streamid, mode, request->vhost, url_subpath);
|
||||
if (! ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t pos = url_subpath.find("/");
|
||||
string stream_with_params = "";
|
||||
if (pos == string::npos) {
|
||||
request->app = _srs_config->get_default_app_name();
|
||||
stream_with_params = url_subpath;
|
||||
} else {
|
||||
request->app = url_subpath.substr(0, pos);
|
||||
stream_with_params = url_subpath.substr(pos + 1);
|
||||
}
|
||||
|
||||
pos = stream_with_params.find("?");
|
||||
if (pos == string::npos) {
|
||||
request->stream = stream_with_params;
|
||||
} else {
|
||||
request->stream = stream_with_params.substr(0, pos);
|
||||
request->param = stream_with_params.substr(pos + 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
32
trunk/src/app/srs_app_srt_utility.hpp
Normal file
32
trunk/src/app/srs_app_srt_utility.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#ifndef SRS_APP_SRT_UTILITY_HPP
|
||||
#define SRS_APP_SRT_UTILITY_HPP
|
||||
|
||||
#include <srs_core.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_service_utility.hpp>
|
||||
|
||||
class SrsRequest;
|
||||
|
||||
enum SrtMode
|
||||
{
|
||||
SrtModePull = 1,
|
||||
SrtModePush = 2,
|
||||
};
|
||||
|
||||
// Get SRT streamid info.
|
||||
extern bool srs_srt_streamid_info(const std::string& streamid, SrtMode& mode, std::string& vhost, std::string& url_subpath);
|
||||
|
||||
// SRT streamid to request.
|
||||
extern bool srs_srt_streamid_to_request(const std::string& streamid, SrtMode& mode, SrsRequest* request);
|
||||
|
||||
#endif
|
||||
|
|
@ -22,6 +22,10 @@
|
|||
#include <srs_app_rtc_conn.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef SRS_SRT
|
||||
#include <srs_app_srt_source.hpp>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
|
@ -298,6 +302,10 @@ srs_error_t srs_thread_initialize()
|
|||
_srs_stages = new SrsStageManager();
|
||||
_srs_circuit_breaker = new SrsCircuitBreaker();
|
||||
|
||||
#ifdef SRS_SRT
|
||||
_srs_srt_sources = new SrsSrtSourceManager();
|
||||
#endif
|
||||
|
||||
#ifdef SRS_RTC
|
||||
_srs_rtc_sources = new SrsRtcSourceManager();
|
||||
_srs_blackhole = new SrsRtcBlackhole();
|
||||
|
|
|
@ -170,6 +170,10 @@
|
|||
#define SRS_CONSTS_LOG_EXEC "EXE"
|
||||
// The rtc.
|
||||
#define SRS_CONSTS_LOG_RTC "RTC"
|
||||
// Srt client play
|
||||
#define SRS_CONSTS_LOG_SRT_PLAY "SRT_PLA"
|
||||
// Srt client publish
|
||||
#define SRS_CONSTS_LOG_SRT_PUBLISH "SRT_CPB"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////
|
||||
|
|
|
@ -347,6 +347,18 @@
|
|||
#define ERROR_RTC_NO_TRACK 5030
|
||||
#define ERROR_RTC_RTCP_EMPTY_RR 5031
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// SRT protocol error.
|
||||
///////////////////////////////////////////////////////
|
||||
#define ERROR_SRT_EPOLL 6000
|
||||
#define ERROR_SRT_IO 6001
|
||||
#define ERROR_SRT_TIMEOUT 6002
|
||||
#define ERROR_SRT_INTERRUPT 6003
|
||||
#define ERROR_SRT_LISTEN 6004
|
||||
#define ERROR_SRT_SOCKOPT 6005
|
||||
#define ERROR_SRT_CONN 6006
|
||||
#define ERROR_SRT_SOURCE_BUSY 6007
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// HTTP API error.
|
||||
///////////////////////////////////////////////////////
|
||||
|
|
|
@ -44,7 +44,8 @@ using namespace std;
|
|||
#endif
|
||||
|
||||
#ifdef SRS_SRT
|
||||
#include <srt_server.hpp>
|
||||
#include <srs_service_st_srt.hpp>
|
||||
#include <srs_app_srt_server.hpp>
|
||||
#endif
|
||||
|
||||
// pre-declare
|
||||
|
@ -65,6 +66,10 @@ extern const char* _srs_version;
|
|||
// @global main SRS server, for debugging
|
||||
SrsServer* _srs_server = NULL;
|
||||
|
||||
#ifdef SRS_SRT
|
||||
SrsSrtEventLoop* _srt_eventloop = NULL;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* main entrance.
|
||||
*/
|
||||
|
@ -455,7 +460,14 @@ srs_error_t run_hybrid_server()
|
|||
_srs_hybrid->register_server(new SrsServerAdapter());
|
||||
|
||||
#ifdef SRS_SRT
|
||||
_srs_hybrid->register_server(new SrtServerAdapter());
|
||||
_srt_eventloop = new SrsSrtEventLoop();
|
||||
if ((err = _srt_eventloop->initialize()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt poller initialize");
|
||||
}
|
||||
if ((err = _srt_eventloop->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt poller start");
|
||||
}
|
||||
_srs_hybrid->register_server(new SrsSrtServerAdapter());
|
||||
#endif
|
||||
|
||||
#ifdef SRS_RTC
|
||||
|
|
789
trunk/src/protocol/srs_service_st_srt.cpp
Normal file
789
trunk/src/protocol/srs_service_st_srt.cpp
Normal file
|
@ -0,0 +1,789 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#include <srs_service_st_srt.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_log.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
|
||||
#define SET_SRT_OPT_STR(srtfd, optname, buf, size) \
|
||||
if (srt_setsockflag(srtfd, optname, buf, size) == SRT_ERROR) { \
|
||||
std::stringstream ss; \
|
||||
ss << "srtfd=" << srtfd << ",set " << #optname \
|
||||
<< " failed,err=" << srt_getlasterror_str(); \
|
||||
return srs_error_new(ERROR_SRT_SOCKOPT, "%s", ss.str().c_str()); \
|
||||
}
|
||||
|
||||
#define SET_SRT_OPT(srtfd, optname, val) \
|
||||
if (srt_setsockflag(srtfd, optname, &val, sizeof(val)) == SRT_ERROR) { \
|
||||
std::stringstream ss; \
|
||||
ss << "srtfd=" << srtfd << ",set " << #optname << "=" << val \
|
||||
<< " failed,err=" << srt_getlasterror_str(); \
|
||||
return srs_error_new(ERROR_SRT_SOCKOPT, "%s", ss.str().c_str()); \
|
||||
}
|
||||
|
||||
#define GET_SRT_OPT(srtfd, optname, val) \
|
||||
do { \
|
||||
int size = sizeof(val); \
|
||||
if (srt_getsockflag(srtfd, optname, &val, &size) == SRT_ERROR) { \
|
||||
std::stringstream ss; \
|
||||
ss << "srtfd=" << srtfd << ",get " << #optname \
|
||||
<< " failed,err=" << srt_getlasterror_str(); \
|
||||
return srs_error_new(ERROR_SRT_SOCKOPT, "%s", ss.str().c_str()); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static srs_error_t do_srs_srt_listen(SRTSOCKET srt_fd, addrinfo* r)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = srs_srt_nonblock(srt_fd)) != srs_success) {
|
||||
return srs_error_wrap(err, "nonblock");
|
||||
}
|
||||
|
||||
if (srt_bind(srt_fd, r->ai_addr, r->ai_addrlen) == -1) {
|
||||
return srs_error_new(ERROR_SOCKET_BIND, "bind");
|
||||
}
|
||||
|
||||
if (srt_listen(srt_fd, 100) == -1) {
|
||||
return srs_error_new(ERROR_SOCKET_LISTEN, "listen");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static srs_error_t do_srs_srt_get_streamid(SRTSOCKET srt_fd, string& streamid)
|
||||
{
|
||||
// SRT max streamid length is 512.
|
||||
char sid[512];
|
||||
GET_SRT_OPT(srt_fd, SRTO_STREAMID, sid);
|
||||
|
||||
streamid.assign(sid);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_socket(SRTSOCKET* pfd)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SRTSOCKET srt_fd = 0;
|
||||
if ((srt_fd = srt_create_socket()) < 0) {
|
||||
return srs_error_new(ERROR_SOCKET_CREATE, "create srt socket");
|
||||
}
|
||||
|
||||
*pfd = srt_fd;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_socket_with_default_option(SRTSOCKET* pfd)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SRTSOCKET srt_fd = 0;
|
||||
if ((srt_fd = srt_create_socket()) < 0) {
|
||||
return srs_error_new(ERROR_SOCKET_CREATE, "create srt socket");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_nonblock(srt_fd)) != srs_success) {
|
||||
return srs_error_wrap(err, "nonblock");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_tsbpdmode(srt_fd, false)) != srs_success) {
|
||||
return srs_error_wrap(err, "set tsbpdmode");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_tlpktdrop(srt_fd, false)) != srs_success) {
|
||||
return srs_error_wrap(err, "set tlpktdrop");
|
||||
}
|
||||
|
||||
if ((err = srs_srt_set_latency(srt_fd, false)) != srs_success) {
|
||||
return srs_error_wrap(err, "set latency");
|
||||
}
|
||||
|
||||
*pfd = srt_fd;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_listen(SRTSOCKET srt_fd, std::string ip, int port)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
char sport[8];
|
||||
snprintf(sport, sizeof(sport), "%d", port);
|
||||
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
|
||||
addrinfo* r = NULL;
|
||||
SrsAutoFreeH(addrinfo, r, freeaddrinfo);
|
||||
if(getaddrinfo(ip.c_str(), sport, (const addrinfo*)&hints, &r)) {
|
||||
return srs_error_new(ERROR_SYSTEM_IP_INVALID, "getaddrinfo hints=(%d,%d,%d)",
|
||||
hints.ai_family, hints.ai_socktype, hints.ai_flags);
|
||||
}
|
||||
|
||||
if ((err = do_srs_srt_listen(srt_fd, r)) != srs_success) {
|
||||
srt_close(srt_fd);
|
||||
return srs_error_wrap(err, "srt_fd=%d", srt_fd);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_nonblock(SRTSOCKET srt_fd)
|
||||
{
|
||||
int sync = 0;
|
||||
SET_SRT_OPT(srt_fd, SRTO_SNDSYN, sync);
|
||||
SET_SRT_OPT(srt_fd, SRTO_RCVSYN, sync);
|
||||
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_maxbw(SRTSOCKET srt_fd, int maxbw)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_MAXBW, maxbw);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_mss(SRTSOCKET srt_fd, int mss)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_MSS, mss);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_payload_size(SRTSOCKET srt_fd, int payload_size)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_PAYLOADSIZE, payload_size);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_connect_timeout(SRTSOCKET srt_fd, int timeout)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_CONNTIMEO, timeout);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_peer_idle_timeout(SRTSOCKET srt_fd, int timeout)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_PEERIDLETIMEO, timeout);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_tsbpdmode(SRTSOCKET srt_fd, bool tsbpdmode)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_TSBPDMODE, tsbpdmode);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_sndbuf(SRTSOCKET srt_fd, int sndbuf)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_SNDBUF, sndbuf);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_rcvbuf(SRTSOCKET srt_fd, int rcvbuf)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_RCVBUF, rcvbuf);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_tlpktdrop(SRTSOCKET srt_fd, bool tlpktdrop)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_TLPKTDROP, tlpktdrop);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_latency(SRTSOCKET srt_fd, int latency)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_LATENCY, latency);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_rcv_latency(SRTSOCKET srt_fd, int rcv_latency)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_RCVLATENCY, rcv_latency);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_peer_latency(SRTSOCKET srt_fd, int peer_latency)
|
||||
{
|
||||
SET_SRT_OPT(srt_fd, SRTO_PEERLATENCY, peer_latency);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_set_streamid(SRTSOCKET srt_fd, const std::string& streamid)
|
||||
{
|
||||
SET_SRT_OPT_STR(srt_fd, SRTO_STREAMID, streamid.data(), streamid.size());
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_maxbw(SRTSOCKET srt_fd, int& maxbw)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_MAXBW, maxbw);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_mss(SRTSOCKET srt_fd, int& mss)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_MSS, mss);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_payload_size(SRTSOCKET srt_fd, int& payload_size)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_PAYLOADSIZE, payload_size);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_connect_timeout(SRTSOCKET srt_fd, int& timeout)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_CONNTIMEO, timeout);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_peer_idle_timeout(SRTSOCKET srt_fd, int& timeout)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_PEERIDLETIMEO, timeout);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_tsbpdmode(SRTSOCKET srt_fd, bool& tsbpdmode)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_TSBPDMODE, tsbpdmode);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_sndbuf(SRTSOCKET srt_fd, int& sndbuf)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_SNDBUF, sndbuf);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_rcvbuf(SRTSOCKET srt_fd, int& rcvbuf)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_RCVBUF, rcvbuf);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_tlpktdrop(SRTSOCKET srt_fd, bool& tlpktdrop)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_TLPKTDROP, tlpktdrop);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_latency(SRTSOCKET srt_fd, int& latency)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_LATENCY, latency);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_rcv_latency(SRTSOCKET srt_fd, int& rcv_latency)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_RCVLATENCY, rcv_latency);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_peer_latency(SRTSOCKET srt_fd, int& peer_latency)
|
||||
{
|
||||
GET_SRT_OPT(srt_fd, SRTO_PEERLATENCY, peer_latency);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_streamid(SRTSOCKET srt_fd, std::string& streamid)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = do_srs_srt_get_streamid(srt_fd, streamid)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt get streamid");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_local_ip_port(SRTSOCKET srt_fd, std::string& ip, int& port)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// discovery client information
|
||||
sockaddr_storage addr;
|
||||
int addrlen = sizeof(addr);
|
||||
if (srt_getsockname(srt_fd, (sockaddr*)&addr, &addrlen) == -1) {
|
||||
return srs_error_new(ERROR_SRT_SOCKOPT, "srt_getsockname");
|
||||
}
|
||||
|
||||
char saddr[64];
|
||||
char* h = (char*)saddr;
|
||||
socklen_t nbh = (socklen_t)sizeof(saddr);
|
||||
const int r0 = getnameinfo((const sockaddr*)&addr, addrlen, h, nbh,NULL, 0, NI_NUMERICHOST);
|
||||
if (r0) {
|
||||
return srs_error_new(ERROR_SRT_SOCKOPT, "getnameinfo");
|
||||
}
|
||||
|
||||
switch(addr.ss_family) {
|
||||
case AF_INET:
|
||||
port = ntohs(((sockaddr_in*)&addr)->sin_port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
port = ntohs(((sockaddr_in6*)&addr)->sin6_port);
|
||||
break;
|
||||
}
|
||||
|
||||
ip.assign(saddr);
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t srs_srt_get_remote_ip_port(SRTSOCKET srt_fd, std::string& ip, int& port)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// discovery client information
|
||||
sockaddr_storage addr;
|
||||
int addrlen = sizeof(addr);
|
||||
if (srt_getpeername(srt_fd, (sockaddr*)&addr, &addrlen) == -1) {
|
||||
return srs_error_new(ERROR_SRT_SOCKOPT, "srt_getpeername");
|
||||
}
|
||||
|
||||
char saddr[64];
|
||||
char* h = (char*)saddr;
|
||||
socklen_t nbh = (socklen_t)sizeof(saddr);
|
||||
const int r0 = getnameinfo((const sockaddr*)&addr, addrlen, h, nbh,NULL, 0, NI_NUMERICHOST);
|
||||
if (r0) {
|
||||
return srs_error_new(ERROR_SRT_SOCKOPT, "getnameinfo");
|
||||
}
|
||||
|
||||
switch(addr.ss_family) {
|
||||
case AF_INET:
|
||||
port = ntohs(((sockaddr_in*)&addr)->sin_port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
port = ntohs(((sockaddr_in6*)&addr)->sin6_port);
|
||||
break;
|
||||
}
|
||||
|
||||
ip.assign(saddr);
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsSrtPoller::SrsSrtPoller()
|
||||
{
|
||||
srt_epoller_fd_ = -1;
|
||||
}
|
||||
|
||||
SrsSrtPoller::~SrsSrtPoller()
|
||||
{
|
||||
if (srt_epoller_fd_ > 0) {
|
||||
srt_epoll_release(srt_epoller_fd_);
|
||||
}
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtPoller::initialize()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
srt_epoller_fd_ = srt_epoll_create();
|
||||
events_.resize(1024);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtPoller::add_socket(SrsSrtSocket* srt_skt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
int events = srt_skt->events();
|
||||
SRTSOCKET srtfd = srt_skt->fd();
|
||||
|
||||
int ret = srt_epoll_add_usock(srt_epoller_fd_, srtfd, &events);
|
||||
|
||||
srs_info("srt poller %d add srt socket %d, events=%d", srt_epoller_fd_, srtfd, events);
|
||||
if (ret == SRT_ERROR) {
|
||||
return srs_error_new(ERROR_SRT_EPOLL, "srt epoll add socket=%lu failed, err=%s", srtfd, srt_getlasterror_str());
|
||||
}
|
||||
|
||||
// record srtfd to SrsSrtSocket*
|
||||
fd_sockets_[srtfd] = srt_skt;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtPoller::del_socket(SrsSrtSocket* srt_skt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SRTSOCKET srtfd = srt_skt->fd();
|
||||
|
||||
int ret = srt_epoll_remove_usock(srt_epoller_fd_, srtfd);
|
||||
srs_info("srt poller %d remove srt socket %d", srt_epoller_fd_, srtfd);
|
||||
if (ret == SRT_ERROR) {
|
||||
return srs_error_new(ERROR_SRT_EPOLL, "srt epoll remove socket=%lu failed, err=%s", srtfd, srt_getlasterror_str());
|
||||
}
|
||||
|
||||
fd_sockets_.erase(srtfd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtPoller::wait(int timeout_ms)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// wait srt event fired, will timeout after `timeout_ms` milliseconds.
|
||||
int ret = srt_epoll_uwait(srt_epoller_fd_, events_.data(), events_.size(), timeout_ms);
|
||||
if (ret < 0) {
|
||||
return srs_error_new(ERROR_SRT_EPOLL, "srt_epoll_uwait, ret=%d", ret);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ret; ++i) {
|
||||
SRT_EPOLL_EVENT event = events_[i];
|
||||
map<SRTSOCKET, SrsSrtSocket*>::iterator iter = fd_sockets_.find(event.fd);
|
||||
if (iter == fd_sockets_.end()) {
|
||||
srs_assert(false);
|
||||
}
|
||||
|
||||
SrsSrtSocket* srt_skt = iter->second;
|
||||
srs_assert(srt_skt != NULL);
|
||||
|
||||
// notify error, don't notify read/write event.
|
||||
if (event.events & SRT_EPOLL_ERR) {
|
||||
srt_skt->notify_error();
|
||||
} else {
|
||||
if (event.events & SRT_EPOLL_IN) {
|
||||
srt_skt->notify_readable();
|
||||
}
|
||||
if (event.events & SRT_EPOLL_OUT) {
|
||||
srt_skt->notify_writeable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtPoller::mod_socket(SrsSrtSocket* srt_skt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
int events = srt_skt->events();
|
||||
SRTSOCKET srtfd = srt_skt->fd();
|
||||
|
||||
int ret = srt_epoll_update_usock(srt_epoller_fd_, srtfd, &events);
|
||||
srs_info("srt poller %d update srt socket %d, events=%d", srt_epoller_fd_, srtfd, events);
|
||||
|
||||
if (ret == SRT_ERROR) {
|
||||
return srs_error_new(ERROR_SRT_EPOLL, "srt epoll update socket=%lu failed, err=%s", srtfd, srt_getlasterror_str());
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsSrtSocket::SrsSrtSocket(SrsSrtPoller* srt_poller, SRTSOCKET srt_fd)
|
||||
{
|
||||
srt_poller_ = srt_poller;
|
||||
srt_fd_ = srt_fd;
|
||||
has_error_ = 0;
|
||||
read_cond_ = srs_cond_new();
|
||||
write_cond_ = srs_cond_new();
|
||||
|
||||
recv_timeout_ = 5 * SRS_UTIME_SECONDS;
|
||||
send_timeout_ = 5 * SRS_UTIME_SECONDS;
|
||||
|
||||
recv_bytes_ = 0;
|
||||
send_bytes_ = 0;
|
||||
|
||||
events_ = 0;
|
||||
}
|
||||
|
||||
SrsSrtSocket::~SrsSrtSocket()
|
||||
{
|
||||
srs_error_t err = srt_poller_->del_socket(this);
|
||||
if (err != srs_success) {
|
||||
srs_error("srt poller remove socket failed, err=%s", srs_error_desc(err).c_str());
|
||||
srs_error_reset(err);
|
||||
}
|
||||
|
||||
srs_cond_destroy(read_cond_);
|
||||
srs_cond_destroy(write_cond_);
|
||||
|
||||
srs_trace("close srt_fd=%d", srt_fd_);
|
||||
srt_close(srt_fd_);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::connect(const string& ip, int port)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
sockaddr_in inaddr;
|
||||
inaddr.sin_family = AF_INET;
|
||||
inaddr.sin_port = htons(port);
|
||||
// TODO: FIXME: inet_addr is deprecated
|
||||
inaddr.sin_addr.s_addr = inet_addr(ip.c_str());
|
||||
|
||||
int ret = srt_connect(srt_fd_, (const sockaddr*)&inaddr, sizeof(inaddr));
|
||||
|
||||
// TODO: FIXME: check return value.
|
||||
SRT_SOCKSTATUS srt_status = srt_getsockstate(srt_fd_);
|
||||
if (srt_status != SRTS_CONNECTED) {
|
||||
// Connect is in progress, wait until it finish or error.
|
||||
if ((err = wait_writeable()) != srs_success) {
|
||||
return srs_error_wrap(err, "wait writeable");
|
||||
}
|
||||
|
||||
// Double check if connect is established.
|
||||
srt_status = srt_getsockstate(srt_fd_);
|
||||
if (srt_status != SRTS_CONNECTED) {
|
||||
return srs_error_new(ERROR_SRT_IO, "srt_connect, err=%s", srt_getlasterror_str());
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::accept(SRTSOCKET* client_srt_fd)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
while (true) {
|
||||
sockaddr_in inaddr;
|
||||
int addrlen = sizeof(inaddr);
|
||||
SRTSOCKET srt_fd = srt_accept(srt_fd_, (sockaddr*)&inaddr, &addrlen);
|
||||
if (srt_fd == SRT_INVALID_SOCK) {
|
||||
if (srt_getlasterror(NULL) == SRT_EASYNCRCV) {
|
||||
// Accept would block, wait until new client connect or error.
|
||||
if ((err = wait_readable()) != srs_success) {
|
||||
return srs_error_wrap(err, "wait readable");
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
return srs_error_new(ERROR_SRT_IO, "srt_accept, err=%s", srt_getlasterror_str());
|
||||
}
|
||||
} else {
|
||||
*client_srt_fd = srt_fd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::recvmsg(void* buf, size_t size, ssize_t* nread)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
while (true) {
|
||||
int ret = srt_recvmsg(srt_fd_, (char*)buf, size);
|
||||
if (ret < 0) {
|
||||
if (srt_getlasterror(NULL) == SRT_EASYNCRCV) {
|
||||
if ((err = wait_readable()) != srs_success) {
|
||||
return srs_error_wrap(err, "wait readable");
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
return srs_error_new(ERROR_SRT_IO, "srt_recvmsg, err=%s", srt_getlasterror_str());
|
||||
}
|
||||
} else {
|
||||
recv_bytes_ += ret;
|
||||
*nread = ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::sendmsg(void* buf, size_t size, ssize_t* nwrite)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
while (true) {
|
||||
int ret = srt_sendmsg(srt_fd_, (const char*)buf, size, -1, 1);
|
||||
if (ret < 0) {
|
||||
if (srt_getlasterror(NULL) == SRT_EASYNCSND) {
|
||||
if ((err = wait_writeable()) != srs_success) {
|
||||
return srs_error_wrap(err, "wait writeable");
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
return srs_error_new(ERROR_SRT_IO, "srt_sendmsg, err=%s", srt_getlasterror_str());
|
||||
}
|
||||
} else {
|
||||
send_bytes_ += ret;
|
||||
*nwrite = ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::wait_readable()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// Check if error occured.
|
||||
if ((err = check_error()) != srs_success) {
|
||||
return srs_error_wrap(err, "has error");
|
||||
}
|
||||
|
||||
// Subscribe in and error event
|
||||
if ((err = enable_read()) != srs_success) {
|
||||
return srs_error_wrap(err, "enable read");
|
||||
}
|
||||
|
||||
// Wait event fired or timeout.
|
||||
int ret = srs_cond_timedwait(read_cond_, recv_timeout_);
|
||||
// TODO: FIXME: need to disable it?
|
||||
err = disable_read();
|
||||
if (err != srs_success) {
|
||||
srs_freep(err);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
// Timeout and events no fired.
|
||||
if (errno == ETIME) {
|
||||
return srs_error_new(ERROR_SRT_TIMEOUT, "srt socket %d timeout", srt_fd_);
|
||||
}
|
||||
// Interrupted, maybe coroutine terminated.
|
||||
if (errno == EINTR) {
|
||||
return srs_error_new(ERROR_SRT_INTERRUPT, "srt socket %d interrupted", srt_fd_);
|
||||
}
|
||||
return srs_error_new(ERROR_SRT_IO, "srt socket %d wait read", srt_fd_);
|
||||
}
|
||||
|
||||
// Check if we are notify with error event.
|
||||
if ((err = check_error()) != srs_success) {
|
||||
return srs_error_wrap(err, "has error");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::wait_writeable()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = check_error()) != srs_success) {
|
||||
return srs_error_wrap(err, "has error");
|
||||
}
|
||||
|
||||
if ((err = enable_write()) != srs_success) {
|
||||
return srs_error_wrap(err, "enable write");
|
||||
}
|
||||
|
||||
int ret = srs_cond_timedwait(write_cond_, send_timeout_);
|
||||
err = disable_write();
|
||||
if (err != srs_success) {
|
||||
srs_freep(err);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
if (errno == ETIME) {
|
||||
return srs_error_new(ERROR_SRT_TIMEOUT, "srt socket %d timeout", srt_fd_);
|
||||
}
|
||||
if (errno == EINTR) {
|
||||
return srs_error_new(ERROR_SRT_INTERRUPT, "srt socket %d interrupted", srt_fd_);
|
||||
}
|
||||
return srs_error_new(ERROR_SRT_IO, "srt socket %d wait write", srt_fd_);
|
||||
}
|
||||
|
||||
if ((err = check_error()) != srs_success) {
|
||||
return srs_error_wrap(err, "has error");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void SrsSrtSocket::notify_readable()
|
||||
{
|
||||
srs_cond_signal(read_cond_);
|
||||
}
|
||||
|
||||
void SrsSrtSocket::notify_writeable()
|
||||
{
|
||||
srs_cond_signal(write_cond_);
|
||||
}
|
||||
|
||||
void SrsSrtSocket::notify_error()
|
||||
{
|
||||
// mark error, and check when read/write
|
||||
has_error_ = true;
|
||||
srs_cond_signal(read_cond_);
|
||||
srs_cond_signal(write_cond_);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::enable_read()
|
||||
{
|
||||
return enable_event(SRT_EPOLL_IN | SRT_EPOLL_ERR);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::disable_read()
|
||||
{
|
||||
return disable_event(SRT_EPOLL_IN);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::enable_write()
|
||||
{
|
||||
return enable_event(SRT_EPOLL_OUT | SRT_EPOLL_ERR);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::disable_write()
|
||||
{
|
||||
return disable_event(SRT_EPOLL_OUT);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::enable_event(int event) {
|
||||
srs_error_t err = srs_success;
|
||||
// Event has been subscribed.
|
||||
if ((events_ & event) == event) {
|
||||
return err;
|
||||
}
|
||||
|
||||
int old_events = events_;
|
||||
events_ |= event;
|
||||
|
||||
if (old_events == 0) {
|
||||
err = srt_poller_->add_socket(this);
|
||||
} else {
|
||||
err = srt_poller_->mod_socket(this);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::disable_event(int event) {
|
||||
srs_error_t err = srs_success;
|
||||
// Event has been unsubscribed.
|
||||
if ((events_ & event) == 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
events_ &= (~event);
|
||||
|
||||
if (events_ == 0) {
|
||||
err = srt_poller_->del_socket(this);
|
||||
} else {
|
||||
err = srt_poller_->mod_socket(this);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSocket::check_error() {
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (has_error_) {
|
||||
return srs_error_new(ERROR_SRT_IO, "has error");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
154
trunk/src/protocol/srs_service_st_srt.hpp
Normal file
154
trunk/src/protocol/srs_service_st_srt.hpp
Normal file
|
@ -0,0 +1,154 @@
|
|||
//
|
||||
// Copyright (c) 2013-2021 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
||||
//
|
||||
|
||||
#ifndef SRS_SERVICE_ST_SRT_HPP
|
||||
#define SRS_SERVICE_ST_SRT_HPP
|
||||
|
||||
#include <srs_core.hpp>
|
||||
#include <srs_service_st.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <srt/srt.h>
|
||||
|
||||
// Create srt socket only, with libsrt's default option.
|
||||
extern srs_error_t srs_srt_socket(SRTSOCKET* pfd);
|
||||
|
||||
// Create srt socket with srs recommend default option(tsbpdmode=false,tlpktdrop=false,latency=0,sndsyn=0,rcvsyn=0)
|
||||
extern srs_error_t srs_srt_socket_with_default_option(SRTSOCKET* pfd);
|
||||
|
||||
// For server, listen at SRT endpoint.
|
||||
extern srs_error_t srs_srt_listen(SRTSOCKET srt_fd, std::string ip, int port);
|
||||
|
||||
// Set read/write no block.
|
||||
extern srs_error_t srs_srt_nonblock(SRTSOCKET srt_fd);
|
||||
|
||||
// Set SRT options.
|
||||
extern srs_error_t srs_srt_set_maxbw(SRTSOCKET srt_fd, int maxbw);
|
||||
extern srs_error_t srs_srt_set_mss(SRTSOCKET srt_fd, int mss);
|
||||
extern srs_error_t srs_srt_set_payload_size(SRTSOCKET srt_fd, int payload_size);
|
||||
extern srs_error_t srs_srt_set_connect_timeout(SRTSOCKET srt_fd, int timeout);
|
||||
extern srs_error_t srs_srt_set_peer_idle_timeout(SRTSOCKET srt_fd, int timeout);
|
||||
extern srs_error_t srs_srt_set_tsbpdmode(SRTSOCKET srt_fd, bool tsbpdmode);
|
||||
extern srs_error_t srs_srt_set_sndbuf(SRTSOCKET srt_fd, int sndbuf);
|
||||
extern srs_error_t srs_srt_set_rcvbuf(SRTSOCKET srt_fd, int rcvbuf);
|
||||
extern srs_error_t srs_srt_set_tlpktdrop(SRTSOCKET srt_fd, bool tlpktdrop);
|
||||
extern srs_error_t srs_srt_set_latency(SRTSOCKET srt_fd, int latency);
|
||||
extern srs_error_t srs_srt_set_rcv_latency(SRTSOCKET srt_fd, int rcv_latency);
|
||||
extern srs_error_t srs_srt_set_peer_latency(SRTSOCKET srt_fd, int peer_latency);
|
||||
extern srs_error_t srs_srt_set_streamid(SRTSOCKET srt_fd, const std::string& streamid);
|
||||
|
||||
// Get SRT options.
|
||||
extern srs_error_t srs_srt_get_maxbw(SRTSOCKET srt_fd, int& maxbw);
|
||||
extern srs_error_t srs_srt_get_mss(SRTSOCKET srt_fd, int& mss);
|
||||
extern srs_error_t srs_srt_get_payload_size(SRTSOCKET srt_fd, int& payload_size);
|
||||
extern srs_error_t srs_srt_get_connect_timeout(SRTSOCKET srt_fd, int& timeout);
|
||||
extern srs_error_t srs_srt_get_peer_idle_timeout(SRTSOCKET srt_fd, int& timeout);
|
||||
extern srs_error_t srs_srt_get_tsbpdmode(SRTSOCKET srt_fd, bool& tsbpdmode);
|
||||
extern srs_error_t srs_srt_get_sndbuf(SRTSOCKET srt_fd, int& sndbuf);
|
||||
extern srs_error_t srs_srt_get_rcvbuf(SRTSOCKET srt_fd, int& rcvbuf);
|
||||
extern srs_error_t srs_srt_get_tlpktdrop(SRTSOCKET srt_fd, bool& tlpktdrop);
|
||||
extern srs_error_t srs_srt_get_latency(SRTSOCKET srt_fd, int& latency);
|
||||
extern srs_error_t srs_srt_get_rcv_latency(SRTSOCKET srt_fd, int& rcv_latency);
|
||||
extern srs_error_t srs_srt_get_peer_latency(SRTSOCKET srt_fd, int& peer_latency);
|
||||
extern srs_error_t srs_srt_get_streamid(SRTSOCKET srt_fd, std::string& streamid);
|
||||
|
||||
// Get SRT socket info.
|
||||
extern srs_error_t srs_srt_get_local_ip_port(SRTSOCKET srt_fd, std::string& ip, int& port);
|
||||
extern srs_error_t srs_srt_get_remote_ip_port(SRTSOCKET srt_fd, std::string& ip, int& port);
|
||||
|
||||
class SrsSrtSocket;
|
||||
|
||||
// Srt poller, subscribe/unsubscribed events and wait them fired.
|
||||
class SrsSrtPoller
|
||||
{
|
||||
public:
|
||||
SrsSrtPoller();
|
||||
virtual ~SrsSrtPoller();
|
||||
public:
|
||||
srs_error_t initialize();
|
||||
srs_error_t add_socket(SrsSrtSocket* srt_skt);
|
||||
srs_error_t mod_socket(SrsSrtSocket* srt_skt);
|
||||
srs_error_t del_socket(SrsSrtSocket* srt_skt);
|
||||
srs_error_t wait(int timeout_ms);
|
||||
private:
|
||||
// Find SrsSrtSocket* context by SRTSOCKET.
|
||||
std::map<SRTSOCKET, SrsSrtSocket*> fd_sockets_;
|
||||
int srt_epoller_fd_;
|
||||
std::vector<SRT_EPOLL_EVENT> events_;
|
||||
};
|
||||
|
||||
// Srt ST socket, wrap SRT io and make it adapt to ST-thread.
|
||||
class SrsSrtSocket
|
||||
{
|
||||
public:
|
||||
SrsSrtSocket(SrsSrtPoller* srt_poller, SRTSOCKET srt_fd);
|
||||
virtual ~SrsSrtSocket();
|
||||
public: // IO API
|
||||
srs_error_t connect(const std::string& ip, int port);
|
||||
srs_error_t accept(SRTSOCKET* client_srt_fd);
|
||||
srs_error_t recvmsg(void* buf, size_t size, ssize_t* nread);
|
||||
srs_error_t sendmsg(void* buf, size_t size, ssize_t* nwrite);
|
||||
public:
|
||||
SRTSOCKET fd() const { return srt_fd_; }
|
||||
int events() const { return events_; }
|
||||
public:
|
||||
void set_recv_timeout(srs_utime_t tm) { recv_timeout_ = tm; }
|
||||
void set_send_timeout(srs_utime_t tm) { send_timeout_ = tm; }
|
||||
srs_utime_t get_send_timeout() { return send_timeout_; }
|
||||
srs_utime_t get_recv_timeout() { return recv_timeout_; }
|
||||
int64_t get_send_bytes() { return send_bytes_; }
|
||||
int64_t get_recv_bytes() { return recv_bytes_; }
|
||||
// Yiled coroutine and wait this socket readable.
|
||||
srs_error_t wait_readable();
|
||||
// Yiled coroutine and wait this socket writeable.
|
||||
srs_error_t wait_writeable();
|
||||
// Notify this socket readable, and resume coroutine later.
|
||||
void notify_readable();
|
||||
// Notify this socket writeable, and resume coroutine later.
|
||||
void notify_writeable();
|
||||
// Notify this socket error, resume coroutine later and error will return in all the operator of this socket.
|
||||
void notify_error();
|
||||
public:
|
||||
// Subscribed IN/ERR event to srt poller.
|
||||
srs_error_t enable_read();
|
||||
// Unsubscribed IN event to srt poller.
|
||||
srs_error_t disable_read();
|
||||
// Subscribed OUT/ERR event to srt poller.
|
||||
srs_error_t enable_write();
|
||||
// Unsubscribed OUT event to srt poller.
|
||||
srs_error_t disable_write();
|
||||
private:
|
||||
srs_error_t enable_event(int event);
|
||||
srs_error_t disable_event(int event);
|
||||
srs_error_t check_error();
|
||||
|
||||
private:
|
||||
SRTSOCKET srt_fd_;
|
||||
// Mark if some error occured in srt socket.
|
||||
bool has_error_;
|
||||
// When read operator like recvmsg/accept would block, wait this condition timeout or notified,
|
||||
// and the coroutine itself will be yiled and resume when condition be notified.
|
||||
srs_cond_t read_cond_;
|
||||
// When write operator like sendmsg/connectt would block, wait this condition timeout or notified,
|
||||
// and the coroutine itself will be yiled and resume when condition be notified.
|
||||
srs_cond_t write_cond_;
|
||||
|
||||
srs_utime_t recv_timeout_;
|
||||
srs_utime_t send_timeout_;
|
||||
|
||||
int64_t recv_bytes_;
|
||||
int64_t send_bytes_;
|
||||
|
||||
// Event of this socket subscribed.
|
||||
int events_;
|
||||
// Srt poller which this socket attach to.
|
||||
SrsSrtPoller* srt_poller_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -17,6 +17,10 @@
|
|||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
#ifdef SRS_SRT
|
||||
#include <srs_app_srt_server.hpp>
|
||||
#endif
|
||||
|
||||
// Temporary disk config.
|
||||
std::string _srs_tmp_file_prefix = "/tmp/srs-utest-";
|
||||
// Temporary network config.
|
||||
|
@ -32,6 +36,10 @@ SrsConfig* _srs_config = NULL;
|
|||
SrsServer* _srs_server = NULL;
|
||||
bool _srs_in_docker = false;
|
||||
|
||||
#ifdef SRS_SRT
|
||||
SrsSrtEventLoop* _srt_eventloop = NULL;
|
||||
#endif
|
||||
|
||||
#include <srs_app_st.hpp>
|
||||
|
||||
// Initialize global settings.
|
||||
|
@ -52,6 +60,16 @@ srs_error_t prepare_main() {
|
|||
srs_freep(_srs_context);
|
||||
_srs_context = new SrsThreadContext();
|
||||
|
||||
#ifdef SRS_SRT
|
||||
_srt_eventloop = new SrsSrtEventLoop();
|
||||
if ((err = _srt_eventloop->initialize()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt poller initialize");
|
||||
}
|
||||
if ((err = _srt_eventloop->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt poller start");
|
||||
}
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,84 +6,461 @@
|
|||
#include <srs_utest_srt.hpp>
|
||||
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srt_conn.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_service_st_srt.hpp>
|
||||
#include <srs_rtmp_stack.hpp>
|
||||
#include <srs_app_srt_utility.hpp>
|
||||
#include <srs_app_srt_server.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
VOID TEST(ProtocolSrtTest, SrtGetStreamInfoNormal) {
|
||||
extern SrsSrtEventLoop* _srt_eventloop;
|
||||
|
||||
// Test srt st service
|
||||
VOID TEST(ServiceSrtPoller, SrtPollOperateSocket)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsSrtPoller* srt_poller = new SrsSrtPoller();
|
||||
HELPER_EXPECT_SUCCESS(srt_poller->initialize());
|
||||
|
||||
SRTSOCKET srt_fd = SRT_INVALID_SOCK;
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_socket(&srt_fd));
|
||||
EXPECT_TRUE(srt_fd > 0);
|
||||
|
||||
SrsSrtSocket* srt_socket = new SrsSrtSocket(srt_poller, srt_fd);
|
||||
EXPECT_EQ(srt_socket->events(), 0);
|
||||
|
||||
// Enable read, will subscribe SRT_EPOLL_IN and SRT_EPOLL_ERR event in srt poller.
|
||||
HELPER_EXPECT_SUCCESS(srt_socket->enable_read());
|
||||
EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_IN);
|
||||
EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_ERR);
|
||||
|
||||
// Enable read, will subscribe SRT_EPOLL_OUT and SRT_EPOLL_ERR event in srt poller.
|
||||
HELPER_EXPECT_SUCCESS(srt_socket->enable_write());
|
||||
EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_OUT);
|
||||
EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_ERR);
|
||||
|
||||
// Disable read, will unsubscribe SRT_EPOLL_IN event in srt poller.
|
||||
HELPER_EXPECT_SUCCESS(srt_socket->disable_read());
|
||||
EXPECT_FALSE(srt_socket->events() & SRT_EPOLL_IN);
|
||||
EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_ERR);
|
||||
|
||||
// Disable write, will unsubscribe SRT_EPOLL_OUT event in srt poller.
|
||||
HELPER_EXPECT_SUCCESS(srt_socket->disable_write());
|
||||
EXPECT_FALSE(srt_socket->events() & SRT_EPOLL_OUT);
|
||||
EXPECT_TRUE(srt_socket->events() & SRT_EPOLL_ERR);
|
||||
|
||||
EXPECT_EQ(srt_poller->fd_sockets_.size(), 1);
|
||||
// Delete socket, will remove in srt poller.
|
||||
srs_freep(srt_socket);
|
||||
EXPECT_EQ(srt_poller->fd_sockets_.size(), 0);
|
||||
|
||||
srs_freep(srt_poller);
|
||||
}
|
||||
|
||||
VOID TEST(ServiceSrtPoller, SrtSetGetSocketOpt)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SRTSOCKET srt_fd = SRT_INVALID_SOCK;
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_socket(&srt_fd));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_nonblock(srt_fd));
|
||||
|
||||
int maxbw = 20000;
|
||||
int mss = 1400;
|
||||
int payload_size = 1316;
|
||||
int connect_timeout = 5000;
|
||||
int peer_idle_timeout = 10000;
|
||||
bool tsbpdmode = false;
|
||||
int sndbuf = 2 * 1024 * 1024;
|
||||
int rcvbuf = 10 * 1024 * 1024;
|
||||
bool tlpktdrop = false;
|
||||
int latency = 0;
|
||||
int rcv_latency = 120;
|
||||
int peer_latency = 120;
|
||||
std::string streamid = "SRS_SRT";
|
||||
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_maxbw(srt_fd, maxbw));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_mss(srt_fd, mss));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_payload_size(srt_fd, payload_size));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_connect_timeout(srt_fd, connect_timeout));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_peer_idle_timeout(srt_fd, peer_idle_timeout));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_tsbpdmode(srt_fd, tsbpdmode));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_sndbuf(srt_fd, sndbuf));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_rcvbuf(srt_fd, rcvbuf));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_tlpktdrop(srt_fd, tlpktdrop));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_latency(srt_fd, latency));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_rcv_latency(srt_fd, rcv_latency));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_peer_latency(srt_fd, peer_latency));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_streamid(srt_fd, streamid));
|
||||
|
||||
bool b;
|
||||
int i = 0;
|
||||
std::string s;
|
||||
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_maxbw(srt_fd, i));
|
||||
EXPECT_EQ(i, maxbw);
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_mss(srt_fd, i));
|
||||
EXPECT_EQ(i, mss);
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_payload_size(srt_fd, i));
|
||||
EXPECT_EQ(i, payload_size);
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_connect_timeout(srt_fd, i));
|
||||
EXPECT_EQ(i, connect_timeout);
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_peer_idle_timeout(srt_fd, i));
|
||||
EXPECT_EQ(i, peer_idle_timeout);
|
||||
|
||||
// Don't check b equal to option blow, because some opt will deterimated after srt handshake done or change when set.
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_tsbpdmode(srt_fd, b));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_sndbuf(srt_fd, i));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_rcvbuf(srt_fd, i));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_tlpktdrop(srt_fd, b));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_latency(srt_fd, i));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_rcv_latency(srt_fd, i));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_peer_latency(srt_fd, i));
|
||||
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_streamid(srt_fd, s));
|
||||
EXPECT_EQ(s, streamid);
|
||||
}
|
||||
|
||||
class MockSrtServer
|
||||
{
|
||||
public:
|
||||
SrsSrtSocket* srt_socket_;
|
||||
SRTSOCKET srt_server_fd_;
|
||||
|
||||
MockSrtServer() {
|
||||
srt_server_fd_ = SRT_INVALID_SOCK;
|
||||
srt_socket_ = NULL;
|
||||
}
|
||||
|
||||
srs_error_t create_socket() {
|
||||
srs_error_t err = srs_success;
|
||||
if ((err = srs_srt_socket_with_default_option(&srt_server_fd_)) != srs_success) {
|
||||
return srs_error_wrap(err, "create srt socket");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t listen(std::string ip, int port) {
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = srs_srt_listen(srt_server_fd_, ip, port)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt listen");
|
||||
}
|
||||
|
||||
srt_socket_ = new SrsSrtSocket(_srt_eventloop->get_srt_poller(), srt_server_fd_);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
virtual ~MockSrtServer() {
|
||||
srs_freep(srt_socket_);
|
||||
}
|
||||
|
||||
virtual srs_error_t accept(SRTSOCKET* client_fd) {
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = srt_socket_->accept(client_fd)) != srs_success) {
|
||||
return srs_error_wrap(err, "srt accept");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
VOID TEST(ServiceStSRTTest, ListenConnectAccept)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
std::string server_ip = "127.0.0.1";
|
||||
int server_port = 9000;
|
||||
|
||||
MockSrtServer srt_server;
|
||||
HELPER_EXPECT_SUCCESS(srt_server.create_socket());
|
||||
HELPER_EXPECT_SUCCESS(srt_server.listen(server_ip, server_port));
|
||||
|
||||
SRTSOCKET srt_client_fd = SRT_INVALID_SOCK;
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_socket(&srt_client_fd));
|
||||
|
||||
SrsSrtSocket* srt_client_socket = new SrsSrtSocket(_srt_eventloop->get_srt_poller(), srt_client_fd);
|
||||
|
||||
// No client connected, accept will timeout.
|
||||
SRTSOCKET srt_fd = SRT_INVALID_SOCK;
|
||||
// Make utest fast timeout.
|
||||
srt_server.srt_socket_->set_recv_timeout(50 * SRS_UTIME_MILLISECONDS);
|
||||
err = srt_server.accept(&srt_fd);
|
||||
EXPECT_EQ(srs_error_code(err), ERROR_SRT_TIMEOUT);
|
||||
EXPECT_EQ(srt_fd, SRT_INVALID_SOCK);
|
||||
|
||||
// Client connect to server
|
||||
HELPER_EXPECT_SUCCESS(srt_client_socket->connect(server_ip, server_port));
|
||||
|
||||
// Server will accept one client.
|
||||
HELPER_EXPECT_SUCCESS(srt_server.accept(&srt_fd));
|
||||
EXPECT_NE(srt_fd, SRT_INVALID_SOCK);
|
||||
}
|
||||
|
||||
VOID TEST(ServiceStSRTTest, ConnectTimeout)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SRTSOCKET srt_client_fd = SRT_INVALID_SOCK;
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_socket_with_default_option(&srt_client_fd));
|
||||
SrsSrtSocket* srt_client_socket = new SrsSrtSocket(_srt_eventloop->get_srt_poller(), srt_client_fd);
|
||||
|
||||
srt_client_socket->set_send_timeout(50 * SRS_UTIME_MILLISECONDS);
|
||||
// Client connect to server which is no listening.
|
||||
HELPER_EXPECT_FAILED(srt_client_socket->connect("127.0.0.1", 9099));
|
||||
}
|
||||
|
||||
VOID TEST(ServiceStSRTTest, ConnectWithStreamid)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
std::string server_ip = "127.0.0.1";
|
||||
int server_port = 9000;
|
||||
|
||||
MockSrtServer srt_server;
|
||||
HELPER_EXPECT_SUCCESS(srt_server.create_socket());
|
||||
HELPER_EXPECT_SUCCESS(srt_server.listen(server_ip, server_port));
|
||||
|
||||
std::string streamid = "SRS_SRT_Streamid";
|
||||
SRTSOCKET srt_client_fd = SRT_INVALID_SOCK;
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_socket_with_default_option(&srt_client_fd));
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_set_streamid(srt_client_fd, streamid));
|
||||
SrsSrtSocket* srt_client_socket = new SrsSrtSocket(_srt_eventloop->get_srt_poller(), srt_client_fd);
|
||||
|
||||
HELPER_EXPECT_SUCCESS(srt_client_socket->connect("127.0.0.1", 9000));
|
||||
|
||||
SRTSOCKET srt_server_accepted_fd = SRT_INVALID_SOCK;
|
||||
HELPER_EXPECT_SUCCESS(srt_server.accept(&srt_server_accepted_fd));
|
||||
EXPECT_NE(srt_server_accepted_fd, SRT_INVALID_SOCK);
|
||||
std::string s;
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_get_streamid(srt_server_accepted_fd, s));
|
||||
EXPECT_EQ(s, streamid);
|
||||
}
|
||||
|
||||
VOID TEST(ServiceStSRTTest, ReadWrite)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
std::string server_ip = "127.0.0.1";
|
||||
int server_port = 9000;
|
||||
|
||||
MockSrtServer srt_server;
|
||||
HELPER_EXPECT_SUCCESS(srt_server.create_socket());
|
||||
HELPER_EXPECT_SUCCESS(srt_server.listen(server_ip, server_port));
|
||||
|
||||
SRTSOCKET srt_client_fd = SRT_INVALID_SOCK;
|
||||
HELPER_EXPECT_SUCCESS(srs_srt_socket_with_default_option(&srt_client_fd));
|
||||
SrsSrtSocket* srt_client_socket = new SrsSrtSocket(_srt_eventloop->get_srt_poller(), srt_client_fd);
|
||||
|
||||
// Client connect to server
|
||||
HELPER_EXPECT_SUCCESS(srt_client_socket->connect(server_ip, server_port));
|
||||
|
||||
// Server will accept one client.
|
||||
SRTSOCKET srt_server_accepted_fd = SRT_INVALID_SOCK;
|
||||
HELPER_EXPECT_SUCCESS(srt_server.accept(&srt_server_accepted_fd));
|
||||
EXPECT_NE(srt_server_accepted_fd, SRT_INVALID_SOCK);
|
||||
SrsSrtSocket* srt_server_accepted_socket = new SrsSrtSocket(_srt_eventloop->get_srt_poller(), srt_server_accepted_fd);
|
||||
|
||||
if (true) {
|
||||
int mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(get_streamid_info("#!::r=live/livestream,key1=value1,key2=value2", mode, vhost, subpath));
|
||||
EXPECT_EQ(PULL_SRT_MODE, mode);
|
||||
std::string content = "Hello, SRS SRT!";
|
||||
|
||||
// Client send msg to server.
|
||||
ssize_t nb_write = 0;
|
||||
HELPER_EXPECT_SUCCESS(srt_client_socket->sendmsg((char*)content.data(), content.size(), &nb_write));
|
||||
EXPECT_EQ(nb_write, content.size());
|
||||
|
||||
// Server recv msg from client
|
||||
char buf[1500];
|
||||
ssize_t nb_read = 0;
|
||||
HELPER_EXPECT_SUCCESS(srt_server_accepted_socket->recvmsg(buf, sizeof(buf), &nb_read));
|
||||
EXPECT_EQ(nb_read, content.size());
|
||||
EXPECT_EQ(std::string(buf, nb_read), content);
|
||||
|
||||
// Server echo msg back to client.
|
||||
HELPER_EXPECT_SUCCESS(srt_server_accepted_socket->sendmsg(buf, nb_read, &nb_write));
|
||||
EXPECT_EQ(nb_write, content.size());
|
||||
|
||||
// Client recv echo msg from server.
|
||||
HELPER_EXPECT_SUCCESS(srt_client_socket->recvmsg(buf, sizeof(buf), &nb_read));
|
||||
EXPECT_EQ(nb_read, content.size());
|
||||
EXPECT_EQ(std::string(buf, nb_read), content);
|
||||
}
|
||||
|
||||
if (true) {
|
||||
char buf[1500];
|
||||
ssize_t nb_read = 0;
|
||||
// Make socket fast timeout in ustet.
|
||||
srt_server_accepted_socket->set_recv_timeout(50 * SRS_UTIME_MILLISECONDS);
|
||||
// Recv msg from client, but client no send any msg, so will be timeout.
|
||||
err = srt_server_accepted_socket->recvmsg(buf, sizeof(buf), &nb_read);
|
||||
EXPECT_EQ(srs_error_code(err), ERROR_SRT_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
// Test srt server
|
||||
class MockSrtHandler : public ISrsSrtHandler
|
||||
{
|
||||
private:
|
||||
SRTSOCKET srt_fd;
|
||||
public:
|
||||
MockSrtHandler() {
|
||||
srt_fd = SRT_INVALID_SOCK;
|
||||
}
|
||||
virtual ~MockSrtHandler() {
|
||||
}
|
||||
public:
|
||||
virtual srs_error_t on_srt_client(SRTSOCKET fd) {
|
||||
srt_fd = fd;
|
||||
return srs_success;
|
||||
}
|
||||
};
|
||||
|
||||
VOID TEST(SrtServerTest, SrtListener)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (true) {
|
||||
MockSrtHandler h;
|
||||
SrsSrtListener srt_listener(&h, "127.0.0.1", 9000);
|
||||
HELPER_EXPECT_SUCCESS(srt_listener.create_socket());
|
||||
HELPER_EXPECT_SUCCESS(srt_listener.listen());
|
||||
EXPECT_TRUE(srt_listener.fd() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Test srt app
|
||||
VOID TEST(ProtocolSrtTest, SrtGetStreamInfoNormal)
|
||||
{
|
||||
if (true) {
|
||||
SrtMode mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(srs_srt_streamid_info("#!::r=live/livestream,key1=value1,key2=value2", mode, vhost, subpath));
|
||||
EXPECT_EQ(SrtModePull, mode);
|
||||
EXPECT_STREQ("", vhost.c_str());
|
||||
EXPECT_STREQ("live/livestream?key1=value1&key2=value2", subpath.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(get_streamid_info("#!::h=host.com,r=live/livestream,key1=value1,key2=value2", mode, vhost, subpath));
|
||||
EXPECT_EQ(PULL_SRT_MODE, mode);
|
||||
SrtMode mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(srs_srt_streamid_info("#!::h=host.com,r=live/livestream,key1=value1,key2=value2", mode, vhost, subpath));
|
||||
EXPECT_EQ(SrtModePull, mode);
|
||||
EXPECT_STREQ("host.com", vhost.c_str());
|
||||
EXPECT_STREQ("live/livestream?vhost=host.com&key1=value1&key2=value2", subpath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolSrtTest, SrtGetStreamInfoMethod) {
|
||||
VOID TEST(ProtocolSrtTest, SrtGetStreamInfoMethod)
|
||||
{
|
||||
if (true) {
|
||||
int mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(get_streamid_info("#!::r=live/livestream,m=request", mode, vhost, subpath));
|
||||
EXPECT_EQ(PULL_SRT_MODE, mode);
|
||||
SrtMode mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(srs_srt_streamid_info("#!::r=live/livestream,m=request", mode, vhost, subpath));
|
||||
EXPECT_EQ(SrtModePull, mode);
|
||||
EXPECT_STREQ("live/livestream", subpath.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(get_streamid_info("#!::r=live/livestream,m=publish", mode, vhost, subpath));
|
||||
EXPECT_EQ(PUSH_SRT_MODE, mode);
|
||||
SrtMode mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(srs_srt_streamid_info("#!::r=live/livestream,m=publish", mode, vhost, subpath));
|
||||
EXPECT_EQ(SrtModePush, mode);
|
||||
EXPECT_STREQ("live/livestream", subpath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolSrtTest, SrtGetStreamInfoCompatible) {
|
||||
VOID TEST(ProtocolSrtTest, SrtGetStreamInfoCompatible)
|
||||
{
|
||||
if (true) {
|
||||
int mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(get_streamid_info("#!::h=live/livestream,m=request", mode, vhost, subpath));
|
||||
EXPECT_EQ(PULL_SRT_MODE, mode);
|
||||
SrtMode mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(srs_srt_streamid_info("#!::h=live/livestream,m=request", mode, vhost, subpath));
|
||||
EXPECT_EQ(SrtModePull, mode);
|
||||
EXPECT_STREQ("", vhost.c_str());
|
||||
EXPECT_STREQ("live/livestream", subpath.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(get_streamid_info("#!::h=live/livestream,m=publish", mode, vhost, subpath));
|
||||
EXPECT_EQ(PUSH_SRT_MODE, mode);
|
||||
SrtMode mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(srs_srt_streamid_info("#!::h=live/livestream,m=publish", mode, vhost, subpath));
|
||||
EXPECT_EQ(SrtModePush, mode);
|
||||
EXPECT_STREQ("", vhost.c_str());
|
||||
EXPECT_STREQ("live/livestream", subpath.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(get_streamid_info("#!::h=srs.srt.com.cn/live/livestream,m=request", mode, vhost, subpath));
|
||||
EXPECT_EQ(PULL_SRT_MODE, mode);
|
||||
SrtMode mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(srs_srt_streamid_info("#!::h=srs.srt.com.cn/live/livestream,m=request", mode, vhost, subpath));
|
||||
EXPECT_EQ(SrtModePull, mode);
|
||||
EXPECT_STREQ("srs.srt.com.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live/livestream?vhost=srs.srt.com.cn", subpath.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(get_streamid_info("#!::h=srs.srt.com.cn/live/livestream,m=publish", mode, vhost, subpath));
|
||||
EXPECT_EQ(PUSH_SRT_MODE, mode);
|
||||
SrtMode mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(srs_srt_streamid_info("#!::h=srs.srt.com.cn/live/livestream,m=publish", mode, vhost, subpath));
|
||||
EXPECT_EQ(SrtModePush, mode);
|
||||
EXPECT_STREQ("srs.srt.com.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live/livestream?vhost=srs.srt.com.cn", subpath.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(get_streamid_info("#!::h=live/livestream?secret=d6d2be37,m=publish", mode, vhost, subpath));
|
||||
EXPECT_EQ(PUSH_SRT_MODE, mode);
|
||||
SrtMode mode; string vhost; string subpath;
|
||||
EXPECT_TRUE(srs_srt_streamid_info("#!::h=live/livestream?secret=d6d2be37,m=publish", mode, vhost, subpath));
|
||||
EXPECT_EQ(SrtModePush, mode);
|
||||
EXPECT_STREQ("", vhost.c_str());
|
||||
EXPECT_STREQ("live/livestream?secret=d6d2be37", subpath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolSrtTest, SrtStreamIdToRequest)
|
||||
{
|
||||
if (true) {
|
||||
SrtMode mode;
|
||||
SrsRequest req;
|
||||
EXPECT_TRUE(srs_srt_streamid_to_request("#!::r=live/livestream?key1=val1,key2=val2", mode, &req));
|
||||
EXPECT_EQ(mode, SrtModePull);
|
||||
EXPECT_STREQ(req.vhost.c_str(), "");
|
||||
EXPECT_STREQ(req.app.c_str(), "live");
|
||||
EXPECT_STREQ(req.stream.c_str(), "livestream");
|
||||
EXPECT_STREQ(req.param.c_str(), "key1=val1&key2=val2");
|
||||
}
|
||||
|
||||
if (true) {
|
||||
SrtMode mode;
|
||||
SrsRequest req;
|
||||
EXPECT_TRUE(srs_srt_streamid_to_request("#!::h=srs.srt.com.cn,r=live/livestream?key1=val1,key2=val2", mode, &req));
|
||||
EXPECT_EQ(mode, SrtModePull);
|
||||
EXPECT_STREQ(req.vhost.c_str(), "srs.srt.com.cn");
|
||||
EXPECT_STREQ(req.app.c_str(), "live");
|
||||
EXPECT_STREQ(req.stream.c_str(), "livestream");
|
||||
EXPECT_STREQ(req.param.c_str(), "vhost=srs.srt.com.cn&key1=val1&key2=val2");
|
||||
}
|
||||
|
||||
if (true) {
|
||||
SrtMode mode;
|
||||
SrsRequest req;
|
||||
EXPECT_TRUE(srs_srt_streamid_to_request("#!::h=live/livestream?key1=val1,key2=val2", mode, &req));
|
||||
EXPECT_EQ(mode, SrtModePull);
|
||||
EXPECT_STREQ(req.vhost.c_str(), "");
|
||||
EXPECT_STREQ(req.app.c_str(), "live");
|
||||
EXPECT_STREQ(req.stream.c_str(), "livestream");
|
||||
EXPECT_STREQ(req.param.c_str(), "key1=val1&key2=val2");
|
||||
}
|
||||
|
||||
if (true) {
|
||||
SrtMode mode;
|
||||
SrsRequest req;
|
||||
EXPECT_TRUE(srs_srt_streamid_to_request("#!::h=srs.srt.com.cn/live/livestream?key1=val1,key2=val2", mode, &req));
|
||||
EXPECT_EQ(mode, SrtModePull);
|
||||
EXPECT_STREQ(req.vhost.c_str(), "srs.srt.com.cn");
|
||||
EXPECT_STREQ(req.app.c_str(), "live");
|
||||
EXPECT_STREQ(req.stream.c_str(), "livestream");
|
||||
EXPECT_STREQ(req.param.c_str(), "vhost=srs.srt.com.cn&key1=val1&key2=val2");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: FIXME: add mpegts conn test
|
||||
// set srt option, recv srt client, get srt client opt and check.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue