mirror of
https://github.com/ossrs/srs.git
synced 2025-02-15 04:42:04 +00:00
for #133, rtsp extract tcp/udp listener.
This commit is contained in:
parent
4807f7850d
commit
40fbfd8560
17 changed files with 876 additions and 300 deletions
|
@ -160,6 +160,11 @@ stream_caster {
|
||||||
# for mpegts_over_udp caster, listen at udp port. for example, 8935.
|
# for mpegts_over_udp caster, listen at udp port. for example, 8935.
|
||||||
# for rtsp caster, listen at tcp port. for example, 554.
|
# for rtsp caster, listen at tcp port. for example, 554.
|
||||||
listen 8935;
|
listen 8935;
|
||||||
|
# for the rtsp caster, the rtp server local port over udp,
|
||||||
|
# which reply the rtsp setup request message, the port will be used:
|
||||||
|
# [rtp_port_min, rtp_port_max)
|
||||||
|
rtp_port_min 57200;
|
||||||
|
rtp_port_max 57300;
|
||||||
}
|
}
|
||||||
stream_caster {
|
stream_caster {
|
||||||
enabled off;
|
enabled off;
|
||||||
|
|
2
trunk/configure
vendored
2
trunk/configure
vendored
|
@ -392,7 +392,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
|
||||||
"srs_app_json" "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge"
|
"srs_app_json" "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge"
|
||||||
"srs_app_kbps" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client"
|
"srs_app_kbps" "srs_app_heartbeat" "srs_app_empty" "srs_app_http_client"
|
||||||
"srs_app_recv_thread" "srs_app_security" "srs_app_statistic"
|
"srs_app_recv_thread" "srs_app_security" "srs_app_statistic"
|
||||||
"srs_app_mpegts_udp" "srs_app_rtsp")
|
"srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener")
|
||||||
APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh
|
APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh
|
||||||
APP_OBJS="${MODULE_OBJS[@]}"
|
APP_OBJS="${MODULE_OBJS[@]}"
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -102,6 +102,8 @@ file
|
||||||
../../src/app/srs_app_json.cpp,
|
../../src/app/srs_app_json.cpp,
|
||||||
../../src/app/srs_app_kbps.hpp,
|
../../src/app/srs_app_kbps.hpp,
|
||||||
../../src/app/srs_app_kbps.cpp,
|
../../src/app/srs_app_kbps.cpp,
|
||||||
|
../../src/app/srs_app_listener.hpp,
|
||||||
|
../../src/app/srs_app_listener.cpp,
|
||||||
../../src/app/srs_app_log.hpp,
|
../../src/app/srs_app_log.hpp,
|
||||||
../../src/app/srs_app_log.cpp,
|
../../src/app/srs_app_log.cpp,
|
||||||
../../src/app/srs_app_mpegts_udp.hpp,
|
../../src/app/srs_app_mpegts_udp.hpp,
|
||||||
|
|
|
@ -82,6 +82,7 @@
|
||||||
<ClInclude Include="..\..\src\app\srs_app_ingest.hpp" />
|
<ClInclude Include="..\..\src\app\srs_app_ingest.hpp" />
|
||||||
<ClInclude Include="..\..\src\app\srs_app_json.hpp" />
|
<ClInclude Include="..\..\src\app\srs_app_json.hpp" />
|
||||||
<ClInclude Include="..\..\src\app\srs_app_kbps.hpp" />
|
<ClInclude Include="..\..\src\app\srs_app_kbps.hpp" />
|
||||||
|
<ClInclude Include="..\..\src\app\srs_app_listener.hpp" />
|
||||||
<ClInclude Include="..\..\src\app\srs_app_log.hpp" />
|
<ClInclude Include="..\..\src\app\srs_app_log.hpp" />
|
||||||
<ClInclude Include="..\..\src\app\srs_app_mpegts_udp.hpp" />
|
<ClInclude Include="..\..\src\app\srs_app_mpegts_udp.hpp" />
|
||||||
<ClInclude Include="..\..\src\app\srs_app_pithy_print.hpp" />
|
<ClInclude Include="..\..\src\app\srs_app_pithy_print.hpp" />
|
||||||
|
@ -161,6 +162,7 @@
|
||||||
<ClCompile Include="..\..\src\app\srs_app_ingest.cpp" />
|
<ClCompile Include="..\..\src\app\srs_app_ingest.cpp" />
|
||||||
<ClCompile Include="..\..\src\app\srs_app_json.cpp" />
|
<ClCompile Include="..\..\src\app\srs_app_json.cpp" />
|
||||||
<ClCompile Include="..\..\src\app\srs_app_kbps.cpp" />
|
<ClCompile Include="..\..\src\app\srs_app_kbps.cpp" />
|
||||||
|
<ClCompile Include="..\..\src\app\srs_app_listener.cpp" />
|
||||||
<ClCompile Include="..\..\src\app\srs_app_log.cpp" />
|
<ClCompile Include="..\..\src\app\srs_app_log.cpp" />
|
||||||
<ClCompile Include="..\..\src\app\srs_app_mpegts_udp.cpp" />
|
<ClCompile Include="..\..\src\app\srs_app_mpegts_udp.cpp" />
|
||||||
<ClCompile Include="..\..\src\app\srs_app_pithy_print.cpp" />
|
<ClCompile Include="..\..\src\app\srs_app_pithy_print.cpp" />
|
||||||
|
|
|
@ -235,6 +235,9 @@
|
||||||
<ClCompile Include="..\..\src\protocol\srs_rtsp_stack.cpp">
|
<ClCompile Include="..\..\src\protocol\srs_rtsp_stack.cpp">
|
||||||
<Filter>srs</Filter>
|
<Filter>srs</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\app\srs_app_listener.cpp">
|
||||||
|
<Filter>srs</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\src\app\srs_app_bandwidth.hpp">
|
<ClInclude Include="..\..\src\app\srs_app_bandwidth.hpp">
|
||||||
|
@ -432,6 +435,9 @@
|
||||||
<ClInclude Include="..\..\src\protocol\srs_rtsp_stack.hpp">
|
<ClInclude Include="..\..\src\protocol\srs_rtsp_stack.hpp">
|
||||||
<Filter>srs</Filter>
|
<Filter>srs</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\app\srs_app_listener.hpp">
|
||||||
|
<Filter>srs</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Filter Include="research">
|
<Filter Include="research">
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ int SrsConfig::check_config()
|
||||||
SrsConfDirective* conf = stream_caster->at(i);
|
SrsConfDirective* conf = stream_caster->at(i);
|
||||||
string n = conf->name;
|
string n = conf->name;
|
||||||
if (n != "enabled" && n != "caster" && n != "output"
|
if (n != "enabled" && n != "caster" && n != "output"
|
||||||
&& n != "listen"
|
&& n != "listen" && n != "rtp_port_min" && n != "rtp_port_max"
|
||||||
) {
|
) {
|
||||||
ret = ERROR_SYSTEM_CONFIG_INVALID;
|
ret = ERROR_SYSTEM_CONFIG_INVALID;
|
||||||
srs_error("unsupported stream_caster directive %s, ret=%d", n.c_str(), ret);
|
srs_error("unsupported stream_caster directive %s, ret=%d", n.c_str(), ret);
|
||||||
|
@ -2065,6 +2065,30 @@ int SrsConfig::get_stream_caster_listen(SrsConfDirective* sc)
|
||||||
return ::atoi(conf->arg0().c_str());
|
return ::atoi(conf->arg0().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SrsConfig::get_stream_caster_rtp_port_min(SrsConfDirective* sc)
|
||||||
|
{
|
||||||
|
srs_assert(sc);
|
||||||
|
|
||||||
|
SrsConfDirective* conf = sc->get("rtp_port_min");
|
||||||
|
if (!conf) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::atoi(conf->arg0().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* sc)
|
||||||
|
{
|
||||||
|
srs_assert(sc);
|
||||||
|
|
||||||
|
SrsConfDirective* conf = sc->get("rtp_port_max");
|
||||||
|
if (!conf) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::atoi(conf->arg0().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
SrsConfDirective* SrsConfig::get_vhost(string vhost)
|
SrsConfDirective* SrsConfig::get_vhost(string vhost)
|
||||||
{
|
{
|
||||||
srs_assert(root);
|
srs_assert(root);
|
||||||
|
|
|
@ -481,6 +481,14 @@ public:
|
||||||
* get the listen port of stream caster.
|
* get the listen port of stream caster.
|
||||||
*/
|
*/
|
||||||
virtual int get_stream_caster_listen(SrsConfDirective* sc);
|
virtual int get_stream_caster_listen(SrsConfDirective* sc);
|
||||||
|
/**
|
||||||
|
* get the min udp port for rtp of stream caster rtsp.
|
||||||
|
*/
|
||||||
|
virtual int get_stream_caster_rtp_port_min(SrsConfDirective* sc);
|
||||||
|
/**
|
||||||
|
* get the max udp port for rtp of stream caster rtsp.
|
||||||
|
*/
|
||||||
|
virtual int get_stream_caster_rtp_port_max(SrsConfDirective* sc);
|
||||||
// vhost specified section
|
// vhost specified section
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
272
trunk/src/app/srs_app_listener.cpp
Normal file
272
trunk/src/app/srs_app_listener.cpp
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <srs_app_listener.hpp>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <srs_kernel_log.hpp>
|
||||||
|
#include <srs_kernel_error.hpp>
|
||||||
|
#include <srs_app_server.hpp>
|
||||||
|
#include <srs_app_utility.hpp>
|
||||||
|
|
||||||
|
// set the max packet size.
|
||||||
|
#define SRS_UDP_MAX_PACKET_SIZE 65535
|
||||||
|
|
||||||
|
// sleep in ms for udp recv packet.
|
||||||
|
#define SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS 0
|
||||||
|
|
||||||
|
// nginx also set to 512
|
||||||
|
#define SERVER_LISTEN_BACKLOG 512
|
||||||
|
|
||||||
|
ISrsUdpHandler::ISrsUdpHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsUdpHandler::~ISrsUdpHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsTcpHandler::ISrsTcpHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsTcpHandler::~ISrsTcpHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsUdpListener::SrsUdpListener(ISrsUdpHandler* h, int p)
|
||||||
|
{
|
||||||
|
handler = h;
|
||||||
|
port = p;
|
||||||
|
|
||||||
|
_fd = -1;
|
||||||
|
stfd = NULL;
|
||||||
|
|
||||||
|
nb_buf = SRS_UDP_MAX_PACKET_SIZE;
|
||||||
|
buf = new char[nb_buf];
|
||||||
|
|
||||||
|
pthread = new SrsThread("udp", this, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsUdpListener::~SrsUdpListener()
|
||||||
|
{
|
||||||
|
srs_close_stfd(stfd);
|
||||||
|
|
||||||
|
pthread->stop();
|
||||||
|
srs_freep(pthread);
|
||||||
|
|
||||||
|
// st does not close it sometimes,
|
||||||
|
// close it manually.
|
||||||
|
close(_fd);
|
||||||
|
|
||||||
|
srs_freep(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsUdpListener::fd()
|
||||||
|
{
|
||||||
|
return _fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsUdpListener::listen()
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if ((_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||||
|
ret = ERROR_SOCKET_CREATE;
|
||||||
|
srs_error("create linux socket error. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("create linux socket success. port=%d, fd=%d", port, _fd);
|
||||||
|
|
||||||
|
int reuse_socket = 1;
|
||||||
|
if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) {
|
||||||
|
ret = ERROR_SOCKET_SETREUSE;
|
||||||
|
srs_error("setsockopt reuse-addr error. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("setsockopt reuse-addr success. port=%d, fd=%d", port, _fd);
|
||||||
|
|
||||||
|
sockaddr_in addr;
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
if (bind(_fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) {
|
||||||
|
ret = ERROR_SOCKET_BIND;
|
||||||
|
srs_error("bind socket error. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("bind socket success. port=%d, fd=%d", port, _fd);
|
||||||
|
|
||||||
|
if ((stfd = st_netfd_open_socket(_fd)) == NULL){
|
||||||
|
ret = ERROR_ST_OPEN_SOCKET;
|
||||||
|
srs_error("st_netfd_open_socket open socket failed. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("st open socket success. port=%d, fd=%d", port, _fd);
|
||||||
|
|
||||||
|
if ((ret = pthread->start()) != ERROR_SUCCESS) {
|
||||||
|
srs_error("st_thread_create listen thread error. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("create st listen thread success, port=%d", port);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsUdpListener::cycle()
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
// TODO: FIXME: support ipv6, @see man 7 ipv6
|
||||||
|
sockaddr_in from;
|
||||||
|
int nb_from = sizeof(sockaddr_in);
|
||||||
|
int nread = 0;
|
||||||
|
|
||||||
|
if ((nread = st_recvfrom(stfd, buf, nb_buf, (sockaddr*)&from, &nb_from, ST_UTIME_NO_TIMEOUT)) <= 0) {
|
||||||
|
srs_warn("ignore recv udp packet failed, nread=%d", nread);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = handler->on_udp_packet(&from, buf, nread)) != ERROR_SUCCESS) {
|
||||||
|
srs_warn("handle udp packet failed. ret=%d", ret);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS > 0) {
|
||||||
|
st_usleep(SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsTcpListener::SrsTcpListener(ISrsTcpHandler* h, int p)
|
||||||
|
{
|
||||||
|
handler = h;
|
||||||
|
port = p;
|
||||||
|
|
||||||
|
_fd = -1;
|
||||||
|
stfd = NULL;
|
||||||
|
|
||||||
|
pthread = new SrsThread("tcp", this, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsTcpListener::~SrsTcpListener()
|
||||||
|
{
|
||||||
|
srs_close_stfd(stfd);
|
||||||
|
|
||||||
|
pthread->stop();
|
||||||
|
srs_freep(pthread);
|
||||||
|
|
||||||
|
// st does not close it sometimes,
|
||||||
|
// close it manually.
|
||||||
|
close(_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsTcpListener::fd()
|
||||||
|
{
|
||||||
|
return _fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsTcpListener::listen()
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if ((_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||||
|
ret = ERROR_SOCKET_CREATE;
|
||||||
|
srs_error("create linux socket error. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("create linux socket success. port=%d, fd=%d", port, _fd);
|
||||||
|
|
||||||
|
int reuse_socket = 1;
|
||||||
|
if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) {
|
||||||
|
ret = ERROR_SOCKET_SETREUSE;
|
||||||
|
srs_error("setsockopt reuse-addr error. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("setsockopt reuse-addr success. port=%d, fd=%d", port, _fd);
|
||||||
|
|
||||||
|
sockaddr_in addr;
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
if (bind(_fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) {
|
||||||
|
ret = ERROR_SOCKET_BIND;
|
||||||
|
srs_error("bind socket error. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("bind socket success. port=%d, fd=%d", port, _fd);
|
||||||
|
|
||||||
|
if (::listen(_fd, SERVER_LISTEN_BACKLOG) == -1) {
|
||||||
|
ret = ERROR_SOCKET_LISTEN;
|
||||||
|
srs_error("listen socket error. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("listen socket success. port=%d, fd=%d", port, _fd);
|
||||||
|
|
||||||
|
if ((stfd = st_netfd_open_socket(_fd)) == NULL){
|
||||||
|
ret = ERROR_ST_OPEN_SOCKET;
|
||||||
|
srs_error("st_netfd_open_socket open socket failed. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("st open socket success. port=%d, fd=%d", port, _fd);
|
||||||
|
|
||||||
|
if ((ret = pthread->start()) != ERROR_SUCCESS) {
|
||||||
|
srs_error("st_thread_create listen thread error. port=%d, ret=%d", port, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("create st listen thread success, port=%d", port);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsTcpListener::cycle()
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
|
||||||
|
|
||||||
|
if(client_stfd == NULL){
|
||||||
|
// ignore error.
|
||||||
|
srs_error("ignore accept thread stoppped for accept client error");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd));
|
||||||
|
|
||||||
|
if ((ret = handler->on_tcp_client(client_stfd)) != ERROR_SUCCESS) {
|
||||||
|
srs_warn("accept client error. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
125
trunk/src/app/srs_app_listener.hpp
Normal file
125
trunk/src/app/srs_app_listener.hpp
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRS_APP_LISTENER_HPP
|
||||||
|
#define SRS_APP_LISTENER_HPP
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <srs_app_listener.hpp>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
#include <srs_app_st.hpp>
|
||||||
|
#include <srs_app_thread.hpp>
|
||||||
|
|
||||||
|
class sockaddr_in;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the udp packet handler.
|
||||||
|
*/
|
||||||
|
class ISrsUdpHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsUdpHandler();
|
||||||
|
virtual ~ISrsUdpHandler();
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* when udp listener got a udp packet, notice server to process it.
|
||||||
|
* @param type, the client type, used to create concrete connection,
|
||||||
|
* for instance RTMP connection to serve client.
|
||||||
|
* @param from, the udp packet from address.
|
||||||
|
* @param buf, the udp packet bytes, user should copy if need to use.
|
||||||
|
* @param nb_buf, the size of udp packet bytes.
|
||||||
|
* @remark user should never use the buf, for it's a shared memory bytes.
|
||||||
|
*/
|
||||||
|
virtual int on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the tcp connection handler.
|
||||||
|
*/
|
||||||
|
class ISrsTcpHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ISrsTcpHandler();
|
||||||
|
virtual ~ISrsTcpHandler();
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* when got tcp client.
|
||||||
|
*/
|
||||||
|
virtual int on_tcp_client(st_netfd_t stfd) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bind udp port, start thread to recv packet and handler it.
|
||||||
|
*/
|
||||||
|
class SrsUdpListener : public ISrsThreadHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int _fd;
|
||||||
|
st_netfd_t stfd;
|
||||||
|
SrsThread* pthread;
|
||||||
|
private:
|
||||||
|
char* buf;
|
||||||
|
int nb_buf;
|
||||||
|
private:
|
||||||
|
ISrsUdpHandler* handler;
|
||||||
|
int port;
|
||||||
|
public:
|
||||||
|
SrsUdpListener(ISrsUdpHandler* h, int p);
|
||||||
|
virtual ~SrsUdpListener();
|
||||||
|
public:
|
||||||
|
virtual int fd();
|
||||||
|
public:
|
||||||
|
virtual int listen();
|
||||||
|
// interface ISrsThreadHandler.
|
||||||
|
public:
|
||||||
|
virtual int cycle();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bind and listen tcp port, use handler to process the client.
|
||||||
|
*/
|
||||||
|
class SrsTcpListener : public ISrsThreadHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int _fd;
|
||||||
|
st_netfd_t stfd;
|
||||||
|
SrsThread* pthread;
|
||||||
|
private:
|
||||||
|
ISrsTcpHandler* handler;
|
||||||
|
int port;
|
||||||
|
public:
|
||||||
|
SrsTcpListener(ISrsTcpHandler* h, int p);
|
||||||
|
virtual ~SrsTcpListener();
|
||||||
|
public:
|
||||||
|
virtual int fd();
|
||||||
|
public:
|
||||||
|
virtual int listen();
|
||||||
|
// interface ISrsThreadHandler.
|
||||||
|
public:
|
||||||
|
virtual int cycle();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -49,14 +49,6 @@ using namespace std;
|
||||||
#include <srs_rtmp_amf0.hpp>
|
#include <srs_rtmp_amf0.hpp>
|
||||||
#include <srs_raw_avc.hpp>
|
#include <srs_raw_avc.hpp>
|
||||||
|
|
||||||
ISrsUdpHandler::ISrsUdpHandler()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ISrsUdpHandler::~ISrsUdpHandler()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsMpegtsQueue::SrsMpegtsQueue()
|
SrsMpegtsQueue::SrsMpegtsQueue()
|
||||||
{
|
{
|
||||||
nb_audios = nb_videos = 0;
|
nb_audios = nb_videos = 0;
|
||||||
|
|
|
@ -50,27 +50,7 @@ class SrsRawAacStreamCodec;
|
||||||
|
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_kernel_ts.hpp>
|
#include <srs_kernel_ts.hpp>
|
||||||
|
#include <srs_app_listener.hpp>
|
||||||
/**
|
|
||||||
* the udp packet handler.
|
|
||||||
*/
|
|
||||||
class ISrsUdpHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ISrsUdpHandler();
|
|
||||||
virtual ~ISrsUdpHandler();
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* when udp listener got a udp packet, notice server to process it.
|
|
||||||
* @param type, the client type, used to create concrete connection,
|
|
||||||
* for instance RTMP connection to serve client.
|
|
||||||
* @param from, the udp packet from address.
|
|
||||||
* @param buf, the udp packet bytes, user should copy if need to use.
|
|
||||||
* @param nb_buf, the size of udp packet bytes.
|
|
||||||
* @remark user should never use the buf, for it's a shared memory bytes.
|
|
||||||
*/
|
|
||||||
virtual int on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the queue for mpegts over udp to send packets.
|
* the queue for mpegts over udp to send packets.
|
||||||
|
|
|
@ -44,9 +44,14 @@ ISrsRtspHandler::~ISrsRtspHandler()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o)
|
SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o, int lpmin, int lpmax)
|
||||||
{
|
{
|
||||||
output = o;
|
output = o;
|
||||||
|
local_port_min = lpmin;
|
||||||
|
local_port_max = lpmax;
|
||||||
|
|
||||||
|
session = "O9EaZ4bf"; // TODO: FIXME: generate session id.
|
||||||
|
|
||||||
caster = c;
|
caster = c;
|
||||||
stfd = fd;
|
stfd = fd;
|
||||||
skt = new SrsStSocket(fd);
|
skt = new SrsStSocket(fd);
|
||||||
|
@ -97,12 +102,35 @@ int SrsRtspConn::do_cycle()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} else if (req->is_announce()) {
|
} else if (req->is_announce()) {
|
||||||
|
srs_assert(req->sdp);
|
||||||
|
sps = req->sdp->video_sps;
|
||||||
|
pps = req->sdp->video_pps;
|
||||||
|
asc = req->sdp->audio_sh;
|
||||||
|
srs_trace("rtsp: video(#%s, %s), audio(#%s, %s, %sHZ %schannels)",
|
||||||
|
req->sdp->video_stream_id.c_str(), req->sdp->video_codec.c_str(),
|
||||||
|
req->sdp->audio_stream_id.c_str(), req->sdp->audio_codec.c_str(),
|
||||||
|
req->sdp->audio_sample_rate.c_str(), req->sdp->audio_channel.c_str()
|
||||||
|
);
|
||||||
if ((ret = rtsp->send_message(new SrsRtspResponse(req->seq))) != ERROR_SUCCESS) {
|
if ((ret = rtsp->send_message(new SrsRtspResponse(req->seq))) != ERROR_SUCCESS) {
|
||||||
if (!srs_is_client_gracefully_close(ret)) {
|
if (!srs_is_client_gracefully_close(ret)) {
|
||||||
srs_error("rtsp: send ANNOUNCE response failed. ret=%d", ret);
|
srs_error("rtsp: send ANNOUNCE response failed. ret=%d", ret);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
} else if (req->is_setup()) {
|
||||||
|
srs_assert(req->transport);
|
||||||
|
SrsRtspSetupResponse* res = new SrsRtspSetupResponse(req->seq);
|
||||||
|
res->client_port_min = req->transport->client_port_min;
|
||||||
|
res->client_port_max = req->transport->client_port_max;
|
||||||
|
res->local_port_min = local_port_min;
|
||||||
|
res->local_port_max = local_port_max;
|
||||||
|
res->session = session;
|
||||||
|
if ((ret = rtsp->send_message(res)) != ERROR_SUCCESS) {
|
||||||
|
if (!srs_is_client_gracefully_close(ret)) {
|
||||||
|
srs_error("rtsp: send SETUP response failed. ret=%d", ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +172,8 @@ SrsRtspCaster::SrsRtspCaster(SrsConfDirective* c)
|
||||||
{
|
{
|
||||||
// TODO: FIXME: support reload.
|
// TODO: FIXME: support reload.
|
||||||
output = _srs_config->get_stream_caster_output(c);
|
output = _srs_config->get_stream_caster_output(c);
|
||||||
|
local_port_min = _srs_config->get_stream_caster_rtp_port_min(c);
|
||||||
|
local_port_max = _srs_config->get_stream_caster_rtp_port_max(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsRtspCaster::~SrsRtspCaster()
|
SrsRtspCaster::~SrsRtspCaster()
|
||||||
|
@ -160,7 +190,11 @@ int SrsRtspCaster::serve_client(st_netfd_t stfd)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
SrsRtspConn* conn = new SrsRtspConn(this, stfd, output);
|
SrsRtspConn* conn = new SrsRtspConn(
|
||||||
|
this, stfd,
|
||||||
|
output, local_port_min, local_port_max
|
||||||
|
);
|
||||||
|
|
||||||
if ((ret = conn->serve()) != ERROR_SUCCESS) {
|
if ((ret = conn->serve()) != ERROR_SUCCESS) {
|
||||||
srs_error("rtsp: serve client failed. ret=%d", ret);
|
srs_error("rtsp: serve client failed. ret=%d", ret);
|
||||||
srs_freep(conn);
|
srs_freep(conn);
|
||||||
|
|
|
@ -65,13 +65,23 @@ class SrsRtspConn : public ISrsThreadHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string output;
|
std::string output;
|
||||||
|
int local_port_min;
|
||||||
|
int local_port_max;
|
||||||
|
private:
|
||||||
|
std::string session;
|
||||||
|
// video sequence header.
|
||||||
|
std::string sps;
|
||||||
|
std::string pps;
|
||||||
|
// audio sequence header.
|
||||||
|
std::string asc;
|
||||||
|
private:
|
||||||
st_netfd_t stfd;
|
st_netfd_t stfd;
|
||||||
SrsStSocket* skt;
|
SrsStSocket* skt;
|
||||||
SrsRtspStack* rtsp;
|
SrsRtspStack* rtsp;
|
||||||
SrsRtspCaster* caster;
|
SrsRtspCaster* caster;
|
||||||
SrsThread* trd;
|
SrsThread* trd;
|
||||||
public:
|
public:
|
||||||
SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o);
|
SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o, int lpmin, int lpmax);
|
||||||
virtual ~SrsRtspConn();
|
virtual ~SrsRtspConn();
|
||||||
public:
|
public:
|
||||||
virtual int serve();
|
virtual int serve();
|
||||||
|
@ -90,6 +100,9 @@ class SrsRtspCaster : public ISrsRtspHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string output;
|
std::string output;
|
||||||
|
int local_port_min;
|
||||||
|
int local_port_max;
|
||||||
|
private:
|
||||||
std::vector<SrsRtspConn*> clients;
|
std::vector<SrsRtspConn*> clients;
|
||||||
public:
|
public:
|
||||||
SrsRtspCaster(SrsConfDirective* c);
|
SrsRtspCaster(SrsConfDirective* c);
|
||||||
|
|
|
@ -24,8 +24,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
#include <srs_app_server.hpp>
|
#include <srs_app_server.hpp>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
@ -51,15 +49,6 @@ using namespace std;
|
||||||
// signal defines.
|
// signal defines.
|
||||||
#define SIGNAL_RELOAD SIGHUP
|
#define SIGNAL_RELOAD SIGHUP
|
||||||
|
|
||||||
// nginx also set to 512
|
|
||||||
#define SERVER_LISTEN_BACKLOG 512
|
|
||||||
|
|
||||||
// sleep in ms for udp recv packet.
|
|
||||||
#define SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS 0
|
|
||||||
|
|
||||||
// set the max packet size.
|
|
||||||
#define SRS_UDP_MAX_PACKET_SIZE 65535
|
|
||||||
|
|
||||||
// system interval in ms,
|
// system interval in ms,
|
||||||
// all resolution times should be times togother,
|
// all resolution times should be times togother,
|
||||||
// for example, system-interval is x=1s(1000ms),
|
// for example, system-interval is x=1s(1000ms),
|
||||||
|
@ -122,26 +111,13 @@ std::string __srs_listener_type2string(SrsListenerType type)
|
||||||
|
|
||||||
SrsListener::SrsListener(SrsServer* server, SrsListenerType type)
|
SrsListener::SrsListener(SrsServer* server, SrsListenerType type)
|
||||||
{
|
{
|
||||||
fd = -1;
|
|
||||||
stfd = NULL;
|
|
||||||
|
|
||||||
_port = 0;
|
_port = 0;
|
||||||
_server = server;
|
_server = server;
|
||||||
_type = type;
|
_type = type;
|
||||||
|
|
||||||
pthread = new SrsThread("listen", this, 0, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsListener::~SrsListener()
|
SrsListener::~SrsListener()
|
||||||
{
|
{
|
||||||
srs_close_stfd(stfd);
|
|
||||||
|
|
||||||
pthread->stop();
|
|
||||||
srs_freep(pthread);
|
|
||||||
|
|
||||||
// st does not close it sometimes,
|
|
||||||
// close it manually.
|
|
||||||
close(fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsListenerType SrsListener::type()
|
SrsListenerType SrsListener::type()
|
||||||
|
@ -149,92 +125,55 @@ SrsListenerType SrsListener::type()
|
||||||
return _type;
|
return _type;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsListener::listen(int port)
|
SrsStreamListener::SrsStreamListener(SrsServer* server, SrsListenerType type) : SrsListener(server, type)
|
||||||
|
{
|
||||||
|
listener = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsStreamListener::~SrsStreamListener()
|
||||||
|
{
|
||||||
|
srs_freep(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsStreamListener::listen(int port)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
_port = port;
|
_port = port;
|
||||||
|
|
||||||
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
srs_freep(listener);
|
||||||
ret = ERROR_SOCKET_CREATE;
|
listener = new SrsTcpListener(this, port);
|
||||||
srs_error("create linux socket error. port=%d, ret=%d", port, ret);
|
|
||||||
|
if ((ret = listener->listen()) != ERROR_SUCCESS) {
|
||||||
|
srs_error("tcp listen failed. ret=%d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
srs_verbose("create linux socket success. port=%d, fd=%d", port, fd);
|
|
||||||
|
|
||||||
int reuse_socket = 1;
|
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) {
|
|
||||||
ret = ERROR_SOCKET_SETREUSE;
|
|
||||||
srs_error("setsockopt reuse-addr error. port=%d, ret=%d", port, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
srs_verbose("setsockopt reuse-addr success. port=%d, fd=%d", port, fd);
|
|
||||||
|
|
||||||
sockaddr_in addr;
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(_port);
|
|
||||||
addr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
if (bind(fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) {
|
|
||||||
ret = ERROR_SOCKET_BIND;
|
|
||||||
srs_error("bind socket error. port=%d, ret=%d", port, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
srs_verbose("bind socket success. port=%d, fd=%d", port, fd);
|
|
||||||
|
|
||||||
if (::listen(fd, SERVER_LISTEN_BACKLOG) == -1) {
|
|
||||||
ret = ERROR_SOCKET_LISTEN;
|
|
||||||
srs_error("listen socket error. port=%d, ret=%d", port, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
srs_verbose("listen socket success. port=%d, fd=%d", port, fd);
|
|
||||||
|
|
||||||
if ((stfd = st_netfd_open_socket(fd)) == NULL){
|
|
||||||
ret = ERROR_ST_OPEN_SOCKET;
|
|
||||||
srs_error("st_netfd_open_socket open socket failed. port=%d, ret=%d", port, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
srs_verbose("st open socket success. port=%d, fd=%d", port, fd);
|
|
||||||
|
|
||||||
if ((ret = pthread->start()) != ERROR_SUCCESS) {
|
|
||||||
srs_error("st_thread_create listen thread error. port=%d, ret=%d", port, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
srs_verbose("create st listen thread success, port=%d", port);
|
|
||||||
|
|
||||||
srs_info("listen thread cid=%d, current_cid=%d, "
|
srs_info("listen thread cid=%d, current_cid=%d, "
|
||||||
"listen at port=%d, type=%d, fd=%d started success, port=%d",
|
"listen at port=%d, type=%d, fd=%d started success, port=%d",
|
||||||
pthread->cid(), _srs_context->get_id(), _port, _type, fd, port);
|
pthread->cid(), _srs_context->get_id(), _port, _type, fd, port);
|
||||||
|
|
||||||
srs_trace("%s listen at tcp://%d, fd=%d", __srs_listener_type2string(_type).c_str(), _port, fd);
|
srs_trace("%s listen at tcp://%d, fd=%d", __srs_listener_type2string(_type).c_str(), _port, listener->fd());
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsListener::cycle()
|
int SrsStreamListener::on_tcp_client(st_netfd_t stfd)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
|
if ((ret = _server->accept_client(_type, stfd)) != ERROR_SUCCESS) {
|
||||||
|
|
||||||
if(client_stfd == NULL){
|
|
||||||
// ignore error.
|
|
||||||
srs_error("ignore accept thread stoppped for accept client error");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd));
|
|
||||||
|
|
||||||
if ((ret = _server->accept_client(_type, client_stfd)) != ERROR_SUCCESS) {
|
|
||||||
srs_warn("accept client error. ret=%d", ret);
|
srs_warn("accept client error. ret=%d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SRS_AUTO_STREAM_CASTER
|
#ifdef SRS_AUTO_STREAM_CASTER
|
||||||
SrsRtspListener::SrsRtspListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c) : SrsListener(server, type)
|
SrsRtspListener::SrsRtspListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c) : SrsListener(server, type)
|
||||||
{
|
{
|
||||||
_type = type;
|
listener = NULL;
|
||||||
|
|
||||||
// the caller already ensure the type is ok,
|
// the caller already ensure the type is ok,
|
||||||
// we just assert here for unknown stream caster.
|
// we just assert here for unknown stream caster.
|
||||||
|
@ -247,34 +186,52 @@ SrsRtspListener::SrsRtspListener(SrsServer* server, SrsListenerType type, SrsCon
|
||||||
SrsRtspListener::~SrsRtspListener()
|
SrsRtspListener::~SrsRtspListener()
|
||||||
{
|
{
|
||||||
srs_freep(caster);
|
srs_freep(caster);
|
||||||
|
srs_freep(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsRtspListener::cycle()
|
int SrsRtspListener::listen(int port)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// the caller already ensure the type is ok,
|
||||||
|
// we just assert here for unknown stream caster.
|
||||||
|
srs_assert(_type == SrsListenerRtsp);
|
||||||
|
|
||||||
st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
|
_port = port;
|
||||||
|
|
||||||
if(client_stfd == NULL){
|
srs_freep(listener);
|
||||||
// ignore error.
|
listener = new SrsTcpListener(this, port);
|
||||||
srs_error("ignore accept thread stoppped for accept client error");
|
|
||||||
return ret;
|
if ((ret = listener->listen()) != ERROR_SUCCESS) {
|
||||||
}
|
srs_error("udp caster listen failed. ret=%d", ret);
|
||||||
srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd));
|
|
||||||
|
|
||||||
if ((ret = caster->serve_client(client_stfd)) != ERROR_SUCCESS) {
|
|
||||||
srs_warn("accept client error. ret=%d", ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srs_info("listen thread cid=%d, current_cid=%d, "
|
||||||
|
"listen at port=%d, type=%d, fd=%d started success, port=%d",
|
||||||
|
pthread->cid(), _srs_context->get_id(), _port, _type, fd, port);
|
||||||
|
|
||||||
|
srs_trace("%s listen at tcp://%d, fd=%d", __srs_listener_type2string(_type).c_str(), _port, listener->fd());
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsUdpListener::SrsUdpListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c) : SrsListener(server, type)
|
int SrsRtspListener::on_tcp_client(st_netfd_t stfd)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if ((ret = caster->serve_client(stfd)) != ERROR_SUCCESS) {
|
||||||
|
srs_warn("accept client error. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsUdpCasterListener::SrsUdpCasterListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c) : SrsListener(server, type)
|
||||||
{
|
{
|
||||||
_type = type;
|
_type = type;
|
||||||
nb_buf = SRS_UDP_MAX_PACKET_SIZE;
|
listener = NULL;
|
||||||
buf = new char[nb_buf];
|
|
||||||
|
|
||||||
// the caller already ensure the type is ok,
|
// the caller already ensure the type is ok,
|
||||||
// we just assert here for unknown stream caster.
|
// we just assert here for unknown stream caster.
|
||||||
|
@ -284,13 +241,13 @@ SrsUdpListener::SrsUdpListener(SrsServer* server, SrsListenerType type, SrsConfD
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsUdpListener::~SrsUdpListener()
|
SrsUdpCasterListener::~SrsUdpCasterListener()
|
||||||
{
|
{
|
||||||
srs_freep(caster);
|
srs_freep(caster);
|
||||||
srs_freep(buf);
|
srs_freep(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsUdpListener::listen(int port)
|
int SrsUdpCasterListener::listen(int port)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -299,83 +256,20 @@ int SrsUdpListener::listen(int port)
|
||||||
srs_assert(_type == SrsListenerMpegTsOverUdp);
|
srs_assert(_type == SrsListenerMpegTsOverUdp);
|
||||||
|
|
||||||
_port = port;
|
_port = port;
|
||||||
|
|
||||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
srs_freep(listener);
|
||||||
ret = ERROR_SOCKET_CREATE;
|
listener = new SrsUdpListener(caster, port);
|
||||||
srs_error("create linux socket error. port=%d, ret=%d", port, ret);
|
|
||||||
|
if ((ret = listener->listen()) != ERROR_SUCCESS) {
|
||||||
|
srs_error("udp caster listen failed. ret=%d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
srs_verbose("create linux socket success. port=%d, fd=%d", port, fd);
|
|
||||||
|
|
||||||
int reuse_socket = 1;
|
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) {
|
|
||||||
ret = ERROR_SOCKET_SETREUSE;
|
|
||||||
srs_error("setsockopt reuse-addr error. port=%d, ret=%d", port, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
srs_verbose("setsockopt reuse-addr success. port=%d, fd=%d", port, fd);
|
|
||||||
|
|
||||||
sockaddr_in addr;
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(_port);
|
|
||||||
addr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
if (bind(fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) {
|
|
||||||
ret = ERROR_SOCKET_BIND;
|
|
||||||
srs_error("bind socket error. port=%d, ret=%d", port, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
srs_verbose("bind socket success. port=%d, fd=%d", port, fd);
|
|
||||||
|
|
||||||
if ((stfd = st_netfd_open_socket(fd)) == NULL){
|
|
||||||
ret = ERROR_ST_OPEN_SOCKET;
|
|
||||||
srs_error("st_netfd_open_socket open socket failed. port=%d, ret=%d", port, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
srs_verbose("st open socket success. port=%d, fd=%d", port, fd);
|
|
||||||
|
|
||||||
if ((ret = pthread->start()) != ERROR_SUCCESS) {
|
|
||||||
srs_error("st_thread_create listen thread error. port=%d, ret=%d", port, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
srs_verbose("create st listen thread success, port=%d", port);
|
|
||||||
|
|
||||||
srs_info("listen thread cid=%d, current_cid=%d, "
|
srs_info("listen thread cid=%d, current_cid=%d, "
|
||||||
"listen at port=%d, type=%d, fd=%d started success, port=%d",
|
"listen at port=%d, type=%d, fd=%d started success, port=%d",
|
||||||
pthread->cid(), _srs_context->get_id(), _port, _type, fd, port);
|
pthread->cid(), _srs_context->get_id(), _port, _type, fd, port);
|
||||||
|
|
||||||
srs_trace("%s listen at udp://%d, fd=%d", __srs_listener_type2string(_type).c_str(), _port, fd);
|
srs_trace("%s listen at udp://%d, fd=%d", __srs_listener_type2string(_type).c_str(), _port, listener->fd());
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SrsUdpListener::cycle()
|
|
||||||
{
|
|
||||||
int ret = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
// the caller already ensure the type is ok,
|
|
||||||
// we just assert here for unknown stream caster.
|
|
||||||
srs_assert(_type == SrsListenerMpegTsOverUdp);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
// TODO: FIXME: support ipv6, @see man 7 ipv6
|
|
||||||
sockaddr_in from;
|
|
||||||
int nb_from = sizeof(sockaddr_in);
|
|
||||||
int nread = 0;
|
|
||||||
|
|
||||||
if ((nread = st_recvfrom(stfd, buf, nb_buf, (sockaddr*)&from, &nb_from, ST_UTIME_NO_TIMEOUT)) <= 0) {
|
|
||||||
srs_warn("ignore recv udp packet failed, nread=%d", nread);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ret = caster->on_udp_packet(&from, buf, nread)) != ERROR_SUCCESS) {
|
|
||||||
srs_warn("handle udp packet failed. ret=%d", ret);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS > 0) {
|
|
||||||
st_usleep(SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS * 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -992,7 +886,7 @@ int SrsServer::listen_rtmp()
|
||||||
close_listeners(SrsListenerRtmpStream);
|
close_listeners(SrsListenerRtmpStream);
|
||||||
|
|
||||||
for (int i = 0; i < (int)ports.size(); i++) {
|
for (int i = 0; i < (int)ports.size(); i++) {
|
||||||
SrsListener* listener = new SrsListener(this, SrsListenerRtmpStream);
|
SrsListener* listener = new SrsStreamListener(this, SrsListenerRtmpStream);
|
||||||
listeners.push_back(listener);
|
listeners.push_back(listener);
|
||||||
|
|
||||||
int port = ::atoi(ports[i].c_str());
|
int port = ::atoi(ports[i].c_str());
|
||||||
|
@ -1012,7 +906,7 @@ int SrsServer::listen_http_api()
|
||||||
#ifdef SRS_AUTO_HTTP_API
|
#ifdef SRS_AUTO_HTTP_API
|
||||||
close_listeners(SrsListenerHttpApi);
|
close_listeners(SrsListenerHttpApi);
|
||||||
if (_srs_config->get_http_api_enabled()) {
|
if (_srs_config->get_http_api_enabled()) {
|
||||||
SrsListener* listener = new SrsListener(this, SrsListenerHttpApi);
|
SrsListener* listener = new SrsStreamListener(this, SrsListenerHttpApi);
|
||||||
listeners.push_back(listener);
|
listeners.push_back(listener);
|
||||||
|
|
||||||
int port = _srs_config->get_http_api_listen();
|
int port = _srs_config->get_http_api_listen();
|
||||||
|
@ -1033,7 +927,7 @@ int SrsServer::listen_http_stream()
|
||||||
#ifdef SRS_AUTO_HTTP_SERVER
|
#ifdef SRS_AUTO_HTTP_SERVER
|
||||||
close_listeners(SrsListenerHttpStream);
|
close_listeners(SrsListenerHttpStream);
|
||||||
if (_srs_config->get_http_stream_enabled()) {
|
if (_srs_config->get_http_stream_enabled()) {
|
||||||
SrsListener* listener = new SrsListener(this, SrsListenerHttpStream);
|
SrsListener* listener = new SrsStreamListener(this, SrsListenerHttpStream);
|
||||||
listeners.push_back(listener);
|
listeners.push_back(listener);
|
||||||
|
|
||||||
int port = _srs_config->get_http_stream_listen();
|
int port = _srs_config->get_http_stream_listen();
|
||||||
|
@ -1067,7 +961,7 @@ int SrsServer::listen_stream_caster()
|
||||||
|
|
||||||
std::string caster = _srs_config->get_stream_caster_engine(stream_caster);
|
std::string caster = _srs_config->get_stream_caster_engine(stream_caster);
|
||||||
if (caster == SRS_CONF_DEFAULT_STREAM_CASTER_MPEGTS_OVER_UDP) {
|
if (caster == SRS_CONF_DEFAULT_STREAM_CASTER_MPEGTS_OVER_UDP) {
|
||||||
listener = new SrsUdpListener(this, SrsListenerMpegTsOverUdp, stream_caster);
|
listener = new SrsUdpCasterListener(this, SrsListenerMpegTsOverUdp, stream_caster);
|
||||||
} else if (caster == SRS_CONF_DEFAULT_STREAM_CASTER_RTSP) {
|
} else if (caster == SRS_CONF_DEFAULT_STREAM_CASTER_RTSP) {
|
||||||
listener = new SrsRtspListener(this, SrsListenerRtsp, stream_caster);
|
listener = new SrsRtspListener(this, SrsListenerRtsp, stream_caster);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -35,9 +35,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_reload.hpp>
|
#include <srs_app_reload.hpp>
|
||||||
#include <srs_app_thread.hpp>
|
|
||||||
#include <srs_app_source.hpp>
|
#include <srs_app_source.hpp>
|
||||||
#include <srs_app_hls.hpp>
|
#include <srs_app_hls.hpp>
|
||||||
|
#include <srs_app_listener.hpp>
|
||||||
|
|
||||||
class SrsServer;
|
class SrsServer;
|
||||||
class SrsConnection;
|
class SrsConnection;
|
||||||
|
@ -49,6 +49,8 @@ class SrsKbps;
|
||||||
class SrsConfDirective;
|
class SrsConfDirective;
|
||||||
class ISrsUdpHandler;
|
class ISrsUdpHandler;
|
||||||
class ISrsRtspHandler;
|
class ISrsRtspHandler;
|
||||||
|
class SrsUdpListener;
|
||||||
|
class SrsTcpListener;
|
||||||
|
|
||||||
// listener type for server to identify the connection,
|
// listener type for server to identify the connection,
|
||||||
// that is, use different type to process the connection.
|
// that is, use different type to process the connection.
|
||||||
|
@ -69,60 +71,71 @@ enum SrsListenerType
|
||||||
/**
|
/**
|
||||||
* the common tcp listener, for RTMP/HTTP server.
|
* the common tcp listener, for RTMP/HTTP server.
|
||||||
*/
|
*/
|
||||||
class SrsListener : public ISrsThreadHandler
|
class SrsListener
|
||||||
{
|
{
|
||||||
public:
|
protected:
|
||||||
SrsListenerType _type;
|
SrsListenerType _type;
|
||||||
protected:
|
protected:
|
||||||
int fd;
|
|
||||||
st_netfd_t stfd;
|
|
||||||
int _port;
|
int _port;
|
||||||
SrsServer* _server;
|
SrsServer* _server;
|
||||||
SrsThread* pthread;
|
|
||||||
public:
|
public:
|
||||||
SrsListener(SrsServer* server, SrsListenerType type);
|
SrsListener(SrsServer* server, SrsListenerType type);
|
||||||
virtual ~SrsListener();
|
virtual ~SrsListener();
|
||||||
public:
|
public:
|
||||||
virtual SrsListenerType type();
|
virtual SrsListenerType type();
|
||||||
virtual int listen(int port);
|
virtual int listen(int port) = 0;
|
||||||
// interface ISrsThreadHandler.
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tcp listener.
|
||||||
|
*/
|
||||||
|
class SrsStreamListener : virtual public SrsListener, virtual public ISrsTcpHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsTcpListener* listener;
|
||||||
public:
|
public:
|
||||||
virtual int cycle();
|
SrsStreamListener(SrsServer* server, SrsListenerType type);
|
||||||
|
virtual ~SrsStreamListener();
|
||||||
|
public:
|
||||||
|
virtual int listen(int port);
|
||||||
|
// ISrsTcpHandler
|
||||||
|
public:
|
||||||
|
virtual int on_tcp_client(st_netfd_t stfd);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef SRS_AUTO_STREAM_CASTER
|
#ifdef SRS_AUTO_STREAM_CASTER
|
||||||
/**
|
/**
|
||||||
* the tcp listener, for rtsp server.
|
* the tcp listener, for rtsp server.
|
||||||
*/
|
*/
|
||||||
class SrsRtspListener : public SrsListener
|
class SrsRtspListener : virtual public SrsListener, virtual public ISrsTcpHandler
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
SrsTcpListener* listener;
|
||||||
private:
|
private:
|
||||||
ISrsRtspHandler* caster;
|
ISrsRtspHandler* caster;
|
||||||
public:
|
public:
|
||||||
SrsRtspListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c);
|
SrsRtspListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c);
|
||||||
virtual ~SrsRtspListener();
|
virtual ~SrsRtspListener();
|
||||||
// interface ISrsThreadHandler.
|
|
||||||
public:
|
public:
|
||||||
virtual int cycle();
|
virtual int listen(int port);
|
||||||
|
// ISrsTcpHandler
|
||||||
|
public:
|
||||||
|
virtual int on_tcp_client(st_netfd_t stfd);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the udp listener, for udp server.
|
* the udp listener, for udp server.
|
||||||
*/
|
*/
|
||||||
class SrsUdpListener : public SrsListener
|
class SrsUdpCasterListener : public SrsListener
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
char* buf;
|
SrsUdpListener* listener;
|
||||||
int nb_buf;
|
|
||||||
ISrsUdpHandler* caster;
|
ISrsUdpHandler* caster;
|
||||||
public:
|
public:
|
||||||
SrsUdpListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c);
|
SrsUdpCasterListener(SrsServer* server, SrsListenerType type, SrsConfDirective* c);
|
||||||
virtual ~SrsUdpListener();
|
virtual ~SrsUdpCasterListener();
|
||||||
public:
|
public:
|
||||||
virtual int listen(int port);
|
virtual int listen(int port);
|
||||||
// interface ISrsThreadHandler.
|
|
||||||
public:
|
|
||||||
virtual int cycle();
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -231,9 +231,9 @@ int SrsRtspSdp::parse(string token)
|
||||||
audio_sample_rate = audio_codec.substr(pos + 1);
|
audio_sample_rate = audio_codec.substr(pos + 1);
|
||||||
audio_codec = audio_codec.substr(0, pos);
|
audio_codec = audio_codec.substr(0, pos);
|
||||||
}
|
}
|
||||||
if ((pos = audio_codec.find("/")) != string::npos) {
|
if ((pos = audio_sample_rate.find("/")) != string::npos) {
|
||||||
audio_channel = audio_codec.substr(pos + 1);
|
audio_channel = audio_sample_rate.substr(pos + 1);
|
||||||
audio_codec = audio_codec.substr(0, pos);
|
audio_sample_rate = audio_sample_rate.substr(0, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (desc_key == "fmtp") {
|
} else if (desc_key == "fmtp") {
|
||||||
|
@ -283,19 +283,20 @@ int SrsRtspSdp::parse(string token)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsRtspSdp::parse_fmtp_attribute(string& attr)
|
int SrsRtspSdp::parse_fmtp_attribute(string attr)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
size_t pos = string::npos;
|
size_t pos = string::npos;
|
||||||
|
std::string token = attr;
|
||||||
|
|
||||||
while (!attr.empty()) {
|
while (!token.empty()) {
|
||||||
std::string item = attr;
|
std::string item = token;
|
||||||
if ((pos = item.find(";")) != string::npos) {
|
if ((pos = item.find(";")) != string::npos) {
|
||||||
item = attr.substr(0, pos);
|
item = token.substr(0, pos);
|
||||||
attr = attr.substr(pos + 1);
|
token = token.substr(pos + 1);
|
||||||
} else {
|
} else {
|
||||||
attr = "";
|
token = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string item_key = item, item_value;
|
std::string item_key = item, item_value;
|
||||||
|
@ -337,19 +338,20 @@ int SrsRtspSdp::parse_fmtp_attribute(string& attr)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SrsRtspSdp::parse_control_attribute(string& attr)
|
int SrsRtspSdp::parse_control_attribute(string attr)
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
size_t pos = string::npos;
|
size_t pos = string::npos;
|
||||||
|
std::string token = attr;
|
||||||
|
|
||||||
while (!attr.empty()) {
|
while (!token.empty()) {
|
||||||
std::string item = attr;
|
std::string item = token;
|
||||||
if ((pos = item.find(";")) != string::npos) {
|
if ((pos = item.find(";")) != string::npos) {
|
||||||
item = attr.substr(0, pos);
|
item = token.substr(0, pos);
|
||||||
attr = attr.substr(pos + 1);
|
token = token.substr(pos + 1);
|
||||||
} else {
|
} else {
|
||||||
attr = "";
|
token = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string item_key = item, item_value;
|
std::string item_key = item, item_value;
|
||||||
|
@ -392,16 +394,81 @@ string SrsRtspSdp::base64_decode(string value)
|
||||||
return plaintext;
|
return plaintext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsRtspTransport::SrsRtspTransport()
|
||||||
|
{
|
||||||
|
client_port_min = 0;
|
||||||
|
client_port_max = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtspTransport::~SrsRtspTransport()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsRtspTransport::parse(string attr)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
size_t pos = string::npos;
|
||||||
|
std::string token = attr;
|
||||||
|
|
||||||
|
while (!token.empty()) {
|
||||||
|
std::string item = token;
|
||||||
|
if ((pos = item.find(";")) != string::npos) {
|
||||||
|
item = token.substr(0, pos);
|
||||||
|
token = token.substr(pos + 1);
|
||||||
|
} else {
|
||||||
|
token = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string item_key = item, item_value;
|
||||||
|
if ((pos = item.find("=")) != string::npos) {
|
||||||
|
item_key = item.substr(0, pos);
|
||||||
|
item_value = item.substr(pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transport.empty()) {
|
||||||
|
transport = item_key;
|
||||||
|
if ((pos = transport.find("/")) != string::npos) {
|
||||||
|
profile = transport.substr(pos + 1);
|
||||||
|
transport = transport.substr(0, pos);
|
||||||
|
}
|
||||||
|
if ((pos = profile.find("/")) != string::npos) {
|
||||||
|
lower_transport = profile.substr(pos + 1);
|
||||||
|
profile = profile.substr(0, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item_key == "unicast" || item_key == "multicast") {
|
||||||
|
cast_type = item_key;
|
||||||
|
} else if (item_key == "mode") {
|
||||||
|
mode = item_value;
|
||||||
|
} else if (item_key == "client_port") {
|
||||||
|
std::string sport = item_value;
|
||||||
|
std::string eport = item_value;
|
||||||
|
if ((pos = eport.find("-")) != string::npos) {
|
||||||
|
sport = eport.substr(0, pos);
|
||||||
|
eport = eport.substr(pos + 1);
|
||||||
|
}
|
||||||
|
client_port_min = ::atoi(sport.c_str());
|
||||||
|
client_port_max = ::atoi(eport.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
SrsRtspRequest::SrsRtspRequest()
|
SrsRtspRequest::SrsRtspRequest()
|
||||||
{
|
{
|
||||||
seq = 0;
|
seq = 0;
|
||||||
content_length = 0;
|
content_length = 0;
|
||||||
sdp = NULL;
|
sdp = NULL;
|
||||||
|
transport = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsRtspRequest::~SrsRtspRequest()
|
SrsRtspRequest::~SrsRtspRequest()
|
||||||
{
|
{
|
||||||
srs_freep(sdp);
|
srs_freep(sdp);
|
||||||
|
srs_freep(transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SrsRtspRequest::is_options()
|
bool SrsRtspRequest::is_options()
|
||||||
|
@ -414,6 +481,11 @@ bool SrsRtspRequest::is_announce()
|
||||||
return method == __SRS_METHOD_ANNOUNCE;
|
return method == __SRS_METHOD_ANNOUNCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SrsRtspRequest::is_setup()
|
||||||
|
{
|
||||||
|
return method == __SRS_METHOD_SETUP;
|
||||||
|
}
|
||||||
|
|
||||||
SrsRtspResponse::SrsRtspResponse(int cseq)
|
SrsRtspResponse::SrsRtspResponse(int cseq)
|
||||||
{
|
{
|
||||||
seq = cseq;
|
seq = cseq;
|
||||||
|
@ -424,8 +496,10 @@ SrsRtspResponse::~SrsRtspResponse()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
stringstream& SrsRtspResponse::encode(stringstream& ss)
|
int SrsRtspResponse::encode(stringstream& ss)
|
||||||
{
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
// status line
|
// status line
|
||||||
ss << __SRS_VERSION << __SRS_RTSP_SP
|
ss << __SRS_VERSION << __SRS_RTSP_SP
|
||||||
<< status << __SRS_RTSP_SP
|
<< status << __SRS_RTSP_SP
|
||||||
|
@ -439,7 +513,20 @@ stringstream& SrsRtspResponse::encode(stringstream& ss)
|
||||||
<< "Pragma: no-cache" << __SRS_RTSP_CRLF
|
<< "Pragma: no-cache" << __SRS_RTSP_CRLF
|
||||||
<< "Server: " << RTMP_SIG_SRS_SERVER << __SRS_RTSP_CRLF;
|
<< "Server: " << RTMP_SIG_SRS_SERVER << __SRS_RTSP_CRLF;
|
||||||
|
|
||||||
return ss;
|
if ((ret = encode_header(ss)) != ERROR_SUCCESS) {
|
||||||
|
srs_error("rtsp: encode header failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
// header EOF.
|
||||||
|
ss << __SRS_RTSP_CRLF;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsRtspResponse::encode_header(std::stringstream& ss)
|
||||||
|
{
|
||||||
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsRtspOptionsResponse::SrsRtspOptionsResponse(int cseq) : SrsRtspResponse(cseq)
|
SrsRtspOptionsResponse::SrsRtspOptionsResponse(int cseq) : SrsRtspResponse(cseq)
|
||||||
|
@ -453,10 +540,8 @@ SrsRtspOptionsResponse::~SrsRtspOptionsResponse()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
stringstream& SrsRtspOptionsResponse::encode(stringstream& ss)
|
int SrsRtspOptionsResponse::encode_header(stringstream& ss)
|
||||||
{
|
{
|
||||||
SrsRtspResponse::encode(ss);
|
|
||||||
|
|
||||||
SrsRtspMethod __methods[] = {
|
SrsRtspMethod __methods[] = {
|
||||||
SrsRtspMethodDescribe,
|
SrsRtspMethodDescribe,
|
||||||
SrsRtspMethodAnnounce,
|
SrsRtspMethodAnnounce,
|
||||||
|
@ -489,10 +574,27 @@ stringstream& SrsRtspOptionsResponse::encode(stringstream& ss)
|
||||||
}
|
}
|
||||||
ss << __SRS_RTSP_CRLF;
|
ss << __SRS_RTSP_CRLF;
|
||||||
|
|
||||||
// eof header.
|
return ERROR_SUCCESS;
|
||||||
ss << __SRS_RTSP_CRLF;
|
}
|
||||||
|
|
||||||
return ss;
|
SrsRtspSetupResponse::SrsRtspSetupResponse(int seq) : SrsRtspResponse(seq)
|
||||||
|
{
|
||||||
|
local_port_min = 0;
|
||||||
|
local_port_max = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsRtspSetupResponse::~SrsRtspSetupResponse()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsRtspSetupResponse::encode_header(stringstream& ss)
|
||||||
|
{
|
||||||
|
ss << __SRS_TOKEN_SESSION << ":" << __SRS_RTSP_SP << session << __SRS_RTSP_CRLF;
|
||||||
|
ss << __SRS_TOKEN_TRANSPORT << ":" << __SRS_RTSP_SP
|
||||||
|
<< "RTP/AVP;unicast;client_port=" << client_port_min << "-" << client_port_max << ";"
|
||||||
|
<< "server_port=" << local_port_min << "-" << local_port_max
|
||||||
|
<< __SRS_RTSP_CRLF;
|
||||||
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsRtspStack::SrsRtspStack(ISrsProtocolReaderWriter* s)
|
SrsRtspStack::SrsRtspStack(ISrsProtocolReaderWriter* s)
|
||||||
|
@ -613,6 +715,21 @@ int SrsRtspStack::do_recv_message(SrsRtspRequest* req)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
req->content_length = ::atol(cl.c_str());
|
req->content_length = ::atol(cl.c_str());
|
||||||
|
} else if (token == __SRS_TOKEN_TRANSPORT) {
|
||||||
|
std::string transport;
|
||||||
|
if ((ret = recv_token_eof(transport)) != ERROR_SUCCESS) {
|
||||||
|
if (!srs_is_client_gracefully_close(ret)) {
|
||||||
|
srs_error("rtsp: parse %s failed. ret=%d", __SRS_TOKEN_TRANSPORT, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (!req->transport) {
|
||||||
|
req->transport = new SrsRtspTransport();
|
||||||
|
}
|
||||||
|
if ((ret = req->transport->parse(transport)) != ERROR_SUCCESS) {
|
||||||
|
srs_error("rtsp: parse transport failed, transport=%s. ret=%d", transport.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// unknown header name, parse util EOF.
|
// unknown header name, parse util EOF.
|
||||||
SrsRtspTokenState state = SrsRtspTokenStateNormal;
|
SrsRtspTokenState state = SrsRtspTokenStateNormal;
|
||||||
|
|
|
@ -60,6 +60,8 @@ class ISrsProtocolReaderWriter;
|
||||||
#define __SRS_TOKEN_PUBLIC "Public"
|
#define __SRS_TOKEN_PUBLIC "Public"
|
||||||
#define __SRS_TOKEN_CONTENT_TYPE "Content-Type"
|
#define __SRS_TOKEN_CONTENT_TYPE "Content-Type"
|
||||||
#define __SRS_TOKEN_CONTENT_LENGTH "Content-Length"
|
#define __SRS_TOKEN_CONTENT_LENGTH "Content-Length"
|
||||||
|
#define __SRS_TOKEN_TRANSPORT "Transport"
|
||||||
|
#define __SRS_TOKEN_SESSION "Session"
|
||||||
|
|
||||||
// RTSP methods
|
// RTSP methods
|
||||||
#define __SRS_METHOD_OPTIONS "OPTIONS"
|
#define __SRS_METHOD_OPTIONS "OPTIONS"
|
||||||
|
@ -97,6 +99,53 @@ enum SrsRtspSdpState
|
||||||
SrsRtspSdpStateVideo,
|
SrsRtspSdpStateVideo,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 10 Method Definitions
|
||||||
|
* The method token indicates the method to be performed on the resource
|
||||||
|
* identified by the Request-URI. The method is case-sensitive. New
|
||||||
|
* methods may be defined in the future. Method names may not start with
|
||||||
|
* a $ character (decimal 24) and must be a token. Methods are
|
||||||
|
* summarized in Table 2.
|
||||||
|
* Notes on Table 2: PAUSE is recommended, but not required in that a
|
||||||
|
* fully functional server can be built that does not support this
|
||||||
|
* method, for example, for live feeds. If a server does not support a
|
||||||
|
* particular method, it MUST return "501 Not Implemented" and a client
|
||||||
|
* SHOULD not try this method again for this server.
|
||||||
|
*/
|
||||||
|
enum SrsRtspMethod
|
||||||
|
{
|
||||||
|
SrsRtspMethodDescribe = 0x0001,
|
||||||
|
SrsRtspMethodAnnounce = 0x0002,
|
||||||
|
SrsRtspMethodGetParameter = 0x0004,
|
||||||
|
SrsRtspMethodOptions = 0x0008,
|
||||||
|
SrsRtspMethodPause = 0x0010,
|
||||||
|
SrsRtspMethodPlay = 0x0020,
|
||||||
|
SrsRtspMethodRecord = 0x0040,
|
||||||
|
SrsRtspMethodRedirect = 0x0080,
|
||||||
|
SrsRtspMethodSetup = 0x0100,
|
||||||
|
SrsRtspMethodSetParameter = 0x0200,
|
||||||
|
SrsRtspMethodTeardown = 0x0400,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the state of rtsp token.
|
||||||
|
*/
|
||||||
|
enum SrsRtspTokenState
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* parse token failed, default state.
|
||||||
|
*/
|
||||||
|
SrsRtspTokenStateError = 100,
|
||||||
|
/**
|
||||||
|
* when SP follow the token.
|
||||||
|
*/
|
||||||
|
SrsRtspTokenStateNormal = 101,
|
||||||
|
/**
|
||||||
|
* when CRLF follow the token.
|
||||||
|
*/
|
||||||
|
SrsRtspTokenStateEOF = 102,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the sdp in announce.
|
* the sdp in announce.
|
||||||
* Appendix C: Use of SDP for RTSP Session Descriptions
|
* Appendix C: Use of SDP for RTSP Session Descriptions
|
||||||
|
@ -179,17 +228,64 @@ private:
|
||||||
/**
|
/**
|
||||||
* generally, the fmtp is the sequence header for video or audio.
|
* generally, the fmtp is the sequence header for video or audio.
|
||||||
*/
|
*/
|
||||||
virtual int parse_fmtp_attribute(std::string& attr);
|
virtual int parse_fmtp_attribute(std::string attr);
|
||||||
/**
|
/**
|
||||||
* generally, the control is the stream info for video or audio.
|
* generally, the control is the stream info for video or audio.
|
||||||
*/
|
*/
|
||||||
virtual int parse_control_attribute(std::string& attr);
|
virtual int parse_control_attribute(std::string attr);
|
||||||
/**
|
/**
|
||||||
* decode the string by base64.
|
* decode the string by base64.
|
||||||
*/
|
*/
|
||||||
virtual std::string base64_decode(std::string value);
|
virtual std::string base64_decode(std::string value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the rtsp transport.
|
||||||
|
* 12.39 Transport
|
||||||
|
* This request header indicates which transport protocol is to be used
|
||||||
|
* and configures its parameters such as destination address,
|
||||||
|
* compression, multicast time-to-live and destination port for a single
|
||||||
|
* stream. It sets those values not already determined by a presentation
|
||||||
|
* description.
|
||||||
|
*/
|
||||||
|
class SrsRtspTransport
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// The syntax for the transport specifier is
|
||||||
|
// transport/profile/lower-transport
|
||||||
|
std::string transport;
|
||||||
|
std::string profile;
|
||||||
|
std::string lower_transport;
|
||||||
|
// unicast | multicast
|
||||||
|
// mutually exclusive indication of whether unicast or multicast
|
||||||
|
// delivery will be attempted. Default value is multicast.
|
||||||
|
// Clients that are capable of handling both unicast and
|
||||||
|
// multicast transmission MUST indicate such capability by
|
||||||
|
// including two full transport-specs with separate parameters
|
||||||
|
// for each.
|
||||||
|
std::string cast_type;
|
||||||
|
// The mode parameter indicates the methods to be supported for
|
||||||
|
// this session. Valid values are PLAY and RECORD. If not
|
||||||
|
// provided, the default is PLAY.
|
||||||
|
std::string mode;
|
||||||
|
// This parameter provides the unicast RTP/RTCP port pair on
|
||||||
|
// which the client has chosen to receive media data and control
|
||||||
|
// information. It is specified as a range, e.g.,
|
||||||
|
// client_port=3456-3457.
|
||||||
|
// where client will use port in:
|
||||||
|
// [client_port_min, client_port_max)
|
||||||
|
int client_port_min;
|
||||||
|
int client_port_max;
|
||||||
|
public:
|
||||||
|
SrsRtspTransport();
|
||||||
|
virtual ~SrsRtspTransport();
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* parse a line of token for transport.
|
||||||
|
*/
|
||||||
|
virtual int parse(std::string attr);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the rtsp request message.
|
* the rtsp request message.
|
||||||
* 6 Request
|
* 6 Request
|
||||||
|
@ -245,12 +341,17 @@ public:
|
||||||
* the sdp in announce, NULL for no sdp.
|
* the sdp in announce, NULL for no sdp.
|
||||||
*/
|
*/
|
||||||
SrsRtspSdp* sdp;
|
SrsRtspSdp* sdp;
|
||||||
|
/**
|
||||||
|
* the transport in setup, NULL for no transport.
|
||||||
|
*/
|
||||||
|
SrsRtspTransport* transport;
|
||||||
public:
|
public:
|
||||||
SrsRtspRequest();
|
SrsRtspRequest();
|
||||||
virtual ~SrsRtspRequest();
|
virtual ~SrsRtspRequest();
|
||||||
public:
|
public:
|
||||||
virtual bool is_options();
|
virtual bool is_options();
|
||||||
virtual bool is_announce();
|
virtual bool is_announce();
|
||||||
|
virtual bool is_setup();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -302,35 +403,12 @@ public:
|
||||||
/**
|
/**
|
||||||
* encode message to string.
|
* encode message to string.
|
||||||
*/
|
*/
|
||||||
virtual std::stringstream& encode(std::stringstream& ss);
|
virtual int encode(std::stringstream& ss);
|
||||||
};
|
protected:
|
||||||
|
/**
|
||||||
/**
|
* sub classes override this to encode the headers.
|
||||||
* 10 Method Definitions
|
*/
|
||||||
* The method token indicates the method to be performed on the resource
|
virtual int encode_header(std::stringstream& ss);
|
||||||
* identified by the Request-URI. The method is case-sensitive. New
|
|
||||||
* methods may be defined in the future. Method names may not start with
|
|
||||||
* a $ character (decimal 24) and must be a token. Methods are
|
|
||||||
* summarized in Table 2.
|
|
||||||
* Notes on Table 2: PAUSE is recommended, but not required in that a
|
|
||||||
* fully functional server can be built that does not support this
|
|
||||||
* method, for example, for live feeds. If a server does not support a
|
|
||||||
* particular method, it MUST return "501 Not Implemented" and a client
|
|
||||||
* SHOULD not try this method again for this server.
|
|
||||||
*/
|
|
||||||
enum SrsRtspMethod
|
|
||||||
{
|
|
||||||
SrsRtspMethodDescribe = 0x0001,
|
|
||||||
SrsRtspMethodAnnounce = 0x0002,
|
|
||||||
SrsRtspMethodGetParameter = 0x0004,
|
|
||||||
SrsRtspMethodOptions = 0x0008,
|
|
||||||
SrsRtspMethodPause = 0x0010,
|
|
||||||
SrsRtspMethodPlay = 0x0020,
|
|
||||||
SrsRtspMethodRecord = 0x0040,
|
|
||||||
SrsRtspMethodRedirect = 0x0080,
|
|
||||||
SrsRtspMethodSetup = 0x0100,
|
|
||||||
SrsRtspMethodSetParameter = 0x0200,
|
|
||||||
SrsRtspMethodTeardown = 0x0400,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -349,27 +427,38 @@ public:
|
||||||
public:
|
public:
|
||||||
SrsRtspOptionsResponse(int cseq);
|
SrsRtspOptionsResponse(int cseq);
|
||||||
virtual ~SrsRtspOptionsResponse();
|
virtual ~SrsRtspOptionsResponse();
|
||||||
public:
|
protected:
|
||||||
virtual std::stringstream& encode(std::stringstream& ss);
|
virtual int encode_header(std::stringstream& ss);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the state of rtsp token.
|
* 10.4 SETUP
|
||||||
|
* The SETUP request for a URI specifies the transport mechanism to be
|
||||||
|
* used for the streamed media. A client can issue a SETUP request for a
|
||||||
|
* stream that is already playing to change transport parameters, which
|
||||||
|
* a server MAY allow. If it does not allow this, it MUST respond with
|
||||||
|
* error "455 Method Not Valid In This State". For the benefit of any
|
||||||
|
* intervening firewalls, a client must indicate the transport
|
||||||
|
* parameters even if it has no influence over these parameters, for
|
||||||
|
* example, where the server advertises a fixed multicast address.
|
||||||
*/
|
*/
|
||||||
enum SrsRtspTokenState
|
class SrsRtspSetupResponse : public SrsRtspResponse
|
||||||
{
|
{
|
||||||
/**
|
public:
|
||||||
* parse token failed, default state.
|
// the client specified port.
|
||||||
*/
|
int client_port_min;
|
||||||
SrsRtspTokenStateError = 100,
|
int client_port_max;
|
||||||
/**
|
// client will use the port in:
|
||||||
* when SP follow the token.
|
// [local_port_min, local_port_max)
|
||||||
*/
|
int local_port_min;
|
||||||
SrsRtspTokenStateNormal = 101,
|
int local_port_max;
|
||||||
/**
|
// session.
|
||||||
* when CRLF follow the token.
|
std::string session;
|
||||||
*/
|
public:
|
||||||
SrsRtspTokenStateEOF = 102,
|
SrsRtspSetupResponse(int cseq);
|
||||||
|
virtual ~SrsRtspSetupResponse();
|
||||||
|
protected:
|
||||||
|
virtual int encode_header(std::stringstream& ss);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue