1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-02-15 04:42:04 +00:00

support forward stream to origin/edge

This commit is contained in:
winlin 2013-11-29 22:20:51 +08:00
parent f656087d74
commit db405b7e0e
11 changed files with 3531 additions and 3392 deletions

View file

@ -47,14 +47,16 @@ m3u8 url: http://127.0.0.1:80/live/livestream.m3u8
8. support cache last gop for flash player to fast startup.<br/> 8. support cache last gop for flash player to fast startup.<br/>
9. support listen at multiple ports.<br/> 9. support listen at multiple ports.<br/>
10. support long time(>4.6hours) publish/play.<br/> 10. support long time(>4.6hours) publish/play.<br/>
11. [dev] support forward publish stream to build active-standby cluster.<br/> 11. high performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB<br/>
12. [plan] support live stream transcoding by ffmpeg.<br/> 12. support forward publish stream to build active-standby cluster.<br/>
13. [plan] support full http callback api.<br/> 13. support broadcast by forward the stream to other servers(origin/edge).<br/>
14. [plan] support network based cli and json result.<br/> 14. [plan] support live stream transcoding by ffmpeg.<br/>
15. [plan] support bandwidth test api and flash client.<br/> 15. [plan] support full http callback api.<br/>
16. no edge server, origin server only.<br/> 16. [plan] support network based cli and json result.<br/>
17. no vod streaming, live streaming only.<br/> 17. [plan] support bandwidth test api and flash client.<br/>
18. no multiple processes, single process only.<br/> 18. no edge server, origin server only.<br/>
19. no vod streaming, live streaming only.<br/>
20. no multiple processes, single process only.<br/>
### Performance ### Performance
1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB. 1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB.

View file

@ -15,7 +15,7 @@ vhost __defaultVhost__ {
hls_path ./objs/nginx/html; hls_path ./objs/nginx/html;
hls_fragment 5; hls_fragment 5;
hls_window 30; hls_window 30;
forward 192.168.1.50; forward 127.0.0.1:1936;
} }
# the vhost which forward publish streams. # the vhost which forward publish streams.
vhost forward.vhost.com { vhost forward.vhost.com {
@ -103,6 +103,9 @@ pithy_print {
publish 2000; publish 2000;
# shared print interval for all play clients, in milliseconds. # shared print interval for all play clients, in milliseconds.
# if not specified, set to 1300. # if not specified, set to 1300.
play 3000; play 3000;
# shared print interval for all forwarders, in milliseconds.
# if not specified, set to 2000.
forwarder 3000;
} }

1048
trunk/src/core/srs_core_client.cpp Normal file → Executable file

File diff suppressed because it is too large Load diff

1654
trunk/src/core/srs_core_config.cpp Normal file → Executable file

File diff suppressed because it is too large Load diff

287
trunk/src/core/srs_core_config.hpp Normal file → Executable file
View file

@ -1,144 +1,145 @@
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2013 winlin Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 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, the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 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 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 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. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#ifndef SRS_CORE_CONIFG_HPP #ifndef SRS_CORE_CONIFG_HPP
#define SRS_CORE_CONIFG_HPP #define SRS_CORE_CONIFG_HPP
/* /*
#include <srs_core_config.hpp> #include <srs_core_config.hpp>
*/ */
#include <srs_core.hpp> #include <srs_core.hpp>
#include <vector> #include <vector>
#include <string> #include <string>
#include <srs_core_reload.hpp> #include <srs_core_reload.hpp>
// default vhost for rtmp // default vhost for rtmp
#define RTMP_VHOST_DEFAULT "__defaultVhost__" #define RTMP_VHOST_DEFAULT "__defaultVhost__"
#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" #define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"
#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 #define SRS_CONF_DEFAULT_HLS_FRAGMENT 10
#define SRS_CONF_DEFAULT_HLS_WINDOW 60 #define SRS_CONF_DEFAULT_HLS_WINDOW 60
// in ms, for HLS aac sync time. // in ms, for HLS aac sync time.
#define SRS_CONF_DEFAULT_AAC_SYNC 100 #define SRS_CONF_DEFAULT_AAC_SYNC 100
// in ms, for HLS aac flush the audio // in ms, for HLS aac flush the audio
#define SRS_CONF_DEFAULT_AAC_DELAY 300 #define SRS_CONF_DEFAULT_AAC_DELAY 300
class SrsFileBuffer class SrsFileBuffer
{ {
public: public:
int fd; int fd;
int line; int line;
// start of buffer. // start of buffer.
char* start; char* start;
// end of buffer. // end of buffer.
char* end; char* end;
// current consumed position. // current consumed position.
char* pos; char* pos;
// last available position. // last available position.
char* last; char* last;
SrsFileBuffer(); SrsFileBuffer();
virtual ~SrsFileBuffer(); virtual ~SrsFileBuffer();
virtual int open(const char* filename); virtual int open(const char* filename);
}; };
class SrsConfDirective class SrsConfDirective
{ {
public: public:
int conf_line; int conf_line;
std::string name; std::string name;
std::vector<std::string> args; std::vector<std::string> args;
std::vector<SrsConfDirective*> directives; std::vector<SrsConfDirective*> directives;
public: public:
SrsConfDirective(); SrsConfDirective();
virtual ~SrsConfDirective(); virtual ~SrsConfDirective();
std::string arg0(); std::string arg0();
std::string arg1(); std::string arg1();
std::string arg2(); std::string arg2();
SrsConfDirective* at(int index); SrsConfDirective* at(int index);
SrsConfDirective* get(std::string _name); SrsConfDirective* get(std::string _name);
public: public:
virtual int parse(const char* filename); virtual int parse(const char* filename);
public: public:
enum SrsDirectiveType{parse_file, parse_block}; enum SrsDirectiveType{parse_file, parse_block};
virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type); virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type);
virtual int read_token(SrsFileBuffer* buffer, std::vector<std::string>& args); virtual int read_token(SrsFileBuffer* buffer, std::vector<std::string>& args);
virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart); virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart);
}; };
/** /**
* the config parser. * the config parser.
* for the config supports reload, so never keep the reference cross st-thread, * for the config supports reload, so never keep the reference cross st-thread,
* that is, never save the SrsConfDirective* get by any api of config, * that is, never save the SrsConfDirective* get by any api of config,
* for it maybe free in the reload st-thread cycle. * for it maybe free in the reload st-thread cycle.
* you can keep it before st-thread switch, or simply never keep it. * you can keep it before st-thread switch, or simply never keep it.
*/ */
class SrsConfig class SrsConfig
{ {
private: private:
bool show_help; bool show_help;
bool show_version; bool show_version;
std::string config_file; std::string config_file;
SrsConfDirective* root; SrsConfDirective* root;
std::vector<SrsReloadHandler*> subscribes; std::vector<SrsReloadHandler*> subscribes;
public: public:
SrsConfig(); SrsConfig();
virtual ~SrsConfig(); virtual ~SrsConfig();
public: public:
virtual int reload(); virtual int reload();
virtual void subscribe(SrsReloadHandler* handler); virtual void subscribe(SrsReloadHandler* handler);
virtual void unsubscribe(SrsReloadHandler* handler); virtual void unsubscribe(SrsReloadHandler* handler);
public: public:
virtual int parse_options(int argc, char** argv); virtual int parse_options(int argc, char** argv);
virtual SrsConfDirective* get_vhost(std::string vhost); virtual SrsConfDirective* get_vhost(std::string vhost);
virtual SrsConfDirective* get_vhost_enabled(std::string vhost); virtual SrsConfDirective* get_vhost_enabled(std::string vhost);
virtual SrsConfDirective* get_gop_cache(std::string vhost); virtual SrsConfDirective* get_gop_cache(std::string vhost);
virtual SrsConfDirective* get_forward(std::string vhost); virtual SrsConfDirective* get_forward(std::string vhost);
virtual SrsConfDirective* get_hls(std::string vhost); virtual SrsConfDirective* get_hls(std::string vhost);
virtual SrsConfDirective* get_hls_path(std::string vhost); virtual SrsConfDirective* get_hls_path(std::string vhost);
virtual SrsConfDirective* get_hls_fragment(std::string vhost); virtual SrsConfDirective* get_hls_fragment(std::string vhost);
virtual SrsConfDirective* get_hls_window(std::string vhost); virtual SrsConfDirective* get_hls_window(std::string vhost);
virtual SrsConfDirective* get_refer(std::string vhost); virtual SrsConfDirective* get_refer(std::string vhost);
virtual SrsConfDirective* get_refer_play(std::string vhost); virtual SrsConfDirective* get_refer_play(std::string vhost);
virtual SrsConfDirective* get_refer_publish(std::string vhost); virtual SrsConfDirective* get_refer_publish(std::string vhost);
virtual SrsConfDirective* get_listen(); virtual SrsConfDirective* get_listen();
virtual SrsConfDirective* get_chunk_size(); virtual SrsConfDirective* get_chunk_size();
virtual SrsConfDirective* get_pithy_print_publish(); virtual SrsConfDirective* get_pithy_print_publish();
virtual SrsConfDirective* get_pithy_print_play(); virtual SrsConfDirective* get_pithy_print_forwarder();
private: virtual SrsConfDirective* get_pithy_print_play();
virtual int parse_file(const char* filename); private:
virtual int parse_argv(int& i, char** argv); virtual int parse_file(const char* filename);
virtual void print_help(char** argv); virtual int parse_argv(int& i, char** argv);
}; virtual void print_help(char** argv);
};
/**
* deep compare directive. /**
*/ * deep compare directive.
bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b); */
bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b);
// global config
extern SrsConfig* config; // global config
extern SrsConfig* config;
#endif #endif

627
trunk/src/core/srs_core_forward.cpp Normal file → Executable file
View file

@ -1,277 +1,350 @@
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2013 winlin Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 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, the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 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 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 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. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#include <srs_core_forward.hpp> #include <srs_core_forward.hpp>
#include <stdlib.h> #include <stdlib.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
#include <srs_core_error.hpp> #include <srs_core_error.hpp>
#include <srs_core_rtmp.hpp> #include <srs_core_rtmp.hpp>
#include <srs_core_log.hpp> #include <srs_core_log.hpp>
#include <srs_core_protocol.hpp>
#define SRS_FORWARDER_SLEEP_MS 2000 #include <srs_core_pithy_print.hpp>
#define SRS_SEND_TIMEOUT_US 3000000L
#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US #define SRS_PULSE_TIMEOUT_MS 100
#define SRS_FORWARDER_SLEEP_MS 2000
SrsForwarder::SrsForwarder() #define SRS_SEND_TIMEOUT_US 3000000L
{ #define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US
client = NULL;
tid = NULL; SrsForwarder::SrsForwarder()
stfd = NULL; {
loop = false; client = NULL;
stream_id = 0; tid = NULL;
} stfd = NULL;
loop = false;
SrsForwarder::~SrsForwarder() stream_id = 0;
{ }
on_unpublish();
} SrsForwarder::~SrsForwarder()
{
int SrsForwarder::on_publish(std::string vhost, std::string _app, std::string stream, std::string forward_server) on_unpublish();
{
int ret = ERROR_SUCCESS; std::vector<SrsSharedPtrMessage*>::iterator it;
for (it = msgs.begin(); it != msgs.end(); ++it) {
app = _app; SrsSharedPtrMessage* msg = *it;
srs_freep(msg);
tc_url = "rtmp://"; }
tc_url += vhost; msgs.clear();
tc_url += "/"; }
tc_url += app;
int SrsForwarder::on_publish(std::string vhost, std::string _app, std::string stream, std::string forward_server)
stream_name = stream; {
server = forward_server; int ret = ERROR_SUCCESS;
port = 1935;
app = _app;
size_t pos = forward_server.find(":");
if (pos != std::string::npos) { tc_url = "rtmp://";
port = ::atoi(forward_server.substr(pos + 1).c_str()); tc_url += vhost;
server = forward_server.substr(0, pos); tc_url += "/";
} tc_url += app;
if ((ret = open_socket()) != ERROR_SUCCESS) { stream_name = stream;
return ret; server = forward_server;
} port = 1935;
srs_assert(!tid); size_t pos = forward_server.find(":");
if((tid = st_thread_create(forward_thread, this, 1, 0)) == NULL){ if (pos != std::string::npos) {
ret = ERROR_ST_CREATE_FORWARD_THREAD; port = ::atoi(forward_server.substr(pos + 1).c_str());
srs_error("st_thread_create failed. ret=%d", ret); server = forward_server.substr(0, pos);
return ret; }
}
if ((ret = open_socket()) != ERROR_SUCCESS) {
return ret; return ret;
} }
void SrsForwarder::on_unpublish() srs_assert(!tid);
{ if((tid = st_thread_create(forward_thread, this, 1, 0)) == NULL){
if (tid) { ret = ERROR_ST_CREATE_FORWARD_THREAD;
loop = false; srs_error("st_thread_create failed. ret=%d", ret);
st_thread_interrupt(tid); return ret;
st_thread_join(tid, NULL); }
tid = NULL;
} return ret;
}
if (stfd) {
int fd = st_netfd_fileno(stfd); void SrsForwarder::on_unpublish()
st_netfd_close(stfd); {
stfd = NULL; if (tid) {
loop = false;
// st does not close it sometimes, st_thread_interrupt(tid);
// close it manually. st_thread_join(tid, NULL);
close(fd); tid = NULL;
} }
srs_freep(client); if (stfd) {
} int fd = st_netfd_fileno(stfd);
st_netfd_close(stfd);
int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata) stfd = NULL;
{
int ret = ERROR_SUCCESS; // st does not close it sometimes,
return ret; // close it manually.
} close(fd);
}
int SrsForwarder::on_audio(SrsSharedPtrMessage* msg)
{ srs_freep(client);
int ret = ERROR_SUCCESS; }
return ret;
} int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata)
{
int SrsForwarder::on_video(SrsSharedPtrMessage* msg) int ret = ERROR_SUCCESS;
{
int ret = ERROR_SUCCESS; msgs.push_back(metadata);
return ret;
} return ret;
}
int SrsForwarder::open_socket()
{ int SrsForwarder::on_audio(SrsSharedPtrMessage* msg)
int ret = ERROR_SUCCESS; {
int ret = ERROR_SUCCESS;
srs_trace("forward stream=%s, tcUrl=%s to server=%s, port=%d",
stream_name.c_str(), tc_url.c_str(), server.c_str(), port); msgs.push_back(msg);
int sock = socket(AF_INET, SOCK_STREAM, 0); return ret;
if(sock == -1){ }
ret = ERROR_SOCKET_CREATE;
srs_error("create socket error. ret=%d", ret); int SrsForwarder::on_video(SrsSharedPtrMessage* msg)
return ret; {
} int ret = ERROR_SUCCESS;
stfd = st_netfd_open_socket(sock); msgs.push_back(msg);
if(stfd == NULL){
ret = ERROR_ST_OPEN_SOCKET; return ret;
srs_error("st_netfd_open_socket failed. ret=%d", ret); }
return ret;
} int SrsForwarder::open_socket()
{
srs_freep(client); int ret = ERROR_SUCCESS;
client = new SrsRtmpClient(stfd);
srs_trace("forward stream=%s, tcUrl=%s to server=%s, port=%d",
return ret; stream_name.c_str(), tc_url.c_str(), server.c_str(), port);
}
int sock = socket(AF_INET, SOCK_STREAM, 0);
int SrsForwarder::connect_server() if(sock == -1){
{ ret = ERROR_SOCKET_CREATE;
int ret = ERROR_SUCCESS; srs_error("create socket error. ret=%d", ret);
return ret;
std::string ip = parse_server(server); }
if (ip.empty()) {
ret = ERROR_SYSTEM_IP_INVALID; stfd = st_netfd_open_socket(sock);
srs_error("dns resolve server error, ip empty. ret=%d", ret); if(stfd == NULL){
return ret; ret = ERROR_ST_OPEN_SOCKET;
} srs_error("st_netfd_open_socket failed. ret=%d", ret);
return ret;
sockaddr_in addr; }
addr.sin_family = AF_INET;
addr.sin_port = htons(port); srs_freep(client);
addr.sin_addr.s_addr = inet_addr(ip.c_str()); client = new SrsRtmpClient(stfd);
if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1){ return ret;
ret = ERROR_ST_CONNECT; }
srs_error("connect to server error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret);
return ret; int SrsForwarder::connect_server()
} {
srs_trace("connect to server success. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port); int ret = ERROR_SUCCESS;
return ret; std::string ip = parse_server(server);
} if (ip.empty()) {
ret = ERROR_SYSTEM_IP_INVALID;
std::string SrsForwarder::parse_server(std::string host) srs_error("dns resolve server error, ip empty. ret=%d", ret);
{ return ret;
if (inet_addr(host.c_str()) != INADDR_NONE) { }
return host;
} sockaddr_in addr;
addr.sin_family = AF_INET;
hostent* answer = gethostbyname(host.c_str()); addr.sin_port = htons(port);
if (answer == NULL) { addr.sin_addr.s_addr = inet_addr(ip.c_str());
srs_error("dns resolve host %s error.", host.c_str());
return ""; if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1){
} ret = ERROR_ST_CONNECT;
srs_error("connect to server error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret);
char ipv4[16]; return ret;
memset(ipv4, 0, sizeof(ipv4)); }
for (int i = 0; i < answer->h_length; i++) { srs_trace("connect to server success. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port);
inet_ntop(AF_INET, answer->h_addr_list[i], ipv4, sizeof(ipv4));
srs_info("dns resolve host %s to %s.", host.c_str(), ipv4); return ret;
break; }
}
std::string SrsForwarder::parse_server(std::string host)
return ipv4; {
} if (inet_addr(host.c_str()) != INADDR_NONE) {
return host;
int SrsForwarder::forward_cycle_imp() }
{
int ret = ERROR_SUCCESS; hostent* answer = gethostbyname(host.c_str());
if (answer == NULL) {
client->set_recv_timeout(SRS_RECV_TIMEOUT_US); srs_error("dns resolve host %s error.", host.c_str());
client->set_send_timeout(SRS_SEND_TIMEOUT_US); return "";
}
if ((ret = connect_server()) != ERROR_SUCCESS) {
return ret; char ipv4[16];
} memset(ipv4, 0, sizeof(ipv4));
srs_assert(client); for (int i = 0; i < answer->h_length; i++) {
inet_ntop(AF_INET, answer->h_addr_list[i], ipv4, sizeof(ipv4));
if ((ret = client->handshake()) != ERROR_SUCCESS) { srs_info("dns resolve host %s to %s.", host.c_str(), ipv4);
srs_error("handshake with server failed. ret=%d", ret); break;
return ret; }
}
if ((ret = client->connect_app(app, tc_url)) != ERROR_SUCCESS) { return ipv4;
srs_error("connect with server failed, tcUrl=%s. ret=%d", tc_url.c_str(), ret); }
return ret;
} int SrsForwarder::forward_cycle_imp()
if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) { {
srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret); int ret = ERROR_SUCCESS;
return ret;
} client->set_recv_timeout(SRS_RECV_TIMEOUT_US);
if ((ret = client->publish(stream_name, stream_id)) != ERROR_SUCCESS) { client->set_send_timeout(SRS_SEND_TIMEOUT_US);
srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d",
stream_name.c_str(), stream_id, ret); if ((ret = connect_server()) != ERROR_SUCCESS) {
return ret; return ret;
} }
srs_assert(client);
return ret;
} if ((ret = client->handshake()) != ERROR_SUCCESS) {
srs_error("handshake with server failed. ret=%d", ret);
void SrsForwarder::forward_cycle() return ret;
{ }
int ret = ERROR_SUCCESS; if ((ret = client->connect_app(app, tc_url)) != ERROR_SUCCESS) {
srs_error("connect with server failed, tcUrl=%s. ret=%d", tc_url.c_str(), ret);
log_context->generate_id(); return ret;
srs_trace("forward cycle start"); }
if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) {
while (loop) { srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret);
if ((ret = forward_cycle_imp()) != ERROR_SUCCESS) { return ret;
srs_warn("forward cycle failed, ignored and retry, ret=%d", ret); }
} else { if ((ret = client->publish(stream_name, stream_id)) != ERROR_SUCCESS) {
srs_info("forward cycle success, retry"); srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d",
} stream_name.c_str(), stream_id, ret);
return ret;
if (!loop) { }
break;
} if ((ret = forward()) != ERROR_SUCCESS) {
return ret;
st_usleep(SRS_FORWARDER_SLEEP_MS * 1000); }
if ((ret = open_socket()) != ERROR_SUCCESS) { return ret;
srs_warn("forward cycle reopen failed, ignored and retry, ret=%d", ret); }
} else {
srs_info("forward cycle reopen success"); int SrsForwarder::forward()
} {
} int ret = ERROR_SUCCESS;
srs_trace("forward cycle finished");
} client->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000);
void* SrsForwarder::forward_thread(void* arg) SrsPithyPrint pithy_print(SRS_STAGE_FORWARDER);
{
SrsForwarder* obj = (SrsForwarder*)arg; while (loop) {
srs_assert(obj != NULL); pithy_print.elapse(SRS_PULSE_TIMEOUT_MS);
obj->loop = true; // switch to other st-threads.
obj->forward_cycle(); st_usleep(0);
return NULL; // read from client.
} if (true) {
SrsCommonMessage* msg = NULL;
ret = client->recv_message(&msg);
srs_verbose("play loop recv message. ret=%d", ret);
if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) {
srs_error("recv server control message failed. ret=%d", ret);
return ret;
}
}
int count = (int)msgs.size();
// reportable
if (pithy_print.can_print()) {
srs_trace("-> clock=%u, time=%"PRId64", msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d",
(int)(srs_get_system_time_ms()/1000), pithy_print.get_age(), count, client->get_send_bytes(), client->get_recv_bytes(), client->get_send_kbps(), client->get_recv_kbps());
}
// all msgs to forward.
for (int i = 0; i < count; i++) {
SrsSharedPtrMessage* msg = msgs[i];
msgs[i] = NULL;
if ((ret = client->send_message(msg)) != ERROR_SUCCESS) {
srs_error("forwarder send message to server failed. ret=%d", ret);
return ret;
}
}
msgs.clear();
}
return ret;
}
void SrsForwarder::forward_cycle()
{
int ret = ERROR_SUCCESS;
log_context->generate_id();
srs_trace("forward cycle start");
while (loop) {
if ((ret = forward_cycle_imp()) != ERROR_SUCCESS) {
srs_warn("forward cycle failed, ignored and retry, ret=%d", ret);
} else {
srs_info("forward cycle success, retry");
}
if (!loop) {
break;
}
st_usleep(SRS_FORWARDER_SLEEP_MS * 1000);
if ((ret = open_socket()) != ERROR_SUCCESS) {
srs_warn("forward cycle reopen failed, ignored and retry, ret=%d", ret);
} else {
srs_info("forward cycle reopen success");
}
}
srs_trace("forward cycle finished");
}
void* SrsForwarder::forward_thread(void* arg)
{
SrsForwarder* obj = (SrsForwarder*)arg;
srs_assert(obj != NULL);
obj->loop = true;
obj->forward_cycle();
return NULL;
}

157
trunk/src/core/srs_core_forward.hpp Normal file → Executable file
View file

@ -1,77 +1,80 @@
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2013 winlin Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 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, the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 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 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 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. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#ifndef SRS_CORE_FORWARD_HPP #ifndef SRS_CORE_FORWARD_HPP
#define SRS_CORE_FORWARD_HPP #define SRS_CORE_FORWARD_HPP
/* /*
#include <srs_core_forward.hpp> #include <srs_core_forward.hpp>
*/ */
#include <srs_core.hpp> #include <srs_core.hpp>
#include <string> #include <string>
#include <vector>
#include <st.h>
#include <st.h>
class SrsSharedPtrMessage;
class SrsOnMetaDataPacket; class SrsSharedPtrMessage;
class SrsRtmpClient; class SrsOnMetaDataPacket;
class SrsRtmpClient;
/**
* forward the stream to other servers. /**
*/ * forward the stream to other servers.
class SrsForwarder */
{ class SrsForwarder
private: {
std::string app; private:
std::string tc_url; std::string app;
std::string stream_name; std::string tc_url;
int stream_id; std::string stream_name;
std::string server; int stream_id;
int port; std::string server;
private: int port;
st_netfd_t stfd; private:
st_thread_t tid; st_netfd_t stfd;
bool loop; st_thread_t tid;
private: bool loop;
SrsRtmpClient* client; private:
public: SrsRtmpClient* client;
SrsForwarder(); std::vector<SrsSharedPtrMessage*> msgs;
virtual ~SrsForwarder(); public:
public: SrsForwarder();
virtual int on_publish(std::string vhost, std::string app, std::string stream, std::string forward_server); virtual ~SrsForwarder();
virtual void on_unpublish(); public:
virtual int on_meta_data(SrsSharedPtrMessage* metadata); virtual int on_publish(std::string vhost, std::string app, std::string stream, std::string forward_server);
virtual int on_audio(SrsSharedPtrMessage* msg); virtual void on_unpublish();
virtual int on_video(SrsSharedPtrMessage* msg); virtual int on_meta_data(SrsSharedPtrMessage* metadata);
private: virtual int on_audio(SrsSharedPtrMessage* msg);
virtual int open_socket(); virtual int on_video(SrsSharedPtrMessage* msg);
virtual int connect_server(); private:
std::string parse_server(std::string host); virtual int open_socket();
private: virtual int connect_server();
virtual int forward_cycle_imp(); std::string parse_server(std::string host);
virtual void forward_cycle(); private:
static void* forward_thread(void* arg); virtual int forward_cycle_imp();
}; virtual int forward();
virtual void forward_cycle();
#endif static void* forward_thread(void* arg);
};
#endif

337
trunk/src/core/srs_core_pithy_print.cpp Normal file → Executable file
View file

@ -1,164 +1,173 @@
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2013 winlin Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 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, the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 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 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 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. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#include <srs_core_pithy_print.hpp> #include <srs_core_pithy_print.hpp>
#include <stdlib.h> #include <stdlib.h>
#include <map> #include <map>
#include <srs_core_log.hpp> #include <srs_core_log.hpp>
#include <srs_core_config.hpp> #include <srs_core_config.hpp>
#include <srs_core_reload.hpp> #include <srs_core_reload.hpp>
#include <srs_core_error.hpp> #include <srs_core_error.hpp>
#define SRS_STAGE_DEFAULT_INTERVAL_MS 1200 #define SRS_STAGE_DEFAULT_INTERVAL_MS 1200
#define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300 #define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300
#define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100 #define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100
#define SRS_STAGE_FORWARDER_INTERVAL_MS 2000
struct SrsStageInfo : public SrsReloadHandler
{ struct SrsStageInfo : public SrsReloadHandler
int stage_id; {
int pithy_print_time_ms; int stage_id;
int nb_clients; int pithy_print_time_ms;
int nb_clients;
SrsStageInfo(int _stage_id)
{ SrsStageInfo(int _stage_id)
stage_id = _stage_id; {
nb_clients = 0; stage_id = _stage_id;
nb_clients = 0;
update_print_time();
update_print_time();
config->subscribe(this);
} config->subscribe(this);
virtual ~SrsStageInfo() }
{ virtual ~SrsStageInfo()
config->unsubscribe(this); {
} config->unsubscribe(this);
void update_print_time() }
{ void update_print_time()
switch (stage_id) { {
case SRS_STAGE_PLAY_USER: { switch (stage_id) {
pithy_print_time_ms = SRS_STAGE_PLAY_USER_INTERVAL_MS; case SRS_STAGE_PLAY_USER: {
SrsConfDirective* conf = config->get_pithy_print_play(); pithy_print_time_ms = SRS_STAGE_PLAY_USER_INTERVAL_MS;
if (conf && !conf->arg0().empty()) { SrsConfDirective* conf = config->get_pithy_print_play();
pithy_print_time_ms = ::atoi(conf->arg0().c_str()); if (conf && !conf->arg0().empty()) {
} pithy_print_time_ms = ::atoi(conf->arg0().c_str());
break; }
} break;
case SRS_STAGE_PUBLISH_USER: { }
pithy_print_time_ms = SRS_STAGE_PUBLISH_USER_INTERVAL_MS; case SRS_STAGE_PUBLISH_USER: {
SrsConfDirective* conf = config->get_pithy_print_publish(); pithy_print_time_ms = SRS_STAGE_PUBLISH_USER_INTERVAL_MS;
if (conf && !conf->arg0().empty()) { SrsConfDirective* conf = config->get_pithy_print_publish();
pithy_print_time_ms = ::atoi(conf->arg0().c_str()); if (conf && !conf->arg0().empty()) {
} pithy_print_time_ms = ::atoi(conf->arg0().c_str());
break; }
} break;
default: { }
pithy_print_time_ms = SRS_STAGE_DEFAULT_INTERVAL_MS; case SRS_STAGE_FORWARDER: {
break; pithy_print_time_ms = SRS_STAGE_FORWARDER_INTERVAL_MS;
} SrsConfDirective* conf = config->get_pithy_print_forwarder();
} if (conf && !conf->arg0().empty()) {
} pithy_print_time_ms = ::atoi(conf->arg0().c_str());
public: }
virtual int on_reload_pithy_print() break;
{ }
update_print_time(); default: {
return ERROR_SUCCESS; pithy_print_time_ms = SRS_STAGE_DEFAULT_INTERVAL_MS;
} break;
}; }
static std::map<int, SrsStageInfo*> _srs_stages; }
}
SrsPithyPrint::SrsPithyPrint(int _stage_id) public:
{ virtual int on_reload_pithy_print()
stage_id = _stage_id; {
client_id = enter_stage(); update_print_time();
printed_age = age = 0; return ERROR_SUCCESS;
} }
};
SrsPithyPrint::~SrsPithyPrint() static std::map<int, SrsStageInfo*> _srs_stages;
{
leave_stage(); SrsPithyPrint::SrsPithyPrint(int _stage_id)
} {
stage_id = _stage_id;
int SrsPithyPrint::enter_stage() client_id = enter_stage();
{ printed_age = age = 0;
SrsStageInfo* stage = NULL; }
std::map<int, SrsStageInfo*>::iterator it = _srs_stages.find(stage_id); SrsPithyPrint::~SrsPithyPrint()
if (it == _srs_stages.end()) { {
stage = _srs_stages[stage_id] = new SrsStageInfo(stage_id); leave_stage();
} else { }
stage = it->second;
} int SrsPithyPrint::enter_stage()
{
srs_assert(stage != NULL); SrsStageInfo* stage = NULL;
client_id = stage->nb_clients++;
std::map<int, SrsStageInfo*>::iterator it = _srs_stages.find(stage_id);
srs_verbose("enter stage, stage_id=%d, client_id=%d, nb_clients=%d, time_ms=%d", if (it == _srs_stages.end()) {
stage->stage_id, client_id, stage->nb_clients, stage->pithy_print_time_ms); stage = _srs_stages[stage_id] = new SrsStageInfo(stage_id);
} else {
return client_id; stage = it->second;
} }
void SrsPithyPrint::leave_stage() srs_assert(stage != NULL);
{ client_id = stage->nb_clients++;
SrsStageInfo* stage = _srs_stages[stage_id];
srs_assert(stage != NULL); srs_verbose("enter stage, stage_id=%d, client_id=%d, nb_clients=%d, time_ms=%d",
stage->stage_id, client_id, stage->nb_clients, stage->pithy_print_time_ms);
stage->nb_clients--;
return client_id;
srs_verbose("leave stage, stage_id=%d, client_id=%d, nb_clients=%d, time_ms=%d", }
stage->stage_id, client_id, stage->nb_clients, stage->pithy_print_time_ms);
} void SrsPithyPrint::leave_stage()
{
void SrsPithyPrint::elapse(int64_t time_ms) SrsStageInfo* stage = _srs_stages[stage_id];
{ srs_assert(stage != NULL);
age += time_ms;
} stage->nb_clients--;
bool SrsPithyPrint::can_print() srs_verbose("leave stage, stage_id=%d, client_id=%d, nb_clients=%d, time_ms=%d",
{ stage->stage_id, client_id, stage->nb_clients, stage->pithy_print_time_ms);
SrsStageInfo* stage = _srs_stages[stage_id]; }
srs_assert(stage != NULL);
void SrsPithyPrint::elapse(int64_t time_ms)
int64_t alive_age = age - printed_age; {
int64_t can_print_age = stage->nb_clients * stage->pithy_print_time_ms; age += time_ms;
}
bool can_print = alive_age >= can_print_age;
if (can_print) { bool SrsPithyPrint::can_print()
printed_age = age; {
} SrsStageInfo* stage = _srs_stages[stage_id];
srs_assert(stage != NULL);
return can_print;
} int64_t alive_age = age - printed_age;
int64_t can_print_age = stage->nb_clients * stage->pithy_print_time_ms;
int64_t SrsPithyPrint::get_age()
{ bool can_print = alive_age >= can_print_age;
return age; if (can_print) {
} printed_age = age;
}
void SrsPithyPrint::set_age(int64_t _age)
{ return can_print;
age = _age; }
}
int64_t SrsPithyPrint::get_age()
{
return age;
}
void SrsPithyPrint::set_age(int64_t _age)
{
age = _age;
}

164
trunk/src/core/srs_core_pithy_print.hpp Normal file → Executable file
View file

@ -1,82 +1,84 @@
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2013 winlin Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 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, the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 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 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 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. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#ifndef SRS_CORE_PITHY_PRINT_HPP #ifndef SRS_CORE_PITHY_PRINT_HPP
#define SRS_CORE_PITHY_PRINT_HPP #define SRS_CORE_PITHY_PRINT_HPP
/* /*
#include <srs_core_pithy_print.hpp> #include <srs_core_pithy_print.hpp>
*/ */
#include <srs_core.hpp> #include <srs_core.hpp>
// the pithy stage for all play clients. // the pithy stage for all play clients.
#define SRS_STAGE_PLAY_USER 1 #define SRS_STAGE_PLAY_USER 1
// the pithy stage for all publish clients. // the pithy stage for all publish clients.
#define SRS_STAGE_PUBLISH_USER 2 #define SRS_STAGE_PUBLISH_USER 2
// the pithy stage for all forward clients.
/** #define SRS_STAGE_FORWARDER 3
* the stage is used for a collection of object to do print,
* the print time in a stage is constant and not changed. /**
* for example, stage #1 for all play clients, print time is 3s, * the stage is used for a collection of object to do print,
* if there is 10clients, then all clients should print in 10*3s. * the print time in a stage is constant and not changed.
*/ * for example, stage #1 for all play clients, print time is 3s,
class SrsPithyPrint * if there is 10clients, then all clients should print in 10*3s.
{ */
private: class SrsPithyPrint
int client_id; {
int stage_id; private:
int64_t age; int client_id;
int64_t printed_age; int stage_id;
public: int64_t age;
/** int64_t printed_age;
* @param _stage_id defined in SRS_STAGE_xxx, eg. SRS_STAGE_PLAY_USER. public:
*/ /**
SrsPithyPrint(int _stage_id); * @param _stage_id defined in SRS_STAGE_xxx, eg. SRS_STAGE_PLAY_USER.
virtual ~SrsPithyPrint(); */
private: SrsPithyPrint(int _stage_id);
/** virtual ~SrsPithyPrint();
* enter the specified stage, return the client id. private:
*/ /**
virtual int enter_stage(); * enter the specified stage, return the client id.
/** */
* leave the specified stage, release the client id. virtual int enter_stage();
*/ /**
virtual void leave_stage(); * leave the specified stage, release the client id.
public: */
/** virtual void leave_stage();
* specified client elapse some time. public:
*/ /**
virtual void elapse(int64_t time_ms); * specified client elapse some time.
/** */
* whether current client can print. virtual void elapse(int64_t time_ms);
*/ /**
virtual bool can_print(); * whether current client can print.
/** */
* get the elapsed time in ms. virtual bool can_print();
*/ /**
virtual int64_t get_age(); * get the elapsed time in ms.
virtual void set_age(int64_t _age); */
}; virtual int64_t get_age();
virtual void set_age(int64_t _age);
};
#endif #endif

2202
trunk/src/core/srs_core_rtmp.cpp Normal file → Executable file

File diff suppressed because it is too large Load diff

422
trunk/src/core/srs_core_rtmp.hpp Normal file → Executable file
View file

@ -1,209 +1,215 @@
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2013 winlin Copyright (c) 2013 winlin
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 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, the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 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 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 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. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#ifndef SRS_CORE_RTMP_HPP #ifndef SRS_CORE_RTMP_HPP
#define SRS_CORE_RTMP_HPP #define SRS_CORE_RTMP_HPP
/* /*
#include <srs_core_rtmp.hpp> #include <srs_core_rtmp.hpp>
*/ */
#include <srs_core.hpp> #include <srs_core.hpp>
#include <string> #include <string>
#include <st.h> #include <st.h>
class SrsProtocol; class SrsProtocol;
class ISrsMessage; class ISrsMessage;
class SrsCommonMessage; class SrsCommonMessage;
class SrsCreateStreamPacket; class SrsCreateStreamPacket;
class SrsFMLEStartPacket; class SrsFMLEStartPacket;
class SrsPublishPacket; class SrsPublishPacket;
class SrsSharedPtrMessage; class SrsSharedPtrMessage;
class SrsOnMetaDataPacket; class SrsOnMetaDataPacket;
/** /**
* the original request from client. * the original request from client.
*/ */
struct SrsRequest struct SrsRequest
{ {
std::string tcUrl; std::string tcUrl;
std::string pageUrl; std::string pageUrl;
std::string swfUrl; std::string swfUrl;
double objectEncoding; double objectEncoding;
std::string schema; std::string schema;
std::string vhost; std::string vhost;
std::string port; std::string port;
std::string app; std::string app;
std::string stream; std::string stream;
SrsRequest(); SrsRequest();
virtual ~SrsRequest(); virtual ~SrsRequest();
/** /**
* disconvery vhost/app from tcUrl. * disconvery vhost/app from tcUrl.
*/ */
virtual int discovery_app(); virtual int discovery_app();
virtual std::string get_stream_url(); virtual std::string get_stream_url();
virtual void strip(); virtual void strip();
private: private:
std::string& trim(std::string& str, std::string chs); std::string& trim(std::string& str, std::string chs);
}; };
/** /**
* the response to client. * the response to client.
*/ */
struct SrsResponse struct SrsResponse
{ {
int stream_id; int stream_id;
SrsResponse(); SrsResponse();
virtual ~SrsResponse(); virtual ~SrsResponse();
}; };
/** /**
* the rtmp client type. * the rtmp client type.
*/ */
enum SrsClientType enum SrsClientType
{ {
SrsClientUnknown, SrsClientUnknown,
SrsClientPlay, SrsClientPlay,
SrsClientFMLEPublish, SrsClientFMLEPublish,
SrsClientFlashPublish, SrsClientFlashPublish,
}; };
/** /**
* implements the client role protocol. * implements the client role protocol.
*/ */
class SrsRtmpClient class SrsRtmpClient
{ {
private: private:
SrsProtocol* protocol; SrsProtocol* protocol;
st_netfd_t stfd; st_netfd_t stfd;
public: public:
SrsRtmpClient(st_netfd_t _stfd); SrsRtmpClient(st_netfd_t _stfd);
virtual ~SrsRtmpClient(); virtual ~SrsRtmpClient();
public: public:
virtual void set_recv_timeout(int64_t timeout_us); virtual void set_recv_timeout(int64_t timeout_us);
virtual void set_send_timeout(int64_t timeout_us); virtual void set_send_timeout(int64_t timeout_us);
public: virtual int64_t get_recv_bytes();
virtual int handshake(); virtual int64_t get_send_bytes();
virtual int connect_app(std::string app, std::string tc_url); virtual int get_recv_kbps();
virtual int create_stream(int& stream_id); virtual int get_send_kbps();
virtual int play(std::string stream, int stream_id); virtual int recv_message(SrsCommonMessage** pmsg);
virtual int publish(std::string stream, int stream_id); virtual int send_message(ISrsMessage* msg);
}; public:
virtual int handshake();
/** virtual int connect_app(std::string app, std::string tc_url);
* the rtmp provices rtmp-command-protocol services, virtual int create_stream(int& stream_id);
* a high level protocol, media stream oriented services, virtual int play(std::string stream, int stream_id);
* such as connect to vhost/app, play stream, get audio/video data. virtual int publish(std::string stream, int stream_id);
*/ };
class SrsRtmp
{ /**
private: * the rtmp provices rtmp-command-protocol services,
SrsProtocol* protocol; * a high level protocol, media stream oriented services,
st_netfd_t stfd; * such as connect to vhost/app, play stream, get audio/video data.
public: */
SrsRtmp(st_netfd_t client_stfd); class SrsRtmp
virtual ~SrsRtmp(); {
public: private:
virtual SrsProtocol* get_protocol(); SrsProtocol* protocol;
virtual void set_recv_timeout(int64_t timeout_us); st_netfd_t stfd;
virtual int64_t get_recv_timeout(); public:
virtual void set_send_timeout(int64_t timeout_us); SrsRtmp(st_netfd_t client_stfd);
virtual int64_t get_recv_bytes(); virtual ~SrsRtmp();
virtual int64_t get_send_bytes(); public:
virtual int get_recv_kbps(); virtual SrsProtocol* get_protocol();
virtual int get_send_kbps(); virtual void set_recv_timeout(int64_t timeout_us);
virtual int recv_message(SrsCommonMessage** pmsg); virtual int64_t get_recv_timeout();
virtual int send_message(ISrsMessage* msg); virtual void set_send_timeout(int64_t timeout_us);
public: virtual int64_t get_recv_bytes();
virtual int handshake(); virtual int64_t get_send_bytes();
virtual int connect_app(SrsRequest* req); virtual int get_recv_kbps();
virtual int set_window_ack_size(int ack_size); virtual int get_send_kbps();
/** virtual int recv_message(SrsCommonMessage** pmsg);
* @type: The sender can mark this message hard (0), soft (1), or dynamic (2) virtual int send_message(ISrsMessage* msg);
* using the Limit type field. public:
*/ virtual int handshake();
virtual int set_peer_bandwidth(int bandwidth, int type); virtual int connect_app(SrsRequest* req);
virtual int response_connect_app(SrsRequest* req); virtual int set_window_ack_size(int ack_size);
virtual int on_bw_done(); /**
/** * @type: The sender can mark this message hard (0), soft (1), or dynamic (2)
* recv some message to identify the client. * using the Limit type field.
* @stream_id, client will createStream to play or publish by flash, */
* the stream_id used to response the createStream request. virtual int set_peer_bandwidth(int bandwidth, int type);
* @type, output the client type. virtual int response_connect_app(SrsRequest* req);
*/ virtual int on_bw_done();
virtual int identify_client(int stream_id, SrsClientType& type, std::string& stream_name); /**
/** * recv some message to identify the client.
* set the chunk size when client type identified. * @stream_id, client will createStream to play or publish by flash,
*/ * the stream_id used to response the createStream request.
virtual int set_chunk_size(int chunk_size); * @type, output the client type.
/** */
* when client type is play, response with packets: virtual int identify_client(int stream_id, SrsClientType& type, std::string& stream_name);
* StreamBegin, /**
* onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start)., * set the chunk size when client type identified.
* |RtmpSampleAccess(false, false), */
* onStatus(NetStream.Data.Start). virtual int set_chunk_size(int chunk_size);
*/ /**
virtual int start_play(int stream_id); * when client type is play, response with packets:
/** * StreamBegin,
* when client(type is play) send pause message, * onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start).,
* if is_pause, response the following packets: * |RtmpSampleAccess(false, false),
* onStatus(NetStream.Pause.Notify) * onStatus(NetStream.Data.Start).
* StreamEOF */
* if not is_pause, response the following packets: virtual int start_play(int stream_id);
* onStatus(NetStream.Unpause.Notify) /**
* StreamBegin * when client(type is play) send pause message,
*/ * if is_pause, response the following packets:
virtual int on_play_client_pause(int stream_id, bool is_pause); * onStatus(NetStream.Pause.Notify)
/** * StreamEOF
* when client type is publish, response with packets: * if not is_pause, response the following packets:
* releaseStream response * onStatus(NetStream.Unpause.Notify)
* FCPublish * StreamBegin
* FCPublish response */
* createStream response virtual int on_play_client_pause(int stream_id, bool is_pause);
* onFCPublish(NetStream.Publish.Start) /**
* onStatus(NetStream.Publish.Start) * when client type is publish, response with packets:
*/ * releaseStream response
virtual int start_fmle_publish(int stream_id); * FCPublish
/** * FCPublish response
* process the FMLE unpublish event. * createStream response
* @unpublish_tid the unpublish request transaction id. * onFCPublish(NetStream.Publish.Start)
*/ * onStatus(NetStream.Publish.Start)
virtual int fmle_unpublish(int stream_id, double unpublish_tid); */
/** virtual int start_fmle_publish(int stream_id);
* when client type is publish, response with packets: /**
* onStatus(NetStream.Publish.Start) * process the FMLE unpublish event.
*/ * @unpublish_tid the unpublish request transaction id.
virtual int start_flash_publish(int stream_id); */
private: virtual int fmle_unpublish(int stream_id, double unpublish_tid);
virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name); /**
virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name); * when client type is publish, response with packets:
virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name); * onStatus(NetStream.Publish.Start)
}; */
virtual int start_flash_publish(int stream_id);
private:
virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name);
virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name);
virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name);
};
#endif #endif