diff --git a/README.md b/README.md index 42e747cea..d0c54d209 100755 --- a/README.md +++ b/README.md @@ -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.
9. support listen at multiple ports.
10. support long time(>4.6hours) publish/play.
-11. [dev] support forward publish stream to build active-standby cluster.
-12. [plan] support live stream transcoding by ffmpeg.
-13. [plan] support full http callback api.
-14. [plan] support network based cli and json result.
-15. [plan] support bandwidth test api and flash client.
-16. no edge server, origin server only.
-17. no vod streaming, live streaming only.
-18. no multiple processes, single process only.
+11. high performace, 1800 connections(500kbps), 900Mbps, CPU 90.2%, 41MB
+12. support forward publish stream to build active-standby cluster.
+13. support broadcast by forward the stream to other servers(origin/edge).
+14. [plan] support live stream transcoding by ffmpeg.
+15. [plan] support full http callback api.
+16. [plan] support network based cli and json result.
+17. [plan] support bandwidth test api and flash client.
+18. no edge server, origin server only.
+19. no vod streaming, live streaming only.
+20. no multiple processes, single process only.
### Performance 1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB. diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf index 66525035f..8230d7e00 100755 --- a/trunk/conf/srs.conf +++ b/trunk/conf/srs.conf @@ -15,7 +15,7 @@ vhost __defaultVhost__ { hls_path ./objs/nginx/html; hls_fragment 5; hls_window 30; - forward 192.168.1.50; + forward 127.0.0.1:1936; } # the vhost which forward publish streams. vhost forward.vhost.com { @@ -103,6 +103,9 @@ pithy_print { publish 2000; # shared print interval for all play clients, in milliseconds. # 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; } diff --git a/trunk/src/core/srs_core_client.cpp b/trunk/src/core/srs_core_client.cpp old mode 100644 new mode 100755 index a930a1507..ead88b892 --- a/trunk/src/core/srs_core_client.cpp +++ b/trunk/src/core/srs_core_client.cpp @@ -1,524 +1,524 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 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 - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SRS_PULSE_TIMEOUT_MS 100 -#define SRS_SEND_TIMEOUT_US 5000000L -#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US -#define SRS_STREAM_BUSY_SLEEP_MS 2000 - -SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd) - : SrsConnection(srs_server, client_stfd) -{ - ip = NULL; - req = new SrsRequest(); - res = new SrsResponse(); - rtmp = new SrsRtmp(client_stfd); - refer = new SrsRefer(); -} - -SrsClient::~SrsClient() -{ - srs_freepa(ip); - srs_freep(req); - srs_freep(res); - srs_freep(rtmp); - srs_freep(refer); -} - -// TODO: return detail message when error for client. -int SrsClient::do_cycle() -{ - int ret = ERROR_SUCCESS; - - if ((ret = get_peer_ip()) != ERROR_SUCCESS) { - srs_error("get peer ip failed. ret=%d", ret); - return ret; - } - srs_trace("get peer ip success. ip=%s, send_to=%"PRId64", recv_to=%"PRId64"", - ip, SRS_SEND_TIMEOUT_US, SRS_RECV_TIMEOUT_US); - - rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_US); - rtmp->set_send_timeout(SRS_SEND_TIMEOUT_US); - - if ((ret = rtmp->handshake()) != ERROR_SUCCESS) { - srs_error("rtmp handshake failed. ret=%d", ret); - return ret; - } - srs_verbose("rtmp handshake success"); - - if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) { - srs_error("rtmp connect vhost/app failed. ret=%d", ret); - return ret; - } - srs_verbose("rtmp connect app success"); - - if ((ret = check_vhost()) != ERROR_SUCCESS) { - srs_error("check vhost failed. ret=%d", ret); - return ret; - } - srs_verbose("check vhost success."); - - srs_trace("rtmp connect app success. " - "tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s", - req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(), - req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), - req->app.c_str()); - - if ((ret = refer->check(req->pageUrl, config->get_refer(req->vhost))) != ERROR_SUCCESS) { - srs_error("check refer failed. ret=%d", ret); - return ret; - } - srs_verbose("check refer success."); - - if ((ret = rtmp->set_window_ack_size(2.5 * 1000 * 1000)) != ERROR_SUCCESS) { - srs_error("set window acknowledgement size failed. ret=%d", ret); - return ret; - } - srs_verbose("set window acknowledgement size success"); - - if ((ret = rtmp->set_peer_bandwidth(2.5 * 1000 * 1000, 2)) != ERROR_SUCCESS) { - srs_error("set peer bandwidth failed. ret=%d", ret); - return ret; - } - srs_verbose("set peer bandwidth success"); - - if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) { - srs_error("response connect app failed. ret=%d", ret); - return ret; - } - srs_verbose("response connect app success"); - - if ((ret = rtmp->on_bw_done()) != ERROR_SUCCESS) { - srs_error("on_bw_done failed. ret=%d", ret); - return ret; - } - srs_verbose("on_bw_done success"); - - SrsClientType type; - if ((ret = rtmp->identify_client(res->stream_id, type, req->stream)) != ERROR_SUCCESS) { - srs_error("identify client failed. ret=%d", ret); - return ret; - } - req->strip(); - srs_trace("identify client success. type=%d, stream_name=%s", type, req->stream.c_str()); - - int chunk_size = 4096; - SrsConfDirective* conf = config->get_chunk_size(); - if (conf && !conf->arg0().empty()) { - chunk_size = ::atoi(conf->arg0().c_str()); - } - if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) { - srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret); - return ret; - } - srs_trace("set chunk_size=%d success", chunk_size); - - // find a source to publish. - SrsSource* source = SrsSource::find(req->get_stream_url()); - srs_assert(source != NULL); - - // check publish available. - if (type != SrsClientPlay && !source->can_publish()) { - ret = ERROR_SYSTEM_STREAM_BUSY; - srs_warn("stream %s is already publishing. ret=%d", - req->get_stream_url().c_str(), ret); - // to delay request - st_usleep(SRS_STREAM_BUSY_SLEEP_MS * 1000); - return ret; - } - - bool enabled_cache = true; - conf = config->get_gop_cache(req->vhost); - if (conf && conf->arg0() == "off") { - enabled_cache = false; - } - source->set_cache(enabled_cache); - - srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache); - - switch (type) { - case SrsClientPlay: { - srs_verbose("start to play stream %s.", req->stream.c_str()); - - if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) { - srs_error("start to play stream failed. ret=%d", ret); - return ret; - } - srs_info("start to play stream %s success", req->stream.c_str()); - return playing(source); - } - case SrsClientFMLEPublish: { - srs_verbose("FMLE start to publish stream %s.", req->stream.c_str()); - - if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) { - srs_error("start to publish stream failed. ret=%d", ret); - return ret; - } - srs_info("start to publish stream %s success", req->stream.c_str()); - ret = publish(source, true); - source->on_unpublish(); - return ret; - } - case SrsClientFlashPublish: { - srs_verbose("flash start to publish stream %s.", req->stream.c_str()); - - if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) { - srs_error("flash start to publish stream failed. ret=%d", ret); - return ret; - } - srs_info("flash start to publish stream %s success", req->stream.c_str()); - ret = publish(source, false); - source->on_unpublish(); - return ret; - } - default: { - ret = ERROR_SYSTEM_CLIENT_INVALID; - srs_info("invalid client type=%d. ret=%d", type, ret); - return ret; - } - } - - return ret; -} - -int SrsClient::check_vhost() -{ - int ret = ERROR_SUCCESS; - - srs_assert(req != NULL); - - SrsConfDirective* vhost = config->get_vhost(req->vhost); - if (vhost == NULL) { - ret = ERROR_RTMP_VHOST_NOT_FOUND; - srs_error("vhost %s not found. ret=%d", req->vhost.c_str(), ret); - return ret; - } - - SrsConfDirective* conf = NULL; - if ((conf = config->get_vhost_enabled(req->vhost)) != NULL && conf->arg0() != "on") { - ret = ERROR_RTMP_VHOST_NOT_FOUND; - srs_error("vhost %s disabled. ret=%d", req->vhost.c_str(), ret); - return ret; - } - - if (req->vhost != vhost->arg0()) { - srs_trace("vhost change from %s to %s", req->vhost.c_str(), vhost->arg0().c_str()); - req->vhost = vhost->arg0(); - } - - return ret; -} - -int SrsClient::playing(SrsSource* source) -{ - int ret = ERROR_SUCCESS; - - if ((ret = refer->check(req->pageUrl, config->get_refer_play(req->vhost))) != ERROR_SUCCESS) { - srs_error("check play_refer failed. ret=%d", ret); - return ret; - } - srs_verbose("check play_refer success."); - - SrsConsumer* consumer = NULL; - if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) { - srs_error("create consumer failed. ret=%d", ret); - return ret; - } - - srs_assert(consumer != NULL); - SrsAutoFree(SrsConsumer, consumer, false); - srs_verbose("consumer created success."); - - rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000); - - SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER); - - while (true) { - pithy_print.elapse(SRS_PULSE_TIMEOUT_MS); - - // switch to other st-threads. - st_usleep(0); - - // read from client. - int ctl_msg_ret = ERROR_SUCCESS; - if (true) { - SrsCommonMessage* msg = NULL; - ctl_msg_ret = ret = rtmp->recv_message(&msg); - - srs_verbose("play loop recv message. ret=%d", ret); - if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) { - srs_error("recv client control message failed. ret=%d", ret); - return ret; - } - if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { - srs_error("process play control message failed. ret=%d", ret); - return ret; - } - } - - // get messages from consumer. - SrsSharedPtrMessage** msgs = NULL; - int count = 0; - if ((ret = consumer->get_packets(0, msgs, count)) != ERROR_SUCCESS) { - srs_error("get messages from consumer failed. ret=%d", ret); - return ret; - } - - // reportable - if (pithy_print.can_print()) { - srs_trace("-> clock=%u, time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", - (int)(srs_get_system_time_ms()/1000), pithy_print.get_age(), ctl_msg_ret, count, rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps()); - } - - if (count <= 0) { - srs_verbose("no packets in queue."); - continue; - } - SrsAutoFree(SrsSharedPtrMessage*, msgs, true); - - // sendout messages - for (int i = 0; i < count; i++) { - SrsSharedPtrMessage* msg = msgs[i]; - - // the send_message will free the msg, - // so set the msgs[i] to NULL. - msgs[i] = NULL; - - if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send message to client failed. ret=%d", ret); - return ret; - } - } - } - - return ret; -} - -int SrsClient::publish(SrsSource* source, bool is_fmle) -{ - int ret = ERROR_SUCCESS; - - if ((ret = refer->check(req->pageUrl, config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) { - srs_error("check publish_refer failed. ret=%d", ret); - return ret; - } - srs_verbose("check publish_refer success."); - - SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER); - - // notify the hls to prepare when publish start. - if ((ret = source->on_publish(req->vhost, req->app, req->stream)) != ERROR_SUCCESS) { - srs_error("hls on_publish failed. ret=%d", ret); - return ret; - } - srs_verbose("hls on_publish success."); - - while (true) { - // switch to other st-threads. - st_usleep(0); - - SrsCommonMessage* msg = NULL; - if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("recv identify client message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsCommonMessage, msg, false); - - pithy_print.set_age(msg->header.timestamp); - - // reportable - if (pithy_print.can_print()) { - srs_trace("<- clock=%u, time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", - (int)(srs_get_system_time_ms()/1000), pithy_print.get_age(), rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps()); - } - - if ((ret = process_publish_message(source, msg, is_fmle)) != ERROR_SUCCESS) { - srs_error("process publish message failed. ret=%d", ret); - return ret; - } - } - - return ret; -} - -int SrsClient::process_publish_message(SrsSource* source, SrsCommonMessage* msg, bool is_fmle) -{ - int ret = ERROR_SUCCESS; - - // process audio packet - if (msg->header.is_audio()) { - if ((ret = source->on_audio(msg)) != ERROR_SUCCESS) { - srs_error("source process audio message failed. ret=%d", ret); - return ret; - } - } - // process video packet - if (msg->header.is_video()) { - if ((ret = source->on_video(msg)) != ERROR_SUCCESS) { - srs_error("source process video message failed. ret=%d", ret); - return ret; - } - } - - // process onMetaData - if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) { - if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) { - srs_error("decode onMetaData message failed. ret=%d", ret); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - SrsOnMetaDataPacket* metadata = dynamic_cast(pkt); - if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) { - srs_error("source process onMetaData message failed. ret=%d", ret); - return ret; - } - srs_trace("process onMetaData message success."); - return ret; - } - - srs_trace("ignore AMF0/AMF3 data message."); - return ret; - } - - // process UnPublish event. - if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) { - if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) { - srs_error("decode unpublish message failed. ret=%d", ret); - return ret; - } - - // flash unpublish. - if (!is_fmle) { - srs_trace("flash publish finished."); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - SrsFMLEStartPacket* unpublish = dynamic_cast(pkt); - return rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id); - } - - srs_trace("ignore AMF0/AMF3 command message."); - return ret; - } - - return ret; -} - -int SrsClient::get_peer_ip() -{ - int ret = ERROR_SUCCESS; - - int fd = st_netfd_fileno(stfd); - - // discovery client information - sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - if (getpeername(fd, (sockaddr*)&addr, &addrlen) == -1) { - ret = ERROR_SOCKET_GET_PEER_NAME; - srs_error("discovery client information failed. ret=%d", ret); - return ret; - } - srs_verbose("get peer name success."); - - // ip v4 or v6 - char buf[INET6_ADDRSTRLEN]; - memset(buf, 0, sizeof(buf)); - - if ((inet_ntop(addr.sin_family, &addr.sin_addr, buf, sizeof(buf))) == NULL) { - ret = ERROR_SOCKET_GET_PEER_IP; - srs_error("convert client information failed. ret=%d", ret); - return ret; - } - srs_verbose("get peer ip of client ip=%s, fd=%d", buf, fd); - - ip = new char[strlen(buf) + 1]; - strcpy(ip, buf); - - srs_verbose("get peer ip success. ip=%s, fd=%d", ip, fd); - - return ret; -} - -int SrsClient::process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg) -{ - int ret = ERROR_SUCCESS; - - if (!msg) { - srs_verbose("ignore all empty message."); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - - if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { - srs_info("ignore all message except amf0/amf3 command."); - return ret; - } - - if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) { - srs_error("decode the amf0/amf3 command packet failed. ret=%d", ret); - return ret; - } - srs_info("decode the amf0/amf3 command packet success."); - - SrsPausePacket* pause = dynamic_cast(msg->get_packet()); - if (!pause) { - srs_info("ignore all amf0/amf3 command except pause."); - return ret; - } - - if ((ret = rtmp->on_play_client_pause(res->stream_id, pause->is_pause)) != ERROR_SUCCESS) { - srs_error("rtmp process play client pause failed. ret=%d", ret); - return ret; - } - - if ((ret = consumer->on_play_client_pause(pause->is_pause)) != ERROR_SUCCESS) { - srs_error("consumer process play client pause failed. ret=%d", ret); - return ret; - } - srs_info("process pause success, is_pause=%d, time=%d.", pause->is_pause, pause->time_ms); - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SRS_PULSE_TIMEOUT_MS 100 +#define SRS_SEND_TIMEOUT_US 5000000L +#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US +#define SRS_STREAM_BUSY_SLEEP_MS 2000 + +SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd) + : SrsConnection(srs_server, client_stfd) +{ + ip = NULL; + req = new SrsRequest(); + res = new SrsResponse(); + rtmp = new SrsRtmp(client_stfd); + refer = new SrsRefer(); +} + +SrsClient::~SrsClient() +{ + srs_freepa(ip); + srs_freep(req); + srs_freep(res); + srs_freep(rtmp); + srs_freep(refer); +} + +// TODO: return detail message when error for client. +int SrsClient::do_cycle() +{ + int ret = ERROR_SUCCESS; + + if ((ret = get_peer_ip()) != ERROR_SUCCESS) { + srs_error("get peer ip failed. ret=%d", ret); + return ret; + } + srs_trace("get peer ip success. ip=%s, send_to=%"PRId64", recv_to=%"PRId64"", + ip, SRS_SEND_TIMEOUT_US, SRS_RECV_TIMEOUT_US); + + rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_US); + rtmp->set_send_timeout(SRS_SEND_TIMEOUT_US); + + if ((ret = rtmp->handshake()) != ERROR_SUCCESS) { + srs_error("rtmp handshake failed. ret=%d", ret); + return ret; + } + srs_verbose("rtmp handshake success"); + + if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) { + srs_error("rtmp connect vhost/app failed. ret=%d", ret); + return ret; + } + srs_verbose("rtmp connect app success"); + + if ((ret = check_vhost()) != ERROR_SUCCESS) { + srs_error("check vhost failed. ret=%d", ret); + return ret; + } + srs_verbose("check vhost success."); + + srs_trace("rtmp connect app success. " + "tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s", + req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(), + req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), + req->app.c_str()); + + if ((ret = refer->check(req->pageUrl, config->get_refer(req->vhost))) != ERROR_SUCCESS) { + srs_error("check refer failed. ret=%d", ret); + return ret; + } + srs_verbose("check refer success."); + + if ((ret = rtmp->set_window_ack_size(2.5 * 1000 * 1000)) != ERROR_SUCCESS) { + srs_error("set window acknowledgement size failed. ret=%d", ret); + return ret; + } + srs_verbose("set window acknowledgement size success"); + + if ((ret = rtmp->set_peer_bandwidth(2.5 * 1000 * 1000, 2)) != ERROR_SUCCESS) { + srs_error("set peer bandwidth failed. ret=%d", ret); + return ret; + } + srs_verbose("set peer bandwidth success"); + + if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) { + srs_error("response connect app failed. ret=%d", ret); + return ret; + } + srs_verbose("response connect app success"); + + if ((ret = rtmp->on_bw_done()) != ERROR_SUCCESS) { + srs_error("on_bw_done failed. ret=%d", ret); + return ret; + } + srs_verbose("on_bw_done success"); + + SrsClientType type; + if ((ret = rtmp->identify_client(res->stream_id, type, req->stream)) != ERROR_SUCCESS) { + srs_error("identify client failed. ret=%d", ret); + return ret; + } + req->strip(); + srs_trace("identify client success. type=%d, stream_name=%s", type, req->stream.c_str()); + + int chunk_size = 4096; + SrsConfDirective* conf = config->get_chunk_size(); + if (conf && !conf->arg0().empty()) { + chunk_size = ::atoi(conf->arg0().c_str()); + } + if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) { + srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret); + return ret; + } + srs_trace("set chunk_size=%d success", chunk_size); + + // find a source to publish. + SrsSource* source = SrsSource::find(req->get_stream_url()); + srs_assert(source != NULL); + + // check publish available. + if (type != SrsClientPlay && !source->can_publish()) { + ret = ERROR_SYSTEM_STREAM_BUSY; + srs_warn("stream %s is already publishing. ret=%d", + req->get_stream_url().c_str(), ret); + // to delay request + st_usleep(SRS_STREAM_BUSY_SLEEP_MS * 1000); + return ret; + } + + bool enabled_cache = true; + conf = config->get_gop_cache(req->vhost); + if (conf && conf->arg0() == "off") { + enabled_cache = false; + } + source->set_cache(enabled_cache); + + srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache); + + switch (type) { + case SrsClientPlay: { + srs_verbose("start to play stream %s.", req->stream.c_str()); + + if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) { + srs_error("start to play stream failed. ret=%d", ret); + return ret; + } + srs_info("start to play stream %s success", req->stream.c_str()); + return playing(source); + } + case SrsClientFMLEPublish: { + srs_verbose("FMLE start to publish stream %s.", req->stream.c_str()); + + if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) { + srs_error("start to publish stream failed. ret=%d", ret); + return ret; + } + srs_info("start to publish stream %s success", req->stream.c_str()); + ret = publish(source, true); + source->on_unpublish(); + return ret; + } + case SrsClientFlashPublish: { + srs_verbose("flash start to publish stream %s.", req->stream.c_str()); + + if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) { + srs_error("flash start to publish stream failed. ret=%d", ret); + return ret; + } + srs_info("flash start to publish stream %s success", req->stream.c_str()); + ret = publish(source, false); + source->on_unpublish(); + return ret; + } + default: { + ret = ERROR_SYSTEM_CLIENT_INVALID; + srs_info("invalid client type=%d. ret=%d", type, ret); + return ret; + } + } + + return ret; +} + +int SrsClient::check_vhost() +{ + int ret = ERROR_SUCCESS; + + srs_assert(req != NULL); + + SrsConfDirective* vhost = config->get_vhost(req->vhost); + if (vhost == NULL) { + ret = ERROR_RTMP_VHOST_NOT_FOUND; + srs_error("vhost %s not found. ret=%d", req->vhost.c_str(), ret); + return ret; + } + + SrsConfDirective* conf = NULL; + if ((conf = config->get_vhost_enabled(req->vhost)) != NULL && conf->arg0() != "on") { + ret = ERROR_RTMP_VHOST_NOT_FOUND; + srs_error("vhost %s disabled. ret=%d", req->vhost.c_str(), ret); + return ret; + } + + if (req->vhost != vhost->arg0()) { + srs_trace("vhost change from %s to %s", req->vhost.c_str(), vhost->arg0().c_str()); + req->vhost = vhost->arg0(); + } + + return ret; +} + +int SrsClient::playing(SrsSource* source) +{ + int ret = ERROR_SUCCESS; + + if ((ret = refer->check(req->pageUrl, config->get_refer_play(req->vhost))) != ERROR_SUCCESS) { + srs_error("check play_refer failed. ret=%d", ret); + return ret; + } + srs_verbose("check play_refer success."); + + SrsConsumer* consumer = NULL; + if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) { + srs_error("create consumer failed. ret=%d", ret); + return ret; + } + + srs_assert(consumer != NULL); + SrsAutoFree(SrsConsumer, consumer, false); + srs_verbose("consumer created success."); + + rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000); + + SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER); + + while (true) { + pithy_print.elapse(SRS_PULSE_TIMEOUT_MS); + + // switch to other st-threads. + st_usleep(0); + + // read from client. + int ctl_msg_ret = ERROR_SUCCESS; + if (true) { + SrsCommonMessage* msg = NULL; + ctl_msg_ret = ret = rtmp->recv_message(&msg); + + srs_verbose("play loop recv message. ret=%d", ret); + if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) { + srs_error("recv client control message failed. ret=%d", ret); + return ret; + } + if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { + srs_error("process play control message failed. ret=%d", ret); + return ret; + } + } + + // get messages from consumer. + SrsSharedPtrMessage** msgs = NULL; + int count = 0; + if ((ret = consumer->get_packets(0, msgs, count)) != ERROR_SUCCESS) { + srs_error("get messages from consumer failed. ret=%d", ret); + return ret; + } + + // reportable + if (pithy_print.can_print()) { + srs_trace("-> clock=%u, time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", + (int)(srs_get_system_time_ms()/1000), pithy_print.get_age(), ctl_msg_ret, count, rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps()); + } + + if (count <= 0) { + srs_verbose("no packets in queue."); + continue; + } + SrsAutoFree(SrsSharedPtrMessage*, msgs, true); + + // sendout messages + for (int i = 0; i < count; i++) { + SrsSharedPtrMessage* msg = msgs[i]; + + // the send_message will free the msg, + // so set the msgs[i] to NULL. + msgs[i] = NULL; + + if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send message to client failed. ret=%d", ret); + return ret; + } + } + } + + return ret; +} + +int SrsClient::publish(SrsSource* source, bool is_fmle) +{ + int ret = ERROR_SUCCESS; + + if ((ret = refer->check(req->pageUrl, config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) { + srs_error("check publish_refer failed. ret=%d", ret); + return ret; + } + srs_verbose("check publish_refer success."); + + SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER); + + // notify the hls to prepare when publish start. + if ((ret = source->on_publish(req->vhost, req->app, req->stream)) != ERROR_SUCCESS) { + srs_error("hls on_publish failed. ret=%d", ret); + return ret; + } + srs_verbose("hls on_publish success."); + + while (true) { + // switch to other st-threads. + st_usleep(0); + + SrsCommonMessage* msg = NULL; + if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { + srs_error("recv identify client message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsCommonMessage, msg, false); + + pithy_print.set_age(msg->header.timestamp); + + // reportable + if (pithy_print.can_print()) { + srs_trace("<- clock=%u, time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", + (int)(srs_get_system_time_ms()/1000), pithy_print.get_age(), rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps()); + } + + if ((ret = process_publish_message(source, msg, is_fmle)) != ERROR_SUCCESS) { + srs_error("process publish message failed. ret=%d", ret); + return ret; + } + } + + return ret; +} + +int SrsClient::process_publish_message(SrsSource* source, SrsCommonMessage* msg, bool is_fmle) +{ + int ret = ERROR_SUCCESS; + + // process audio packet + if (msg->header.is_audio()) { + if ((ret = source->on_audio(msg)) != ERROR_SUCCESS) { + srs_error("source process audio message failed. ret=%d", ret); + return ret; + } + } + // process video packet + if (msg->header.is_video()) { + if ((ret = source->on_video(msg)) != ERROR_SUCCESS) { + srs_error("source process video message failed. ret=%d", ret); + return ret; + } + } + + // process onMetaData + if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) { + if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) { + srs_error("decode onMetaData message failed. ret=%d", ret); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + SrsOnMetaDataPacket* metadata = dynamic_cast(pkt); + if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) { + srs_error("source process onMetaData message failed. ret=%d", ret); + return ret; + } + srs_trace("process onMetaData message success."); + return ret; + } + + srs_trace("ignore AMF0/AMF3 data message."); + return ret; + } + + // process UnPublish event. + if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) { + if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) { + srs_error("decode unpublish message failed. ret=%d", ret); + return ret; + } + + // flash unpublish. + if (!is_fmle) { + srs_trace("flash publish finished."); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + SrsFMLEStartPacket* unpublish = dynamic_cast(pkt); + return rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id); + } + + srs_trace("ignore AMF0/AMF3 command message."); + return ret; + } + + return ret; +} + +int SrsClient::get_peer_ip() +{ + int ret = ERROR_SUCCESS; + + int fd = st_netfd_fileno(stfd); + + // discovery client information + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + if (getpeername(fd, (sockaddr*)&addr, &addrlen) == -1) { + ret = ERROR_SOCKET_GET_PEER_NAME; + srs_error("discovery client information failed. ret=%d", ret); + return ret; + } + srs_verbose("get peer name success."); + + // ip v4 or v6 + char buf[INET6_ADDRSTRLEN]; + memset(buf, 0, sizeof(buf)); + + if ((inet_ntop(addr.sin_family, &addr.sin_addr, buf, sizeof(buf))) == NULL) { + ret = ERROR_SOCKET_GET_PEER_IP; + srs_error("convert client information failed. ret=%d", ret); + return ret; + } + srs_verbose("get peer ip of client ip=%s, fd=%d", buf, fd); + + ip = new char[strlen(buf) + 1]; + strcpy(ip, buf); + + srs_verbose("get peer ip success. ip=%s, fd=%d", ip, fd); + + return ret; +} + +int SrsClient::process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg) +{ + int ret = ERROR_SUCCESS; + + if (!msg) { + srs_verbose("ignore all empty message."); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + + if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { + srs_info("ignore all message except amf0/amf3 command."); + return ret; + } + + if ((ret = msg->decode_packet(rtmp->get_protocol())) != ERROR_SUCCESS) { + srs_error("decode the amf0/amf3 command packet failed. ret=%d", ret); + return ret; + } + srs_info("decode the amf0/amf3 command packet success."); + + SrsPausePacket* pause = dynamic_cast(msg->get_packet()); + if (!pause) { + srs_info("ignore all amf0/amf3 command except pause."); + return ret; + } + + if ((ret = rtmp->on_play_client_pause(res->stream_id, pause->is_pause)) != ERROR_SUCCESS) { + srs_error("rtmp process play client pause failed. ret=%d", ret); + return ret; + } + + if ((ret = consumer->on_play_client_pause(pause->is_pause)) != ERROR_SUCCESS) { + srs_error("consumer process play client pause failed. ret=%d", ret); + return ret; + } + srs_info("process pause success, is_pause=%d, time=%d.", pause->is_pause, pause->time_ms); + + return ret; +} + diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp old mode 100644 new mode 100755 index dcce32f02..4c0de37b4 --- a/trunk/src/core/srs_core_config.cpp +++ b/trunk/src/core/srs_core_config.cpp @@ -1,822 +1,832 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 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 - -#include -#include -#include -#include -// file operations. -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR) - -int64_t FILE_SIZE(int fd) -{ - int64_t pre = FILE_OFFSET(fd); - int64_t pos = lseek(fd, 0, SEEK_END); - lseek(fd, pre, SEEK_SET); - return pos; -} - -#define LF (char)0x0a -#define CR (char)0x0d - -bool is_common_space(char ch) -{ - return (ch == ' ' || ch == '\t' || ch == CR || ch == LF); -} - -#define CONF_BUFFER_SIZE 4096 - -SrsFileBuffer::SrsFileBuffer() -{ - fd = -1; - line = 0; - - pos = last = start = new char[CONF_BUFFER_SIZE]; - end = start + CONF_BUFFER_SIZE; -} - -SrsFileBuffer::~SrsFileBuffer() -{ - if (fd > 0) { - close(fd); - } - srs_freepa(start); -} - -int SrsFileBuffer::open(const char* filename) -{ - assert(fd == -1); - - if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { - srs_error("open conf file error. errno=%d(%s)", errno, strerror(errno)); - return ERROR_SYSTEM_CONFIG_INVALID; - } - - line = 1; - - return ERROR_SUCCESS; -} - -SrsConfDirective::SrsConfDirective() -{ -} - -SrsConfDirective::~SrsConfDirective() -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - srs_freep(directive); - } - directives.clear(); -} - -std::string SrsConfDirective::arg0() -{ - if (args.size() > 0) { - return args.at(0); - } - - return ""; -} - -std::string SrsConfDirective::arg1() -{ - if (args.size() > 1) { - return args.at(1); - } - - return ""; -} - -std::string SrsConfDirective::arg2() -{ - if (args.size() > 2) { - return args.at(2); - } - - return ""; -} - -SrsConfDirective* SrsConfDirective::at(int index) -{ - return directives.at(index); -} - -SrsConfDirective* SrsConfDirective::get(std::string _name) -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - if (directive->name == _name) { - return directive; - } - } - - return NULL; -} - -int SrsConfDirective::parse(const char* filename) -{ - int ret = ERROR_SUCCESS; - - SrsFileBuffer buffer; - - if ((ret = buffer.open(filename)) != ERROR_SUCCESS) { - return ret; - } - - return parse_conf(&buffer, parse_file); -} - -// see: ngx_conf_parse -int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type) -{ - int ret = ERROR_SUCCESS; - - while (true) { - std::vector args; - ret = read_token(buffer, args); - - /** - * ret maybe: - * ERROR_SYSTEM_CONFIG_INVALID error. - * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found - * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found - * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found - * ERROR_SYSTEM_CONFIG_EOF the config file is done - */ - if (ret == ERROR_SYSTEM_CONFIG_INVALID) { - return ret; - } - if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) { - if (type != parse_block) { - srs_error("line %d: unexpected \"}\"", buffer->line); - return ret; - } - return ERROR_SUCCESS; - } - if (ret == ERROR_SYSTEM_CONFIG_EOF) { - if (type == parse_block) { - srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line); - return ret; - } - return ERROR_SUCCESS; - } - - if (args.empty()) { - srs_error("line %d: empty directive.", buffer->line); - return ret; - } - - // build directive tree. - SrsConfDirective* directive = new SrsConfDirective(); - - directive->conf_line = buffer->line; - directive->name = args[0]; - args.erase(args.begin()); - directive->args.swap(args); - - directives.push_back(directive); - - if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) { - if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) { - return ret; - } - } - } - - return ret; -} - -// see: ngx_conf_read_token -int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args) -{ - int ret = ERROR_SUCCESS; - - char* pstart = buffer->pos; - int startline = buffer->line; - - bool sharp_comment = false; - - bool d_quoted = false; - bool s_quoted = false; - - bool need_space = false; - bool last_space = true; - - while (true) { - if ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) { - if (!args.empty() || !last_space) { - srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ret; - } - - char ch = *buffer->pos++; - - if (ch == LF) { - buffer->line++; - sharp_comment = false; - } - - if (sharp_comment) { - continue; - } - - if (need_space) { - if (is_common_space(ch)) { - last_space = true; - need_space = false; - continue; - } - if (ch == ';') { - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - } - if (ch == '{') { - return ERROR_SYSTEM_CONFIG_BLOCK_START; - } - srs_error("line %d: unexpected '%c'", buffer->line, ch); - return ERROR_SYSTEM_CONFIG_INVALID; - } - - // last charecter is space. - if (last_space) { - if (is_common_space(ch)) { - continue; - } - pstart = buffer->pos - 1; - startline = buffer->line; - switch (ch) { - case ';': - if (args.size() == 0) { - srs_error("line %d: unexpected ';'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - case '{': - if (args.size() == 0) { - srs_error("line %d: unexpected '{'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_BLOCK_START; - case '}': - if (args.size() != 0) { - srs_error("line %d: unexpected '}'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_BLOCK_END; - case '#': - sharp_comment = 1; - continue; - case '"': - pstart++; - d_quoted = true; - last_space = 0; - continue; - case '\'': - pstart++; - s_quoted = true; - last_space = 0; - continue; - default: - last_space = 0; - continue; - } - } else { - // last charecter is not space - bool found = false; - if (d_quoted) { - if (ch == '"') { - d_quoted = false; - need_space = true; - found = true; - } - } else if (s_quoted) { - if (ch == '\'') { - s_quoted = false; - need_space = true; - found = true; - } - } else if (is_common_space(ch) || ch == ';' || ch == '{') { - last_space = true; - found = 1; - } - - if (found) { - int len = buffer->pos - pstart; - char* word = new char[len]; - memcpy(word, pstart, len); - word[len - 1] = 0; - - args.push_back(word); - srs_freepa(word); - - if (ch == ';') { - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - } - if (ch == '{') { - return ERROR_SYSTEM_CONFIG_BLOCK_START; - } - } - } - } - - return ret; -} - -int SrsConfDirective::refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart) -{ - int ret = ERROR_SUCCESS; - - if (buffer->pos < buffer->last) { - return ret; - } - - int size = FILE_SIZE(buffer->fd) - FILE_OFFSET(buffer->fd); - - if (size <= 0) { - return ERROR_SYSTEM_CONFIG_EOF; - } - - int len = buffer->pos - buffer->start; - if (len >= CONF_BUFFER_SIZE) { - buffer->line = startline; - - if (!d_quoted && !s_quoted) { - srs_error("line %d: too long parameter \"%*s...\" started", - buffer->line, 10, buffer->start); - - } else { - srs_error("line %d: too long parameter, " - "probably missing terminating '%c' character", buffer->line, d_quoted? '"':'\''); - } - return ERROR_SYSTEM_CONFIG_INVALID; - } - - if (len) { - memmove(buffer->start, pstart, len); - } - - size = srs_min(size, buffer->end - (buffer->start + len)); - int n = read(buffer->fd, buffer->start + len, size); - if (n != size) { - srs_error("read file read error. expect %d, actual %d bytes.", size, n); - return ERROR_SYSTEM_CONFIG_INVALID; - } - - buffer->pos = buffer->start + len; - buffer->last = buffer->pos + n; - pstart = buffer->start; - - return ret; -} - -SrsConfig* config = new SrsConfig(); - -SrsConfig::SrsConfig() -{ - show_help = false; - show_version = false; - - root = new SrsConfDirective(); - root->conf_line = 0; - root->name = "root"; -} - -SrsConfig::~SrsConfig() -{ - srs_freep(root); -} - -int SrsConfig::reload() -{ - int ret = ERROR_SUCCESS; - - SrsConfig conf; - if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) { - srs_error("config reloader parse file failed. ret=%d", ret); - return ret; - } - srs_info("config reloader parse file success."); - - // store current root to old_root, - // and reap the root from conf to current root. - SrsConfDirective* old_root = root; - SrsAutoFree(SrsConfDirective, old_root, false); - - root = conf.root; - conf.root = NULL; - - // merge config. - std::vector::iterator it; - - // merge config: listen - if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - SrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) { - srs_error("notify subscribes reload listen failed. ret=%d", ret); - return ret; - } - } - srs_trace("reload listen success."); - } - // merge config: pithy_print - if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - SrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) { - srs_error("notify subscribes pithy_print listen failed. ret=%d", ret); - return ret; - } - } - srs_trace("reload pithy_print success."); - } - - return ret; -} - -void SrsConfig::subscribe(SrsReloadHandler* handler) -{ - std::vector::iterator it; - - it = std::find(subscribes.begin(), subscribes.end(), handler); - if (it != subscribes.end()) { - return; - } - - subscribes.push_back(handler); -} - -void SrsConfig::unsubscribe(SrsReloadHandler* handler) -{ - std::vector::iterator it; - - it = std::find(subscribes.begin(), subscribes.end(), handler); - if (it == subscribes.end()) { - return; - } - - subscribes.erase(it); -} - -// see: ngx_get_options -int SrsConfig::parse_options(int argc, char** argv) -{ - int ret = ERROR_SUCCESS; - - for (int i = 1; i < argc; i++) { - if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) { - return ret; - } - } - - if (show_help) { - print_help(argv); - } - - if (show_version) { - printf("%s\n", RTMP_SIG_SRS_VERSION); - } - - if (show_help || show_version) { - exit(0); - } - - if (config_file.empty()) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret); - return ret; - } - - return parse_file(config_file.c_str()); -} - -SrsConfDirective* SrsConfig::get_vhost(std::string vhost) -{ - srs_assert(root); - - for (int i = 0; i < (int)root->directives.size(); i++) { - SrsConfDirective* conf = root->at(i); - - if (conf->name != "vhost") { - continue; - } - - if (conf->arg0() == vhost) { - return conf; - } - } - - if (vhost != RTMP_VHOST_DEFAULT) { - return get_vhost(RTMP_VHOST_DEFAULT); - } - - return NULL; -} - -SrsConfDirective* SrsConfig::get_vhost_enabled(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("enabled"); -} - -SrsConfDirective* SrsConfig::get_gop_cache(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("gop_cache"); -} - -SrsConfDirective* SrsConfig::get_forward(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("forward"); -} - -SrsConfDirective* SrsConfig::get_hls(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("hls"); -} - -SrsConfDirective* SrsConfig::get_hls_path(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("hls_path"); -} - -SrsConfDirective* SrsConfig::get_hls_fragment(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("hls_fragment"); -} - -SrsConfDirective* SrsConfig::get_hls_window(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("hls_window"); -} - -SrsConfDirective* SrsConfig::get_refer(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer"); -} - -SrsConfDirective* SrsConfig::get_refer_play(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer_play"); -} - -SrsConfDirective* SrsConfig::get_refer_publish(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer_publish"); -} - -SrsConfDirective* SrsConfig::get_listen() -{ - return root->get("listen"); -} - -SrsConfDirective* SrsConfig::get_chunk_size() -{ - return root->get("chunk_size"); -} - -SrsConfDirective* SrsConfig::get_pithy_print_publish() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return NULL; - } - - return pithy->get("publish"); -} - -SrsConfDirective* SrsConfig::get_pithy_print_play() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return NULL; - } - - return pithy->get("play"); -} - -int SrsConfig::parse_file(const char* filename) -{ - int ret = ERROR_SUCCESS; - - config_file = filename; - - if (config_file.empty()) { - return ERROR_SYSTEM_CONFIG_INVALID; - } - - if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) { - return ret; - } - - SrsConfDirective* conf = NULL; - if ((conf = get_listen()) == NULL || conf->args.size() == 0) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("line %d: conf error, " - "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret); - return ret; - } - // TODO: check the hls. - // TODO: check other config. - // TODO: check hls. - // TODO: check ssl. - - return ret; -} - -int SrsConfig::parse_argv(int& i, char** argv) -{ - int ret = ERROR_SUCCESS; - - char* p = argv[i]; - - if (*p++ != '-') { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("invalid options(index=%d, value=%s), " - "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret); - return ret; - } - - while (*p) { - switch (*p++) { - case '?': - case 'h': - show_help = true; - break; - case 'v': - case 'V': - show_version = true; - break; - case 'c': - if (*p) { - config_file = p; - return ret; - } - if (argv[++i]) { - config_file = argv[i]; - return ret; - } - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("option \"-c\" requires parameter, ret=%d", ret); - return ret; - default: - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret); - return ret; - } - } - - return ret; -} - -void SrsConfig::print_help(char** argv) -{ - printf(RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION - " Copyright (c) 2013 winlin\n" - "configuration: "SRS_CONFIGURE"\n" - "Usage: %s [-h?vV] [-c ]\n" - "\n" - "Options:\n" - " -?-h : show help\n" - " -v-V : show version and exit\n" - " -c filename : set configuration file\n" - "\n" - RTMP_SIG_SRS_WEB"\n" - RTMP_SIG_SRS_URL"\n" - "Email: "RTMP_SIG_SRS_EMAIL"\n" - "\n", - argv[0]); -} - -bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) -{ - if (!a || !b) { - return false; - } - - if (a->name != b->name) { - return false; - } - - if (a->args.size() != b->args.size()) { - return false; - } - - for (int i = 0; i < (int)a->args.size(); i++) { - if (a->args.at(i) != b->args.at(i)) { - return false; - } - } - - if (a->directives.size() != b->directives.size()) { - return false; - } - - for (int i = 0; i < (int)a->directives.size(); i++) { - SrsConfDirective* a0 = a->at(i); - SrsConfDirective* b0 = b->at(i); - - if (!srs_directive_equals(a0, b0)) { - return false; - } - } - - return true; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 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 + +#include +#include +#include +#include +// file operations. +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR) + +int64_t FILE_SIZE(int fd) +{ + int64_t pre = FILE_OFFSET(fd); + int64_t pos = lseek(fd, 0, SEEK_END); + lseek(fd, pre, SEEK_SET); + return pos; +} + +#define LF (char)0x0a +#define CR (char)0x0d + +bool is_common_space(char ch) +{ + return (ch == ' ' || ch == '\t' || ch == CR || ch == LF); +} + +#define CONF_BUFFER_SIZE 4096 + +SrsFileBuffer::SrsFileBuffer() +{ + fd = -1; + line = 0; + + pos = last = start = new char[CONF_BUFFER_SIZE]; + end = start + CONF_BUFFER_SIZE; +} + +SrsFileBuffer::~SrsFileBuffer() +{ + if (fd > 0) { + close(fd); + } + srs_freepa(start); +} + +int SrsFileBuffer::open(const char* filename) +{ + assert(fd == -1); + + if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { + srs_error("open conf file error. errno=%d(%s)", errno, strerror(errno)); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + line = 1; + + return ERROR_SUCCESS; +} + +SrsConfDirective::SrsConfDirective() +{ +} + +SrsConfDirective::~SrsConfDirective() +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + srs_freep(directive); + } + directives.clear(); +} + +std::string SrsConfDirective::arg0() +{ + if (args.size() > 0) { + return args.at(0); + } + + return ""; +} + +std::string SrsConfDirective::arg1() +{ + if (args.size() > 1) { + return args.at(1); + } + + return ""; +} + +std::string SrsConfDirective::arg2() +{ + if (args.size() > 2) { + return args.at(2); + } + + return ""; +} + +SrsConfDirective* SrsConfDirective::at(int index) +{ + return directives.at(index); +} + +SrsConfDirective* SrsConfDirective::get(std::string _name) +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + if (directive->name == _name) { + return directive; + } + } + + return NULL; +} + +int SrsConfDirective::parse(const char* filename) +{ + int ret = ERROR_SUCCESS; + + SrsFileBuffer buffer; + + if ((ret = buffer.open(filename)) != ERROR_SUCCESS) { + return ret; + } + + return parse_conf(&buffer, parse_file); +} + +// see: ngx_conf_parse +int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type) +{ + int ret = ERROR_SUCCESS; + + while (true) { + std::vector args; + ret = read_token(buffer, args); + + /** + * ret maybe: + * ERROR_SYSTEM_CONFIG_INVALID error. + * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found + * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found + * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found + * ERROR_SYSTEM_CONFIG_EOF the config file is done + */ + if (ret == ERROR_SYSTEM_CONFIG_INVALID) { + return ret; + } + if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) { + if (type != parse_block) { + srs_error("line %d: unexpected \"}\"", buffer->line); + return ret; + } + return ERROR_SUCCESS; + } + if (ret == ERROR_SYSTEM_CONFIG_EOF) { + if (type == parse_block) { + srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line); + return ret; + } + return ERROR_SUCCESS; + } + + if (args.empty()) { + srs_error("line %d: empty directive.", buffer->line); + return ret; + } + + // build directive tree. + SrsConfDirective* directive = new SrsConfDirective(); + + directive->conf_line = buffer->line; + directive->name = args[0]; + args.erase(args.begin()); + directive->args.swap(args); + + directives.push_back(directive); + + if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) { + if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) { + return ret; + } + } + } + + return ret; +} + +// see: ngx_conf_read_token +int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args) +{ + int ret = ERROR_SUCCESS; + + char* pstart = buffer->pos; + int startline = buffer->line; + + bool sharp_comment = false; + + bool d_quoted = false; + bool s_quoted = false; + + bool need_space = false; + bool last_space = true; + + while (true) { + if ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) { + if (!args.empty() || !last_space) { + srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ret; + } + + char ch = *buffer->pos++; + + if (ch == LF) { + buffer->line++; + sharp_comment = false; + } + + if (sharp_comment) { + continue; + } + + if (need_space) { + if (is_common_space(ch)) { + last_space = true; + need_space = false; + continue; + } + if (ch == ';') { + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + } + if (ch == '{') { + return ERROR_SYSTEM_CONFIG_BLOCK_START; + } + srs_error("line %d: unexpected '%c'", buffer->line, ch); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + // last charecter is space. + if (last_space) { + if (is_common_space(ch)) { + continue; + } + pstart = buffer->pos - 1; + startline = buffer->line; + switch (ch) { + case ';': + if (args.size() == 0) { + srs_error("line %d: unexpected ';'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + case '{': + if (args.size() == 0) { + srs_error("line %d: unexpected '{'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_BLOCK_START; + case '}': + if (args.size() != 0) { + srs_error("line %d: unexpected '}'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_BLOCK_END; + case '#': + sharp_comment = 1; + continue; + case '"': + pstart++; + d_quoted = true; + last_space = 0; + continue; + case '\'': + pstart++; + s_quoted = true; + last_space = 0; + continue; + default: + last_space = 0; + continue; + } + } else { + // last charecter is not space + bool found = false; + if (d_quoted) { + if (ch == '"') { + d_quoted = false; + need_space = true; + found = true; + } + } else if (s_quoted) { + if (ch == '\'') { + s_quoted = false; + need_space = true; + found = true; + } + } else if (is_common_space(ch) || ch == ';' || ch == '{') { + last_space = true; + found = 1; + } + + if (found) { + int len = buffer->pos - pstart; + char* word = new char[len]; + memcpy(word, pstart, len); + word[len - 1] = 0; + + args.push_back(word); + srs_freepa(word); + + if (ch == ';') { + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + } + if (ch == '{') { + return ERROR_SYSTEM_CONFIG_BLOCK_START; + } + } + } + } + + return ret; +} + +int SrsConfDirective::refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart) +{ + int ret = ERROR_SUCCESS; + + if (buffer->pos < buffer->last) { + return ret; + } + + int size = FILE_SIZE(buffer->fd) - FILE_OFFSET(buffer->fd); + + if (size <= 0) { + return ERROR_SYSTEM_CONFIG_EOF; + } + + int len = buffer->pos - buffer->start; + if (len >= CONF_BUFFER_SIZE) { + buffer->line = startline; + + if (!d_quoted && !s_quoted) { + srs_error("line %d: too long parameter \"%*s...\" started", + buffer->line, 10, buffer->start); + + } else { + srs_error("line %d: too long parameter, " + "probably missing terminating '%c' character", buffer->line, d_quoted? '"':'\''); + } + return ERROR_SYSTEM_CONFIG_INVALID; + } + + if (len) { + memmove(buffer->start, pstart, len); + } + + size = srs_min(size, buffer->end - (buffer->start + len)); + int n = read(buffer->fd, buffer->start + len, size); + if (n != size) { + srs_error("read file read error. expect %d, actual %d bytes.", size, n); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + buffer->pos = buffer->start + len; + buffer->last = buffer->pos + n; + pstart = buffer->start; + + return ret; +} + +SrsConfig* config = new SrsConfig(); + +SrsConfig::SrsConfig() +{ + show_help = false; + show_version = false; + + root = new SrsConfDirective(); + root->conf_line = 0; + root->name = "root"; +} + +SrsConfig::~SrsConfig() +{ + srs_freep(root); +} + +int SrsConfig::reload() +{ + int ret = ERROR_SUCCESS; + + SrsConfig conf; + if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) { + srs_error("config reloader parse file failed. ret=%d", ret); + return ret; + } + srs_info("config reloader parse file success."); + + // store current root to old_root, + // and reap the root from conf to current root. + SrsConfDirective* old_root = root; + SrsAutoFree(SrsConfDirective, old_root, false); + + root = conf.root; + conf.root = NULL; + + // merge config. + std::vector::iterator it; + + // merge config: listen + if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + SrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) { + srs_error("notify subscribes reload listen failed. ret=%d", ret); + return ret; + } + } + srs_trace("reload listen success."); + } + // merge config: pithy_print + if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + SrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) { + srs_error("notify subscribes pithy_print listen failed. ret=%d", ret); + return ret; + } + } + srs_trace("reload pithy_print success."); + } + + return ret; +} + +void SrsConfig::subscribe(SrsReloadHandler* handler) +{ + std::vector::iterator it; + + it = std::find(subscribes.begin(), subscribes.end(), handler); + if (it != subscribes.end()) { + return; + } + + subscribes.push_back(handler); +} + +void SrsConfig::unsubscribe(SrsReloadHandler* handler) +{ + std::vector::iterator it; + + it = std::find(subscribes.begin(), subscribes.end(), handler); + if (it == subscribes.end()) { + return; + } + + subscribes.erase(it); +} + +// see: ngx_get_options +int SrsConfig::parse_options(int argc, char** argv) +{ + int ret = ERROR_SUCCESS; + + for (int i = 1; i < argc; i++) { + if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) { + return ret; + } + } + + if (show_help) { + print_help(argv); + } + + if (show_version) { + printf("%s\n", RTMP_SIG_SRS_VERSION); + } + + if (show_help || show_version) { + exit(0); + } + + if (config_file.empty()) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret); + return ret; + } + + return parse_file(config_file.c_str()); +} + +SrsConfDirective* SrsConfig::get_vhost(std::string vhost) +{ + srs_assert(root); + + for (int i = 0; i < (int)root->directives.size(); i++) { + SrsConfDirective* conf = root->at(i); + + if (conf->name != "vhost") { + continue; + } + + if (conf->arg0() == vhost) { + return conf; + } + } + + if (vhost != RTMP_VHOST_DEFAULT) { + return get_vhost(RTMP_VHOST_DEFAULT); + } + + return NULL; +} + +SrsConfDirective* SrsConfig::get_vhost_enabled(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("enabled"); +} + +SrsConfDirective* SrsConfig::get_gop_cache(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("gop_cache"); +} + +SrsConfDirective* SrsConfig::get_forward(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("forward"); +} + +SrsConfDirective* SrsConfig::get_hls(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hls"); +} + +SrsConfDirective* SrsConfig::get_hls_path(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hls_path"); +} + +SrsConfDirective* SrsConfig::get_hls_fragment(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hls_fragment"); +} + +SrsConfDirective* SrsConfig::get_hls_window(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("hls_window"); +} + +SrsConfDirective* SrsConfig::get_refer(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer"); +} + +SrsConfDirective* SrsConfig::get_refer_play(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer_play"); +} + +SrsConfDirective* SrsConfig::get_refer_publish(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer_publish"); +} + +SrsConfDirective* SrsConfig::get_listen() +{ + return root->get("listen"); +} + +SrsConfDirective* SrsConfig::get_chunk_size() +{ + return root->get("chunk_size"); +} + +SrsConfDirective* SrsConfig::get_pithy_print_publish() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return NULL; + } + + return pithy->get("publish"); +} + +SrsConfDirective* SrsConfig::get_pithy_print_forwarder() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return NULL; + } + + return pithy->get("forwarder"); +} + +SrsConfDirective* SrsConfig::get_pithy_print_play() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return NULL; + } + + return pithy->get("play"); +} + +int SrsConfig::parse_file(const char* filename) +{ + int ret = ERROR_SUCCESS; + + config_file = filename; + + if (config_file.empty()) { + return ERROR_SYSTEM_CONFIG_INVALID; + } + + if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) { + return ret; + } + + SrsConfDirective* conf = NULL; + if ((conf = get_listen()) == NULL || conf->args.size() == 0) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("line %d: conf error, " + "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret); + return ret; + } + // TODO: check the hls. + // TODO: check other config. + // TODO: check hls. + // TODO: check ssl. + + return ret; +} + +int SrsConfig::parse_argv(int& i, char** argv) +{ + int ret = ERROR_SUCCESS; + + char* p = argv[i]; + + if (*p++ != '-') { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("invalid options(index=%d, value=%s), " + "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret); + return ret; + } + + while (*p) { + switch (*p++) { + case '?': + case 'h': + show_help = true; + break; + case 'v': + case 'V': + show_version = true; + break; + case 'c': + if (*p) { + config_file = p; + return ret; + } + if (argv[++i]) { + config_file = argv[i]; + return ret; + } + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("option \"-c\" requires parameter, ret=%d", ret); + return ret; + default: + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret); + return ret; + } + } + + return ret; +} + +void SrsConfig::print_help(char** argv) +{ + printf(RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION + " Copyright (c) 2013 winlin\n" + "configuration: "SRS_CONFIGURE"\n" + "Usage: %s [-h?vV] [-c ]\n" + "\n" + "Options:\n" + " -?-h : show help\n" + " -v-V : show version and exit\n" + " -c filename : set configuration file\n" + "\n" + RTMP_SIG_SRS_WEB"\n" + RTMP_SIG_SRS_URL"\n" + "Email: "RTMP_SIG_SRS_EMAIL"\n" + "\n", + argv[0]); +} + +bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) +{ + if (!a || !b) { + return false; + } + + if (a->name != b->name) { + return false; + } + + if (a->args.size() != b->args.size()) { + return false; + } + + for (int i = 0; i < (int)a->args.size(); i++) { + if (a->args.at(i) != b->args.at(i)) { + return false; + } + } + + if (a->directives.size() != b->directives.size()) { + return false; + } + + for (int i = 0; i < (int)a->directives.size(); i++) { + SrsConfDirective* a0 = a->at(i); + SrsConfDirective* b0 = b->at(i); + + if (!srs_directive_equals(a0, b0)) { + return false; + } + } + + return true; +} + diff --git a/trunk/src/core/srs_core_config.hpp b/trunk/src/core/srs_core_config.hpp old mode 100644 new mode 100755 index 8c27c1c27..4e2bc6b58 --- a/trunk/src/core/srs_core_config.hpp +++ b/trunk/src/core/srs_core_config.hpp @@ -1,144 +1,145 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 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_CORE_CONIFG_HPP -#define SRS_CORE_CONIFG_HPP - -/* -#include -*/ -#include - -#include -#include - -#include - -// default vhost for rtmp -#define RTMP_VHOST_DEFAULT "__defaultVhost__" - -#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" -#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 -#define SRS_CONF_DEFAULT_HLS_WINDOW 60 -// in ms, for HLS aac sync time. -#define SRS_CONF_DEFAULT_AAC_SYNC 100 -// in ms, for HLS aac flush the audio -#define SRS_CONF_DEFAULT_AAC_DELAY 300 - -class SrsFileBuffer -{ -public: - int fd; - int line; - // start of buffer. - char* start; - // end of buffer. - char* end; - // current consumed position. - char* pos; - // last available position. - char* last; - - SrsFileBuffer(); - virtual ~SrsFileBuffer(); - virtual int open(const char* filename); -}; - -class SrsConfDirective -{ -public: - int conf_line; - std::string name; - std::vector args; - std::vector directives; -public: - SrsConfDirective(); - virtual ~SrsConfDirective(); - std::string arg0(); - std::string arg1(); - std::string arg2(); - SrsConfDirective* at(int index); - SrsConfDirective* get(std::string _name); -public: - virtual int parse(const char* filename); -public: - enum SrsDirectiveType{parse_file, parse_block}; - virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type); - virtual int read_token(SrsFileBuffer* buffer, std::vector& args); - virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart); -}; - -/** -* the config parser. -* 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, -* for it maybe free in the reload st-thread cycle. -* you can keep it before st-thread switch, or simply never keep it. -*/ -class SrsConfig -{ -private: - bool show_help; - bool show_version; - std::string config_file; - SrsConfDirective* root; - std::vector subscribes; -public: - SrsConfig(); - virtual ~SrsConfig(); -public: - virtual int reload(); - virtual void subscribe(SrsReloadHandler* handler); - virtual void unsubscribe(SrsReloadHandler* handler); -public: - virtual int parse_options(int argc, char** argv); - virtual SrsConfDirective* get_vhost(std::string vhost); - virtual SrsConfDirective* get_vhost_enabled(std::string vhost); - virtual SrsConfDirective* get_gop_cache(std::string vhost); - virtual SrsConfDirective* get_forward(std::string vhost); - virtual SrsConfDirective* get_hls(std::string vhost); - virtual SrsConfDirective* get_hls_path(std::string vhost); - virtual SrsConfDirective* get_hls_fragment(std::string vhost); - virtual SrsConfDirective* get_hls_window(std::string vhost); - virtual SrsConfDirective* get_refer(std::string vhost); - virtual SrsConfDirective* get_refer_play(std::string vhost); - virtual SrsConfDirective* get_refer_publish(std::string vhost); - virtual SrsConfDirective* get_listen(); - virtual SrsConfDirective* get_chunk_size(); - virtual SrsConfDirective* get_pithy_print_publish(); - virtual SrsConfDirective* get_pithy_print_play(); -private: - virtual int parse_file(const char* filename); - virtual int parse_argv(int& i, char** argv); - virtual void print_help(char** argv); -}; - -/** -* deep compare directive. -*/ -bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b); - -// global config -extern SrsConfig* config; - +/* +The MIT License (MIT) + +Copyright (c) 2013 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_CORE_CONIFG_HPP +#define SRS_CORE_CONIFG_HPP + +/* +#include +*/ +#include + +#include +#include + +#include + +// default vhost for rtmp +#define RTMP_VHOST_DEFAULT "__defaultVhost__" + +#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" +#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 +#define SRS_CONF_DEFAULT_HLS_WINDOW 60 +// in ms, for HLS aac sync time. +#define SRS_CONF_DEFAULT_AAC_SYNC 100 +// in ms, for HLS aac flush the audio +#define SRS_CONF_DEFAULT_AAC_DELAY 300 + +class SrsFileBuffer +{ +public: + int fd; + int line; + // start of buffer. + char* start; + // end of buffer. + char* end; + // current consumed position. + char* pos; + // last available position. + char* last; + + SrsFileBuffer(); + virtual ~SrsFileBuffer(); + virtual int open(const char* filename); +}; + +class SrsConfDirective +{ +public: + int conf_line; + std::string name; + std::vector args; + std::vector directives; +public: + SrsConfDirective(); + virtual ~SrsConfDirective(); + std::string arg0(); + std::string arg1(); + std::string arg2(); + SrsConfDirective* at(int index); + SrsConfDirective* get(std::string _name); +public: + virtual int parse(const char* filename); +public: + enum SrsDirectiveType{parse_file, parse_block}; + virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type); + virtual int read_token(SrsFileBuffer* buffer, std::vector& args); + virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart); +}; + +/** +* the config parser. +* 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, +* for it maybe free in the reload st-thread cycle. +* you can keep it before st-thread switch, or simply never keep it. +*/ +class SrsConfig +{ +private: + bool show_help; + bool show_version; + std::string config_file; + SrsConfDirective* root; + std::vector subscribes; +public: + SrsConfig(); + virtual ~SrsConfig(); +public: + virtual int reload(); + virtual void subscribe(SrsReloadHandler* handler); + virtual void unsubscribe(SrsReloadHandler* handler); +public: + virtual int parse_options(int argc, char** argv); + virtual SrsConfDirective* get_vhost(std::string vhost); + virtual SrsConfDirective* get_vhost_enabled(std::string vhost); + virtual SrsConfDirective* get_gop_cache(std::string vhost); + virtual SrsConfDirective* get_forward(std::string vhost); + virtual SrsConfDirective* get_hls(std::string vhost); + virtual SrsConfDirective* get_hls_path(std::string vhost); + virtual SrsConfDirective* get_hls_fragment(std::string vhost); + virtual SrsConfDirective* get_hls_window(std::string vhost); + virtual SrsConfDirective* get_refer(std::string vhost); + virtual SrsConfDirective* get_refer_play(std::string vhost); + virtual SrsConfDirective* get_refer_publish(std::string vhost); + virtual SrsConfDirective* get_listen(); + virtual SrsConfDirective* get_chunk_size(); + virtual SrsConfDirective* get_pithy_print_publish(); + virtual SrsConfDirective* get_pithy_print_forwarder(); + virtual SrsConfDirective* get_pithy_print_play(); +private: + virtual int parse_file(const char* filename); + virtual int parse_argv(int& i, char** argv); + virtual void print_help(char** argv); +}; + +/** +* deep compare directive. +*/ +bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b); + +// global config +extern SrsConfig* config; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_forward.cpp b/trunk/src/core/srs_core_forward.cpp old mode 100644 new mode 100755 index f3b3e03f2..dce9a4b21 --- a/trunk/src/core/srs_core_forward.cpp +++ b/trunk/src/core/srs_core_forward.cpp @@ -1,277 +1,350 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 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 - -#include -#include -#include -#include -#include - -#include -#include -#include - -#define SRS_FORWARDER_SLEEP_MS 2000 -#define SRS_SEND_TIMEOUT_US 3000000L -#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US - -SrsForwarder::SrsForwarder() -{ - client = NULL; - tid = NULL; - stfd = NULL; - loop = false; - stream_id = 0; -} - -SrsForwarder::~SrsForwarder() -{ - on_unpublish(); -} - -int SrsForwarder::on_publish(std::string vhost, std::string _app, std::string stream, std::string forward_server) -{ - int ret = ERROR_SUCCESS; - - app = _app; - - tc_url = "rtmp://"; - tc_url += vhost; - tc_url += "/"; - tc_url += app; - - stream_name = stream; - server = forward_server; - port = 1935; - - size_t pos = forward_server.find(":"); - if (pos != std::string::npos) { - port = ::atoi(forward_server.substr(pos + 1).c_str()); - server = forward_server.substr(0, pos); - } - - if ((ret = open_socket()) != ERROR_SUCCESS) { - return ret; - } - - srs_assert(!tid); - if((tid = st_thread_create(forward_thread, this, 1, 0)) == NULL){ - ret = ERROR_ST_CREATE_FORWARD_THREAD; - srs_error("st_thread_create failed. ret=%d", ret); - return ret; - } - - return ret; -} - -void SrsForwarder::on_unpublish() -{ - if (tid) { - loop = false; - st_thread_interrupt(tid); - st_thread_join(tid, NULL); - tid = NULL; - } - - if (stfd) { - int fd = st_netfd_fileno(stfd); - st_netfd_close(stfd); - stfd = NULL; - - // st does not close it sometimes, - // close it manually. - close(fd); - } - - srs_freep(client); -} - -int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata) -{ - int ret = ERROR_SUCCESS; - return ret; -} - -int SrsForwarder::on_audio(SrsSharedPtrMessage* msg) -{ - int ret = ERROR_SUCCESS; - return ret; -} - -int SrsForwarder::on_video(SrsSharedPtrMessage* msg) -{ - int ret = ERROR_SUCCESS; - return ret; -} - -int SrsForwarder::open_socket() -{ - 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); - - int sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock == -1){ - ret = ERROR_SOCKET_CREATE; - srs_error("create socket error. ret=%d", ret); - return ret; - } - - stfd = st_netfd_open_socket(sock); - if(stfd == NULL){ - ret = ERROR_ST_OPEN_SOCKET; - srs_error("st_netfd_open_socket failed. ret=%d", ret); - return ret; - } - - srs_freep(client); - client = new SrsRtmpClient(stfd); - - return ret; -} - -int SrsForwarder::connect_server() -{ - int ret = ERROR_SUCCESS; - - std::string ip = parse_server(server); - if (ip.empty()) { - ret = ERROR_SYSTEM_IP_INVALID; - srs_error("dns resolve server error, ip empty. ret=%d", ret); - return ret; - } - - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = inet_addr(ip.c_str()); - - 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); - return ret; - } - srs_trace("connect to server success. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port); - - return ret; -} - -std::string SrsForwarder::parse_server(std::string host) -{ - if (inet_addr(host.c_str()) != INADDR_NONE) { - return host; - } - - hostent* answer = gethostbyname(host.c_str()); - if (answer == NULL) { - srs_error("dns resolve host %s error.", host.c_str()); - return ""; - } - - char ipv4[16]; - memset(ipv4, 0, sizeof(ipv4)); - for (int i = 0; i < answer->h_length; i++) { - inet_ntop(AF_INET, answer->h_addr_list[i], ipv4, sizeof(ipv4)); - srs_info("dns resolve host %s to %s.", host.c_str(), ipv4); - break; - } - - return ipv4; -} - -int SrsForwarder::forward_cycle_imp() -{ - int ret = ERROR_SUCCESS; - - client->set_recv_timeout(SRS_RECV_TIMEOUT_US); - client->set_send_timeout(SRS_SEND_TIMEOUT_US); - - if ((ret = connect_server()) != ERROR_SUCCESS) { - return ret; - } - srs_assert(client); - - if ((ret = client->handshake()) != ERROR_SUCCESS) { - srs_error("handshake with server failed. ret=%d", ret); - return ret; - } - 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); - return ret; - } - if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) { - srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret); - return ret; - } - if ((ret = client->publish(stream_name, stream_id)) != ERROR_SUCCESS) { - srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d", - stream_name.c_str(), stream_id, ret); - return ret; - } - - 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; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define SRS_PULSE_TIMEOUT_MS 100 +#define SRS_FORWARDER_SLEEP_MS 2000 +#define SRS_SEND_TIMEOUT_US 3000000L +#define SRS_RECV_TIMEOUT_US SRS_SEND_TIMEOUT_US + +SrsForwarder::SrsForwarder() +{ + client = NULL; + tid = NULL; + stfd = NULL; + loop = false; + stream_id = 0; +} + +SrsForwarder::~SrsForwarder() +{ + on_unpublish(); + + std::vector::iterator it; + for (it = msgs.begin(); it != msgs.end(); ++it) { + SrsSharedPtrMessage* msg = *it; + srs_freep(msg); + } + msgs.clear(); +} + +int SrsForwarder::on_publish(std::string vhost, std::string _app, std::string stream, std::string forward_server) +{ + int ret = ERROR_SUCCESS; + + app = _app; + + tc_url = "rtmp://"; + tc_url += vhost; + tc_url += "/"; + tc_url += app; + + stream_name = stream; + server = forward_server; + port = 1935; + + size_t pos = forward_server.find(":"); + if (pos != std::string::npos) { + port = ::atoi(forward_server.substr(pos + 1).c_str()); + server = forward_server.substr(0, pos); + } + + if ((ret = open_socket()) != ERROR_SUCCESS) { + return ret; + } + + srs_assert(!tid); + if((tid = st_thread_create(forward_thread, this, 1, 0)) == NULL){ + ret = ERROR_ST_CREATE_FORWARD_THREAD; + srs_error("st_thread_create failed. ret=%d", ret); + return ret; + } + + return ret; +} + +void SrsForwarder::on_unpublish() +{ + if (tid) { + loop = false; + st_thread_interrupt(tid); + st_thread_join(tid, NULL); + tid = NULL; + } + + if (stfd) { + int fd = st_netfd_fileno(stfd); + st_netfd_close(stfd); + stfd = NULL; + + // st does not close it sometimes, + // close it manually. + close(fd); + } + + srs_freep(client); +} + +int SrsForwarder::on_meta_data(SrsSharedPtrMessage* metadata) +{ + int ret = ERROR_SUCCESS; + + msgs.push_back(metadata); + + return ret; +} + +int SrsForwarder::on_audio(SrsSharedPtrMessage* msg) +{ + int ret = ERROR_SUCCESS; + + msgs.push_back(msg); + + return ret; +} + +int SrsForwarder::on_video(SrsSharedPtrMessage* msg) +{ + int ret = ERROR_SUCCESS; + + msgs.push_back(msg); + + return ret; +} + +int SrsForwarder::open_socket() +{ + 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); + + int sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock == -1){ + ret = ERROR_SOCKET_CREATE; + srs_error("create socket error. ret=%d", ret); + return ret; + } + + stfd = st_netfd_open_socket(sock); + if(stfd == NULL){ + ret = ERROR_ST_OPEN_SOCKET; + srs_error("st_netfd_open_socket failed. ret=%d", ret); + return ret; + } + + srs_freep(client); + client = new SrsRtmpClient(stfd); + + return ret; +} + +int SrsForwarder::connect_server() +{ + int ret = ERROR_SUCCESS; + + std::string ip = parse_server(server); + if (ip.empty()) { + ret = ERROR_SYSTEM_IP_INVALID; + srs_error("dns resolve server error, ip empty. ret=%d", ret); + return ret; + } + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(ip.c_str()); + + 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); + return ret; + } + srs_trace("connect to server success. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port); + + return ret; +} + +std::string SrsForwarder::parse_server(std::string host) +{ + if (inet_addr(host.c_str()) != INADDR_NONE) { + return host; + } + + hostent* answer = gethostbyname(host.c_str()); + if (answer == NULL) { + srs_error("dns resolve host %s error.", host.c_str()); + return ""; + } + + char ipv4[16]; + memset(ipv4, 0, sizeof(ipv4)); + for (int i = 0; i < answer->h_length; i++) { + inet_ntop(AF_INET, answer->h_addr_list[i], ipv4, sizeof(ipv4)); + srs_info("dns resolve host %s to %s.", host.c_str(), ipv4); + break; + } + + return ipv4; +} + +int SrsForwarder::forward_cycle_imp() +{ + int ret = ERROR_SUCCESS; + + client->set_recv_timeout(SRS_RECV_TIMEOUT_US); + client->set_send_timeout(SRS_SEND_TIMEOUT_US); + + if ((ret = connect_server()) != ERROR_SUCCESS) { + return ret; + } + srs_assert(client); + + if ((ret = client->handshake()) != ERROR_SUCCESS) { + srs_error("handshake with server failed. ret=%d", ret); + return ret; + } + 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); + return ret; + } + if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) { + srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret); + return ret; + } + if ((ret = client->publish(stream_name, stream_id)) != ERROR_SUCCESS) { + srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d", + stream_name.c_str(), stream_id, ret); + return ret; + } + + if ((ret = forward()) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsForwarder::forward() +{ + int ret = ERROR_SUCCESS; + + client->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000); + + SrsPithyPrint pithy_print(SRS_STAGE_FORWARDER); + + while (loop) { + pithy_print.elapse(SRS_PULSE_TIMEOUT_MS); + + // switch to other st-threads. + st_usleep(0); + + // 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; +} + diff --git a/trunk/src/core/srs_core_forward.hpp b/trunk/src/core/srs_core_forward.hpp old mode 100644 new mode 100755 index 0eaf1d236..530032430 --- a/trunk/src/core/srs_core_forward.hpp +++ b/trunk/src/core/srs_core_forward.hpp @@ -1,77 +1,80 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 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_CORE_FORWARD_HPP -#define SRS_CORE_FORWARD_HPP - -/* -#include -*/ -#include - -#include - -#include - -class SrsSharedPtrMessage; -class SrsOnMetaDataPacket; -class SrsRtmpClient; - -/** -* forward the stream to other servers. -*/ -class SrsForwarder -{ -private: - std::string app; - std::string tc_url; - std::string stream_name; - int stream_id; - std::string server; - int port; -private: - st_netfd_t stfd; - st_thread_t tid; - bool loop; -private: - SrsRtmpClient* client; -public: - SrsForwarder(); - virtual ~SrsForwarder(); -public: - virtual int on_publish(std::string vhost, std::string app, std::string stream, std::string forward_server); - virtual void on_unpublish(); - virtual int on_meta_data(SrsSharedPtrMessage* metadata); - virtual int on_audio(SrsSharedPtrMessage* msg); - virtual int on_video(SrsSharedPtrMessage* msg); -private: - virtual int open_socket(); - virtual int connect_server(); - std::string parse_server(std::string host); -private: - virtual int forward_cycle_imp(); - virtual void forward_cycle(); - static void* forward_thread(void* arg); -}; - -#endif +/* +The MIT License (MIT) + +Copyright (c) 2013 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_CORE_FORWARD_HPP +#define SRS_CORE_FORWARD_HPP + +/* +#include +*/ +#include + +#include +#include + +#include + +class SrsSharedPtrMessage; +class SrsOnMetaDataPacket; +class SrsRtmpClient; + +/** +* forward the stream to other servers. +*/ +class SrsForwarder +{ +private: + std::string app; + std::string tc_url; + std::string stream_name; + int stream_id; + std::string server; + int port; +private: + st_netfd_t stfd; + st_thread_t tid; + bool loop; +private: + SrsRtmpClient* client; + std::vector msgs; +public: + SrsForwarder(); + virtual ~SrsForwarder(); +public: + virtual int on_publish(std::string vhost, std::string app, std::string stream, std::string forward_server); + virtual void on_unpublish(); + virtual int on_meta_data(SrsSharedPtrMessage* metadata); + virtual int on_audio(SrsSharedPtrMessage* msg); + virtual int on_video(SrsSharedPtrMessage* msg); +private: + virtual int open_socket(); + virtual int connect_server(); + std::string parse_server(std::string host); +private: + virtual int forward_cycle_imp(); + virtual int forward(); + virtual void forward_cycle(); + static void* forward_thread(void* arg); +}; + +#endif diff --git a/trunk/src/core/srs_core_pithy_print.cpp b/trunk/src/core/srs_core_pithy_print.cpp old mode 100644 new mode 100755 index fc875d399..77bca2853 --- a/trunk/src/core/srs_core_pithy_print.cpp +++ b/trunk/src/core/srs_core_pithy_print.cpp @@ -1,164 +1,173 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 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 - -#include -#include - -#include -#include -#include -#include - -#define SRS_STAGE_DEFAULT_INTERVAL_MS 1200 -#define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300 -#define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100 - -struct SrsStageInfo : public SrsReloadHandler -{ - int stage_id; - int pithy_print_time_ms; - int nb_clients; - - SrsStageInfo(int _stage_id) - { - stage_id = _stage_id; - nb_clients = 0; - - update_print_time(); - - config->subscribe(this); - } - virtual ~SrsStageInfo() - { - config->unsubscribe(this); - } - void update_print_time() - { - switch (stage_id) { - case SRS_STAGE_PLAY_USER: { - pithy_print_time_ms = SRS_STAGE_PLAY_USER_INTERVAL_MS; - SrsConfDirective* conf = config->get_pithy_print_play(); - if (conf && !conf->arg0().empty()) { - pithy_print_time_ms = ::atoi(conf->arg0().c_str()); - } - break; - } - case SRS_STAGE_PUBLISH_USER: { - pithy_print_time_ms = SRS_STAGE_PUBLISH_USER_INTERVAL_MS; - SrsConfDirective* conf = config->get_pithy_print_publish(); - if (conf && !conf->arg0().empty()) { - pithy_print_time_ms = ::atoi(conf->arg0().c_str()); - } - break; - } - default: { - pithy_print_time_ms = SRS_STAGE_DEFAULT_INTERVAL_MS; - break; - } - } - } -public: - virtual int on_reload_pithy_print() - { - update_print_time(); - return ERROR_SUCCESS; - } -}; -static std::map _srs_stages; - -SrsPithyPrint::SrsPithyPrint(int _stage_id) -{ - stage_id = _stage_id; - client_id = enter_stage(); - printed_age = age = 0; -} - -SrsPithyPrint::~SrsPithyPrint() -{ - leave_stage(); -} - -int SrsPithyPrint::enter_stage() -{ - SrsStageInfo* stage = NULL; - - std::map::iterator it = _srs_stages.find(stage_id); - if (it == _srs_stages.end()) { - stage = _srs_stages[stage_id] = new SrsStageInfo(stage_id); - } else { - stage = it->second; - } - - srs_assert(stage != NULL); - client_id = stage->nb_clients++; - - 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); - - return client_id; -} - -void SrsPithyPrint::leave_stage() -{ - SrsStageInfo* stage = _srs_stages[stage_id]; - srs_assert(stage != NULL); - - stage->nb_clients--; - - 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::elapse(int64_t time_ms) -{ - age += time_ms; -} - -bool SrsPithyPrint::can_print() -{ - SrsStageInfo* stage = _srs_stages[stage_id]; - srs_assert(stage != NULL); - - int64_t alive_age = age - printed_age; - int64_t can_print_age = stage->nb_clients * stage->pithy_print_time_ms; - - bool can_print = alive_age >= can_print_age; - if (can_print) { - printed_age = age; - } - - return can_print; -} - -int64_t SrsPithyPrint::get_age() -{ - return age; -} - -void SrsPithyPrint::set_age(int64_t _age) -{ - age = _age; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 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 + +#include +#include + +#include +#include +#include +#include + +#define SRS_STAGE_DEFAULT_INTERVAL_MS 1200 +#define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300 +#define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100 +#define SRS_STAGE_FORWARDER_INTERVAL_MS 2000 + +struct SrsStageInfo : public SrsReloadHandler +{ + int stage_id; + int pithy_print_time_ms; + int nb_clients; + + SrsStageInfo(int _stage_id) + { + stage_id = _stage_id; + nb_clients = 0; + + update_print_time(); + + config->subscribe(this); + } + virtual ~SrsStageInfo() + { + config->unsubscribe(this); + } + void update_print_time() + { + switch (stage_id) { + case SRS_STAGE_PLAY_USER: { + pithy_print_time_ms = SRS_STAGE_PLAY_USER_INTERVAL_MS; + SrsConfDirective* conf = config->get_pithy_print_play(); + if (conf && !conf->arg0().empty()) { + pithy_print_time_ms = ::atoi(conf->arg0().c_str()); + } + break; + } + case SRS_STAGE_PUBLISH_USER: { + pithy_print_time_ms = SRS_STAGE_PUBLISH_USER_INTERVAL_MS; + SrsConfDirective* conf = config->get_pithy_print_publish(); + if (conf && !conf->arg0().empty()) { + pithy_print_time_ms = ::atoi(conf->arg0().c_str()); + } + break; + } + case SRS_STAGE_FORWARDER: { + 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()); + } + break; + } + default: { + pithy_print_time_ms = SRS_STAGE_DEFAULT_INTERVAL_MS; + break; + } + } + } +public: + virtual int on_reload_pithy_print() + { + update_print_time(); + return ERROR_SUCCESS; + } +}; +static std::map _srs_stages; + +SrsPithyPrint::SrsPithyPrint(int _stage_id) +{ + stage_id = _stage_id; + client_id = enter_stage(); + printed_age = age = 0; +} + +SrsPithyPrint::~SrsPithyPrint() +{ + leave_stage(); +} + +int SrsPithyPrint::enter_stage() +{ + SrsStageInfo* stage = NULL; + + std::map::iterator it = _srs_stages.find(stage_id); + if (it == _srs_stages.end()) { + stage = _srs_stages[stage_id] = new SrsStageInfo(stage_id); + } else { + stage = it->second; + } + + srs_assert(stage != NULL); + client_id = stage->nb_clients++; + + 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); + + return client_id; +} + +void SrsPithyPrint::leave_stage() +{ + SrsStageInfo* stage = _srs_stages[stage_id]; + srs_assert(stage != NULL); + + stage->nb_clients--; + + 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::elapse(int64_t time_ms) +{ + age += time_ms; +} + +bool SrsPithyPrint::can_print() +{ + SrsStageInfo* stage = _srs_stages[stage_id]; + srs_assert(stage != NULL); + + int64_t alive_age = age - printed_age; + int64_t can_print_age = stage->nb_clients * stage->pithy_print_time_ms; + + bool can_print = alive_age >= can_print_age; + if (can_print) { + printed_age = age; + } + + return can_print; +} + +int64_t SrsPithyPrint::get_age() +{ + return age; +} + +void SrsPithyPrint::set_age(int64_t _age) +{ + age = _age; +} + diff --git a/trunk/src/core/srs_core_pithy_print.hpp b/trunk/src/core/srs_core_pithy_print.hpp old mode 100644 new mode 100755 index a0e60a484..0ce09a4dd --- a/trunk/src/core/srs_core_pithy_print.hpp +++ b/trunk/src/core/srs_core_pithy_print.hpp @@ -1,82 +1,84 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 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_CORE_PITHY_PRINT_HPP -#define SRS_CORE_PITHY_PRINT_HPP - -/* -#include -*/ - -#include - -// the pithy stage for all play clients. -#define SRS_STAGE_PLAY_USER 1 -// the pithy stage for all publish clients. -#define SRS_STAGE_PUBLISH_USER 2 - -/** -* 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, -* if there is 10clients, then all clients should print in 10*3s. -*/ -class SrsPithyPrint -{ -private: - int client_id; - int stage_id; - int64_t age; - int64_t printed_age; -public: - /** - * @param _stage_id defined in SRS_STAGE_xxx, eg. SRS_STAGE_PLAY_USER. - */ - SrsPithyPrint(int _stage_id); - virtual ~SrsPithyPrint(); -private: - /** - * enter the specified stage, return the client id. - */ - virtual int enter_stage(); - /** - * leave the specified stage, release the client id. - */ - virtual void leave_stage(); -public: - /** - * specified client elapse some time. - */ - virtual void elapse(int64_t time_ms); - /** - * whether current client can print. - */ - virtual bool can_print(); - /** - * get the elapsed time in ms. - */ - virtual int64_t get_age(); - virtual void set_age(int64_t _age); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 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_CORE_PITHY_PRINT_HPP +#define SRS_CORE_PITHY_PRINT_HPP + +/* +#include +*/ + +#include + +// the pithy stage for all play clients. +#define SRS_STAGE_PLAY_USER 1 +// the pithy stage for all publish clients. +#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, +* if there is 10clients, then all clients should print in 10*3s. +*/ +class SrsPithyPrint +{ +private: + int client_id; + int stage_id; + int64_t age; + int64_t printed_age; +public: + /** + * @param _stage_id defined in SRS_STAGE_xxx, eg. SRS_STAGE_PLAY_USER. + */ + SrsPithyPrint(int _stage_id); + virtual ~SrsPithyPrint(); +private: + /** + * enter the specified stage, return the client id. + */ + virtual int enter_stage(); + /** + * leave the specified stage, release the client id. + */ + virtual void leave_stage(); +public: + /** + * specified client elapse some time. + */ + virtual void elapse(int64_t time_ms); + /** + * whether current client can print. + */ + virtual bool can_print(); + /** + * get the elapsed time in ms. + */ + virtual int64_t get_age(); + virtual void set_age(int64_t _age); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_rtmp.cpp b/trunk/src/core/srs_core_rtmp.cpp old mode 100644 new mode 100755 index 7d2cfa684..b2ef3195f --- a/trunk/src/core/srs_core_rtmp.cpp +++ b/trunk/src/core/srs_core_rtmp.cpp @@ -1,1086 +1,1116 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 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 - -#include -#include -#include -#include -#include -#include -#include - -/** -* the signature for packets to client. -*/ -#define RTMP_SIG_FMS_VER "3,5,3,888" -#define RTMP_SIG_AMF0_VER 0 -#define RTMP_SIG_CLIENT_ID "ASAICiss" - -/** -* onStatus consts. -*/ -#define StatusLevel "level" -#define StatusCode "code" -#define StatusDescription "description" -#define StatusDetails "details" -#define StatusClientId "clientid" -// status value -#define StatusLevelStatus "status" -// code value -#define StatusCodeConnectSuccess "NetConnection.Connect.Success" -#define StatusCodeStreamReset "NetStream.Play.Reset" -#define StatusCodeStreamStart "NetStream.Play.Start" -#define StatusCodeStreamPause "NetStream.Pause.Notify" -#define StatusCodeStreamUnpause "NetStream.Unpause.Notify" -#define StatusCodePublishStart "NetStream.Publish.Start" -#define StatusCodeDataStart "NetStream.Data.Start" -#define StatusCodeUnpublishSuccess "NetStream.Unpublish.Success" - -// FMLE -#define RTMP_AMF0_COMMAND_ON_FC_PUBLISH "onFCPublish" -#define RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH "onFCUnpublish" - -// default stream id for response the createStream request. -#define SRS_DEFAULT_SID 1 - -SrsRequest::SrsRequest() -{ - objectEncoding = RTMP_SIG_AMF0_VER; -} - -SrsRequest::~SrsRequest() -{ -} - -int SrsRequest::discovery_app() -{ - int ret = ERROR_SUCCESS; - - size_t pos = std::string::npos; - std::string url = tcUrl; - - if ((pos = url.find("://")) != std::string::npos) { - schema = url.substr(0, pos); - url = url.substr(schema.length() + 3); - srs_verbose("discovery schema=%s", schema.c_str()); - } - - if ((pos = url.find("/")) != std::string::npos) { - vhost = url.substr(0, pos); - url = url.substr(vhost.length() + 1); - srs_verbose("discovery vhost=%s", vhost.c_str()); - } - - port = "1935"; - if ((pos = vhost.find(":")) != std::string::npos) { - port = vhost.substr(pos + 1); - vhost = vhost.substr(0, pos); - srs_verbose("discovery vhost=%s, port=%s", vhost.c_str(), port.c_str()); - } - - app = url; - srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s", - schema.c_str(), vhost.c_str(), port.c_str(), app.c_str()); - - if (schema.empty() || vhost.empty() || port.empty() || app.empty()) { - ret = ERROR_RTMP_REQ_TCURL; - srs_error("discovery tcUrl failed. " - "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d", - tcUrl.c_str(), schema.c_str(), vhost.c_str(), port.c_str(), app.c_str(), ret); - return ret; - } - - strip(); - - return ret; -} - -std::string SrsRequest::get_stream_url() -{ - std::string url = ""; - - url += vhost; - url += "/"; - url += app; - url += "/"; - url += stream; - - return url; -} - -void SrsRequest::strip() -{ - trim(vhost, "/ \n\r\t"); - trim(app, "/ \n\r\t"); - trim(stream, "/ \n\r\t"); -} - -std::string& SrsRequest::trim(std::string& str, std::string chs) -{ - for (int i = 0; i < (int)chs.length(); i++) { - char ch = chs.at(i); - - for (std::string::iterator it = str.begin(); it != str.end();) { - if (ch == *it) { - it = str.erase(it); - } else { - ++it; - } - } - } - - return str; -} - -SrsResponse::SrsResponse() -{ - stream_id = SRS_DEFAULT_SID; -} - -SrsResponse::~SrsResponse() -{ -} - -SrsRtmpClient::SrsRtmpClient(st_netfd_t _stfd) -{ - stfd = _stfd; - protocol = new SrsProtocol(stfd); -} - -SrsRtmpClient::~SrsRtmpClient() -{ - srs_freep(protocol); -} - -void SrsRtmpClient::set_recv_timeout(int64_t timeout_us) -{ - protocol->set_recv_timeout(timeout_us); -} - -void SrsRtmpClient::set_send_timeout(int64_t timeout_us) -{ - protocol->set_send_timeout(timeout_us); -} - -int SrsRtmpClient::handshake() -{ - int ret = ERROR_SUCCESS; - - SrsSocket skt(stfd); - - SrsComplexHandshake complex_hs; - SrsSimpleHandshake simple_hs; - if ((ret = simple_hs.handshake_with_server(skt, complex_hs)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -int SrsRtmpClient::connect_app(std::string app, std::string tc_url) -{ - int ret = ERROR_SUCCESS; - - // Connect(vhost, app) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsConnectAppPacket* pkt = new SrsConnectAppPacket(); - msg->set_packet(pkt, 0); - - pkt->command_object = new SrsAmf0Object(); - pkt->command_object->set("app", new SrsAmf0String(app.c_str())); - pkt->command_object->set("swfUrl", new SrsAmf0String()); - pkt->command_object->set("tcUrl", new SrsAmf0String(tc_url.c_str())); - pkt->command_object->set("fpad", new SrsAmf0Boolean(false)); - pkt->command_object->set("capabilities", new SrsAmf0Number(239)); - pkt->command_object->set("audioCodecs", new SrsAmf0Number(3575)); - pkt->command_object->set("videoCodecs", new SrsAmf0Number(252)); - pkt->command_object->set("videoFunction", new SrsAmf0Number(1)); - pkt->command_object->set("pageUrl", new SrsAmf0String()); - pkt->command_object->set("objectEncoding", new SrsAmf0Number(0)); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - return ret; - } - } - - // Set Window Acknowledgement size(2500000) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); - - pkt->ackowledgement_window_size = 2500000; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - return ret; - } - } - - // expect connect _result - SrsCommonMessage* msg = NULL; - SrsConnectAppResPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect connect app response message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get connect app response message"); - - return ret; -} - -int SrsRtmpClient::create_stream(int& stream_id) -{ - int ret = ERROR_SUCCESS; - - // CreateStream - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket(); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - return ret; - } - } - - // CreateStream _result. - if (true) { - SrsCommonMessage* msg = NULL; - SrsCreateStreamResPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect create stream response message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get create stream response message"); - - stream_id = (int)pkt->stream_id; - } - - return ret; -} - -int SrsRtmpClient::play(std::string stream, int stream_id) -{ - int ret = ERROR_SUCCESS; - - // Play(stream) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsPlayPacket* pkt = new SrsPlayPacket(); - - pkt->stream_name = stream; - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send play stream failed. " - "stream=%s, stream_id=%d, ret=%d", - stream.c_str(), stream_id, ret); - return ret; - } - } - - // SetBufferLength(1000ms) - int buffer_length_ms = 1000; - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCSetBufferLength; - pkt->event_data = stream_id; - pkt->extra_data = buffer_length_ms; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send set buffer length failed. " - "stream=%s, stream_id=%d, bufferLength=%d, ret=%d", - stream.c_str(), stream_id, buffer_length_ms, ret); - return ret; - } - } - - return ret; -} - -int SrsRtmpClient::publish(std::string stream, int stream_id) -{ - int ret = ERROR_SUCCESS; - - // publish(stream) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsPublishPacket* pkt = new SrsPublishPacket(); - - pkt->stream_name = stream; - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send publish message failed. " - "stream=%s, stream_id=%d, ret=%d", - stream.c_str(), stream_id, ret); - return ret; - } - } - - return ret; -} - -SrsRtmp::SrsRtmp(st_netfd_t client_stfd) -{ - protocol = new SrsProtocol(client_stfd); - stfd = client_stfd; -} - -SrsRtmp::~SrsRtmp() -{ - srs_freep(protocol); -} - -SrsProtocol* SrsRtmp::get_protocol() -{ - return protocol; -} - -void SrsRtmp::set_recv_timeout(int64_t timeout_us) -{ - protocol->set_recv_timeout(timeout_us); -} - -int64_t SrsRtmp::get_recv_timeout() -{ - return protocol->get_recv_timeout(); -} - -void SrsRtmp::set_send_timeout(int64_t timeout_us) -{ - protocol->set_send_timeout(timeout_us); -} - -int64_t SrsRtmp::get_recv_bytes() -{ - return protocol->get_recv_bytes(); -} - -int64_t SrsRtmp::get_send_bytes() -{ - return protocol->get_send_bytes(); -} - -int SrsRtmp::get_recv_kbps() -{ - return protocol->get_recv_kbps(); -} - -int SrsRtmp::get_send_kbps() -{ - return protocol->get_send_kbps(); -} - -int SrsRtmp::recv_message(SrsCommonMessage** pmsg) -{ - return protocol->recv_message(pmsg); -} - -int SrsRtmp::send_message(ISrsMessage* msg) -{ - return protocol->send_message(msg); -} - -int SrsRtmp::handshake() -{ - int ret = ERROR_SUCCESS; - - SrsSocket skt(stfd); - - SrsComplexHandshake complex_hs; - SrsSimpleHandshake simple_hs; - if ((ret = simple_hs.handshake_with_client(skt, complex_hs)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -int SrsRtmp::connect_app(SrsRequest* req) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = NULL; - SrsConnectAppPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect connect app message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get connect app message"); - - SrsAmf0Any* prop = NULL; - - if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) { - ret = ERROR_RTMP_REQ_CONNECT; - srs_error("invalid request, must specifies the tcUrl. ret=%d", ret); - return ret; - } - req->tcUrl = srs_amf0_convert(prop)->value; - - if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) { - req->pageUrl = srs_amf0_convert(prop)->value; - } - - if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) { - req->swfUrl = srs_amf0_convert(prop)->value; - } - - if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) { - req->objectEncoding = srs_amf0_convert(prop)->value; - } - - srs_info("get connect app message params success."); - - return req->discovery_app(); -} - -int SrsRtmp::set_window_ack_size(int ack_size) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); - - pkt->ackowledgement_window_size = ack_size; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send ack size message failed. ret=%d", ret); - return ret; - } - srs_info("send ack size message success. ack_size=%d", ack_size); - - return ret; -} - -int SrsRtmp::set_peer_bandwidth(int bandwidth, int type) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket(); - - pkt->bandwidth = bandwidth; - pkt->type = type; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send set bandwidth message failed. ret=%d", ret); - return ret; - } - srs_info("send set bandwidth message " - "success. bandwidth=%d, type=%d", bandwidth, type); - - return ret; -} - -int SrsRtmp::response_connect_app(SrsRequest* req) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); - - pkt->props->set("fmsVer", new SrsAmf0String("FMS/"RTMP_SIG_FMS_VER)); - pkt->props->set("capabilities", new SrsAmf0Number(127)); - pkt->props->set("mode", new SrsAmf0Number(1)); - - pkt->info->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->info->set(StatusCode, new SrsAmf0String(StatusCodeConnectSuccess)); - pkt->info->set(StatusDescription, new SrsAmf0String("Connection succeeded")); - pkt->info->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); - SrsASrsAmf0EcmaArray* data = new SrsASrsAmf0EcmaArray(); - pkt->info->set("data", data); - - data->set("srs_version", new SrsAmf0String(RTMP_SIG_FMS_VER)); - data->set("srs_server", new SrsAmf0String(RTMP_SIG_SRS_NAME)); - data->set("srs_license", new SrsAmf0String(RTMP_SIG_SRS_LICENSE)); - data->set("srs_role", new SrsAmf0String(RTMP_SIG_SRS_ROLE)); - data->set("srs_url", new SrsAmf0String(RTMP_SIG_SRS_URL)); - data->set("srs_version", new SrsAmf0String(RTMP_SIG_SRS_VERSION)); - data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB)); - data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL)); - data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT)); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send connect app response message failed. ret=%d", ret); - return ret; - } - srs_info("send connect app response message success."); - - return ret; -} - -int SrsRtmp::on_bw_done() -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket(); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onBWDone message failed. ret=%d", ret); - return ret; - } - srs_info("send onBWDone message success."); - - return ret; -} - -int SrsRtmp::identify_client(int stream_id, SrsClientType& type, std::string& stream_name) -{ - type = SrsClientUnknown; - int ret = ERROR_SUCCESS; - - while (true) { - SrsCommonMessage* msg = NULL; - if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("recv identify client message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsCommonMessage, msg, false); - - if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { - srs_trace("identify ignore messages except " - "AMF0/AMF3 command message. type=%#x", msg->header.message_type); - continue; - } - - if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { - srs_error("identify decode message failed. ret=%d", ret); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - srs_info("identify client by create stream, play or flash publish."); - return identify_create_stream_client( - dynamic_cast(pkt), stream_id, type, stream_name); - } - if (dynamic_cast(pkt)) { - srs_info("identify client by releaseStream, fmle publish."); - return identify_fmle_publish_client( - dynamic_cast(pkt), type, stream_name); - } - - srs_trace("ignore AMF0/AMF3 command message."); - } - - return ret; -} - -int SrsRtmp::set_chunk_size(int chunk_size) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); - - pkt->chunk_size = chunk_size; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send set chunk size message failed. ret=%d", ret); - return ret; - } - srs_info("send set chunk size message success. chunk_size=%d", chunk_size); - - return ret; -} - -int SrsRtmp::start_play(int stream_id) -{ - int ret = ERROR_SUCCESS; - - // StreamBegin - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamBegin; - pkt->event_data = stream_id; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send PCUC(StreamBegin) message failed. ret=%d", ret); - return ret; - } - srs_info("send PCUC(StreamBegin) message success."); - } - - // onStatus(NetStream.Play.Reset) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamReset)); - pkt->data->set(StatusDescription, new SrsAmf0String("Playing and resetting stream.")); - pkt->data->set(StatusDetails, new SrsAmf0String("stream")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Play.Reset) message success."); - } - - // onStatus(NetStream.Play.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started playing stream.")); - pkt->data->set(StatusDetails, new SrsAmf0String("stream")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Play.Reset) message success."); - } - - // |RtmpSampleAccess(false, false) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket(); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret); - return ret; - } - srs_info("send |RtmpSampleAccess(false, false) message success."); - } - - // onStatus(NetStream.Data.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket(); - - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeDataStart)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Data.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Data.Start) message success."); - } - - srs_info("start play success."); - - return ret; -} - -int SrsRtmp::on_play_client_pause(int stream_id, bool is_pause) -{ - int ret = ERROR_SUCCESS; - - if (is_pause) { - // onStatus(NetStream.Pause.Notify) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamPause)); - pkt->data->set(StatusDescription, new SrsAmf0String("Paused stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Pause.Notify) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Pause.Notify) message success."); - } - // StreamEOF - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamEOF; - pkt->event_data = stream_id; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send PCUC(StreamEOF) message failed. ret=%d", ret); - return ret; - } - srs_info("send PCUC(StreamEOF) message success."); - } - } else { - // onStatus(NetStream.Unpause.Notify) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamUnpause)); - pkt->data->set(StatusDescription, new SrsAmf0String("Unpaused stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Unpause.Notify) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Unpause.Notify) message success."); - } - // StreanBegin - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamBegin; - pkt->event_data = stream_id; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret); - return ret; - } - srs_info("send PCUC(StreanBegin) message success."); - } - } - - return ret; -} - -int SrsRtmp::start_fmle_publish(int stream_id) -{ - int ret = ERROR_SUCCESS; - - // FCPublish - double fc_publish_tid = 0; - if (true) { - SrsCommonMessage* msg = NULL; - SrsFMLEStartPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("recv FCPublish message failed. ret=%d", ret); - return ret; - } - srs_info("recv FCPublish request message success."); - - SrsAutoFree(SrsCommonMessage, msg, false); - fc_publish_tid = pkt->transaction_id; - } - // FCPublish response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send FCPublish response message failed. ret=%d", ret); - return ret; - } - srs_info("send FCPublish response message success."); - } - - // createStream - double create_stream_tid = 0; - if (true) { - SrsCommonMessage* msg = NULL; - SrsCreateStreamPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("recv createStream message failed. ret=%d", ret); - return ret; - } - srs_info("recv createStream request message success."); - - SrsAutoFree(SrsCommonMessage, msg, false); - create_stream_tid = pkt->transaction_id; - } - // createStream response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send createStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send createStream response message success."); - } - - // publish - if (true) { - SrsCommonMessage* msg = NULL; - SrsPublishPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("recv publish message failed. ret=%d", ret); - return ret; - } - srs_info("recv publish request message success."); - - SrsAutoFree(SrsCommonMessage, msg, false); - } - // publish response onFCPublish(NetStream.Publish.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH; - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onFCPublish(NetStream.Publish.Start) message success."); - } - // publish response onStatus(NetStream.Publish.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Publish.Start) message success."); - } - - srs_info("FMLE publish success."); - - return ret; -} - -int SrsRtmp::fmle_unpublish(int stream_id, double unpublish_tid) -{ - int ret = ERROR_SUCCESS; - - // publish response onFCUnpublish(NetStream.unpublish.Success) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH; - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); - pkt->data->set(StatusDescription, new SrsAmf0String("Stop publishing stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onFCUnpublish(NetStream.unpublish.Success) message failed. ret=%d", ret); - return ret; - } - srs_info("send onFCUnpublish(NetStream.unpublish.Success) message success."); - } - // FCUnpublish response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(unpublish_tid); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send FCUnpublish response message failed. ret=%d", ret); - return ret; - } - srs_info("send FCUnpublish response message success."); - } - // publish response onStatus(NetStream.Unpublish.Success) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); - pkt->data->set(StatusDescription, new SrsAmf0String("Stream is now unpublished")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Unpublish.Success) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Unpublish.Success) message success."); - } - - srs_info("FMLE unpublish success."); - - return ret; -} - -int SrsRtmp::start_flash_publish(int stream_id) -{ - int ret = ERROR_SUCCESS; - - // publish response onStatus(NetStream.Publish.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Publish.Start) message success."); - } - - srs_info("flash publish success."); - - return ret; -} - -int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name) -{ - int ret = ERROR_SUCCESS; - - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send createStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send createStream response message success."); - } - - while (true) { - SrsCommonMessage* msg = NULL; - if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("recv identify client message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsCommonMessage, msg, false); - - if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { - srs_trace("identify ignore messages except " - "AMF0/AMF3 command message. type=%#x", msg->header.message_type); - continue; - } - - if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { - srs_error("identify decode message failed. ret=%d", ret); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - SrsPlayPacket* play = dynamic_cast(pkt); - type = SrsClientPlay; - stream_name = play->stream_name; - srs_trace("identity client type=play, stream_name=%s", stream_name.c_str()); - return ret; - } - if (dynamic_cast(pkt)) { - srs_info("identify client by publish, falsh publish."); - return identify_flash_publish_client( - dynamic_cast(pkt), type, stream_name); - } - - srs_trace("ignore AMF0/AMF3 command message."); - } - - return ret; -} - -int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name) -{ - int ret = ERROR_SUCCESS; - - type = SrsClientFMLEPublish; - stream_name = req->stream_name; - - // releaseStream response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send releaseStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send releaseStream response message success."); - } - - return ret; -} - -int SrsRtmp::identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name) -{ - int ret = ERROR_SUCCESS; - - type = SrsClientFlashPublish; - stream_name = req->stream_name; - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 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 + +#include +#include +#include +#include +#include +#include +#include + +/** +* the signature for packets to client. +*/ +#define RTMP_SIG_FMS_VER "3,5,3,888" +#define RTMP_SIG_AMF0_VER 0 +#define RTMP_SIG_CLIENT_ID "ASAICiss" + +/** +* onStatus consts. +*/ +#define StatusLevel "level" +#define StatusCode "code" +#define StatusDescription "description" +#define StatusDetails "details" +#define StatusClientId "clientid" +// status value +#define StatusLevelStatus "status" +// code value +#define StatusCodeConnectSuccess "NetConnection.Connect.Success" +#define StatusCodeStreamReset "NetStream.Play.Reset" +#define StatusCodeStreamStart "NetStream.Play.Start" +#define StatusCodeStreamPause "NetStream.Pause.Notify" +#define StatusCodeStreamUnpause "NetStream.Unpause.Notify" +#define StatusCodePublishStart "NetStream.Publish.Start" +#define StatusCodeDataStart "NetStream.Data.Start" +#define StatusCodeUnpublishSuccess "NetStream.Unpublish.Success" + +// FMLE +#define RTMP_AMF0_COMMAND_ON_FC_PUBLISH "onFCPublish" +#define RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH "onFCUnpublish" + +// default stream id for response the createStream request. +#define SRS_DEFAULT_SID 1 + +SrsRequest::SrsRequest() +{ + objectEncoding = RTMP_SIG_AMF0_VER; +} + +SrsRequest::~SrsRequest() +{ +} + +int SrsRequest::discovery_app() +{ + int ret = ERROR_SUCCESS; + + size_t pos = std::string::npos; + std::string url = tcUrl; + + if ((pos = url.find("://")) != std::string::npos) { + schema = url.substr(0, pos); + url = url.substr(schema.length() + 3); + srs_verbose("discovery schema=%s", schema.c_str()); + } + + if ((pos = url.find("/")) != std::string::npos) { + vhost = url.substr(0, pos); + url = url.substr(vhost.length() + 1); + srs_verbose("discovery vhost=%s", vhost.c_str()); + } + + port = "1935"; + if ((pos = vhost.find(":")) != std::string::npos) { + port = vhost.substr(pos + 1); + vhost = vhost.substr(0, pos); + srs_verbose("discovery vhost=%s, port=%s", vhost.c_str(), port.c_str()); + } + + app = url; + srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s", + schema.c_str(), vhost.c_str(), port.c_str(), app.c_str()); + + if (schema.empty() || vhost.empty() || port.empty() || app.empty()) { + ret = ERROR_RTMP_REQ_TCURL; + srs_error("discovery tcUrl failed. " + "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d", + tcUrl.c_str(), schema.c_str(), vhost.c_str(), port.c_str(), app.c_str(), ret); + return ret; + } + + strip(); + + return ret; +} + +std::string SrsRequest::get_stream_url() +{ + std::string url = ""; + + url += vhost; + url += "/"; + url += app; + url += "/"; + url += stream; + + return url; +} + +void SrsRequest::strip() +{ + trim(vhost, "/ \n\r\t"); + trim(app, "/ \n\r\t"); + trim(stream, "/ \n\r\t"); +} + +std::string& SrsRequest::trim(std::string& str, std::string chs) +{ + for (int i = 0; i < (int)chs.length(); i++) { + char ch = chs.at(i); + + for (std::string::iterator it = str.begin(); it != str.end();) { + if (ch == *it) { + it = str.erase(it); + } else { + ++it; + } + } + } + + return str; +} + +SrsResponse::SrsResponse() +{ + stream_id = SRS_DEFAULT_SID; +} + +SrsResponse::~SrsResponse() +{ +} + +SrsRtmpClient::SrsRtmpClient(st_netfd_t _stfd) +{ + stfd = _stfd; + protocol = new SrsProtocol(stfd); +} + +SrsRtmpClient::~SrsRtmpClient() +{ + srs_freep(protocol); +} + +void SrsRtmpClient::set_recv_timeout(int64_t timeout_us) +{ + protocol->set_recv_timeout(timeout_us); +} + +void SrsRtmpClient::set_send_timeout(int64_t timeout_us) +{ + protocol->set_send_timeout(timeout_us); +} + +int64_t SrsRtmpClient::get_recv_bytes() +{ + return protocol->get_recv_bytes(); +} + +int64_t SrsRtmpClient::get_send_bytes() +{ + return protocol->get_send_bytes(); +} + +int SrsRtmpClient::get_recv_kbps() +{ + return protocol->get_recv_kbps(); +} + +int SrsRtmpClient::get_send_kbps() +{ + return protocol->get_send_kbps(); +} + +int SrsRtmpClient::recv_message(SrsCommonMessage** pmsg) +{ + return protocol->recv_message(pmsg); +} + +int SrsRtmpClient::send_message(ISrsMessage* msg) +{ + return protocol->send_message(msg); +} + +int SrsRtmpClient::handshake() +{ + int ret = ERROR_SUCCESS; + + SrsSocket skt(stfd); + + SrsComplexHandshake complex_hs; + SrsSimpleHandshake simple_hs; + if ((ret = simple_hs.handshake_with_server(skt, complex_hs)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsRtmpClient::connect_app(std::string app, std::string tc_url) +{ + int ret = ERROR_SUCCESS; + + // Connect(vhost, app) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsConnectAppPacket* pkt = new SrsConnectAppPacket(); + msg->set_packet(pkt, 0); + + pkt->command_object = new SrsAmf0Object(); + pkt->command_object->set("app", new SrsAmf0String(app.c_str())); + pkt->command_object->set("swfUrl", new SrsAmf0String()); + pkt->command_object->set("tcUrl", new SrsAmf0String(tc_url.c_str())); + pkt->command_object->set("fpad", new SrsAmf0Boolean(false)); + pkt->command_object->set("capabilities", new SrsAmf0Number(239)); + pkt->command_object->set("audioCodecs", new SrsAmf0Number(3575)); + pkt->command_object->set("videoCodecs", new SrsAmf0Number(252)); + pkt->command_object->set("videoFunction", new SrsAmf0Number(1)); + pkt->command_object->set("pageUrl", new SrsAmf0String()); + pkt->command_object->set("objectEncoding", new SrsAmf0Number(0)); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + return ret; + } + } + + // Set Window Acknowledgement size(2500000) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); + + pkt->ackowledgement_window_size = 2500000; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + return ret; + } + } + + // expect connect _result + SrsCommonMessage* msg = NULL; + SrsConnectAppResPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect connect app response message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get connect app response message"); + + return ret; +} + +int SrsRtmpClient::create_stream(int& stream_id) +{ + int ret = ERROR_SUCCESS; + + // CreateStream + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket(); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + return ret; + } + } + + // CreateStream _result. + if (true) { + SrsCommonMessage* msg = NULL; + SrsCreateStreamResPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect create stream response message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get create stream response message"); + + stream_id = (int)pkt->stream_id; + } + + return ret; +} + +int SrsRtmpClient::play(std::string stream, int stream_id) +{ + int ret = ERROR_SUCCESS; + + // Play(stream) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsPlayPacket* pkt = new SrsPlayPacket(); + + pkt->stream_name = stream; + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send play stream failed. " + "stream=%s, stream_id=%d, ret=%d", + stream.c_str(), stream_id, ret); + return ret; + } + } + + // SetBufferLength(1000ms) + int buffer_length_ms = 1000; + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCSetBufferLength; + pkt->event_data = stream_id; + pkt->extra_data = buffer_length_ms; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set buffer length failed. " + "stream=%s, stream_id=%d, bufferLength=%d, ret=%d", + stream.c_str(), stream_id, buffer_length_ms, ret); + return ret; + } + } + + return ret; +} + +int SrsRtmpClient::publish(std::string stream, int stream_id) +{ + int ret = ERROR_SUCCESS; + + // publish(stream) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsPublishPacket* pkt = new SrsPublishPacket(); + + pkt->stream_name = stream; + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send publish message failed. " + "stream=%s, stream_id=%d, ret=%d", + stream.c_str(), stream_id, ret); + return ret; + } + } + + return ret; +} + +SrsRtmp::SrsRtmp(st_netfd_t client_stfd) +{ + protocol = new SrsProtocol(client_stfd); + stfd = client_stfd; +} + +SrsRtmp::~SrsRtmp() +{ + srs_freep(protocol); +} + +SrsProtocol* SrsRtmp::get_protocol() +{ + return protocol; +} + +void SrsRtmp::set_recv_timeout(int64_t timeout_us) +{ + protocol->set_recv_timeout(timeout_us); +} + +int64_t SrsRtmp::get_recv_timeout() +{ + return protocol->get_recv_timeout(); +} + +void SrsRtmp::set_send_timeout(int64_t timeout_us) +{ + protocol->set_send_timeout(timeout_us); +} + +int64_t SrsRtmp::get_recv_bytes() +{ + return protocol->get_recv_bytes(); +} + +int64_t SrsRtmp::get_send_bytes() +{ + return protocol->get_send_bytes(); +} + +int SrsRtmp::get_recv_kbps() +{ + return protocol->get_recv_kbps(); +} + +int SrsRtmp::get_send_kbps() +{ + return protocol->get_send_kbps(); +} + +int SrsRtmp::recv_message(SrsCommonMessage** pmsg) +{ + return protocol->recv_message(pmsg); +} + +int SrsRtmp::send_message(ISrsMessage* msg) +{ + return protocol->send_message(msg); +} + +int SrsRtmp::handshake() +{ + int ret = ERROR_SUCCESS; + + SrsSocket skt(stfd); + + SrsComplexHandshake complex_hs; + SrsSimpleHandshake simple_hs; + if ((ret = simple_hs.handshake_with_client(skt, complex_hs)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsRtmp::connect_app(SrsRequest* req) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = NULL; + SrsConnectAppPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect connect app message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get connect app message"); + + SrsAmf0Any* prop = NULL; + + if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) { + ret = ERROR_RTMP_REQ_CONNECT; + srs_error("invalid request, must specifies the tcUrl. ret=%d", ret); + return ret; + } + req->tcUrl = srs_amf0_convert(prop)->value; + + if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) { + req->pageUrl = srs_amf0_convert(prop)->value; + } + + if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) { + req->swfUrl = srs_amf0_convert(prop)->value; + } + + if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) { + req->objectEncoding = srs_amf0_convert(prop)->value; + } + + srs_info("get connect app message params success."); + + return req->discovery_app(); +} + +int SrsRtmp::set_window_ack_size(int ack_size) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); + + pkt->ackowledgement_window_size = ack_size; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send ack size message failed. ret=%d", ret); + return ret; + } + srs_info("send ack size message success. ack_size=%d", ack_size); + + return ret; +} + +int SrsRtmp::set_peer_bandwidth(int bandwidth, int type) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket(); + + pkt->bandwidth = bandwidth; + pkt->type = type; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set bandwidth message failed. ret=%d", ret); + return ret; + } + srs_info("send set bandwidth message " + "success. bandwidth=%d, type=%d", bandwidth, type); + + return ret; +} + +int SrsRtmp::response_connect_app(SrsRequest* req) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); + + pkt->props->set("fmsVer", new SrsAmf0String("FMS/"RTMP_SIG_FMS_VER)); + pkt->props->set("capabilities", new SrsAmf0Number(127)); + pkt->props->set("mode", new SrsAmf0Number(1)); + + pkt->info->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->info->set(StatusCode, new SrsAmf0String(StatusCodeConnectSuccess)); + pkt->info->set(StatusDescription, new SrsAmf0String("Connection succeeded")); + pkt->info->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); + SrsASrsAmf0EcmaArray* data = new SrsASrsAmf0EcmaArray(); + pkt->info->set("data", data); + + data->set("srs_version", new SrsAmf0String(RTMP_SIG_FMS_VER)); + data->set("srs_server", new SrsAmf0String(RTMP_SIG_SRS_NAME)); + data->set("srs_license", new SrsAmf0String(RTMP_SIG_SRS_LICENSE)); + data->set("srs_role", new SrsAmf0String(RTMP_SIG_SRS_ROLE)); + data->set("srs_url", new SrsAmf0String(RTMP_SIG_SRS_URL)); + data->set("srs_version", new SrsAmf0String(RTMP_SIG_SRS_VERSION)); + data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB)); + data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL)); + data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT)); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send connect app response message failed. ret=%d", ret); + return ret; + } + srs_info("send connect app response message success."); + + return ret; +} + +int SrsRtmp::on_bw_done() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket(); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onBWDone message failed. ret=%d", ret); + return ret; + } + srs_info("send onBWDone message success."); + + return ret; +} + +int SrsRtmp::identify_client(int stream_id, SrsClientType& type, std::string& stream_name) +{ + type = SrsClientUnknown; + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { + srs_error("recv identify client message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsCommonMessage, msg, false); + + if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { + srs_trace("identify ignore messages except " + "AMF0/AMF3 command message. type=%#x", msg->header.message_type); + continue; + } + + if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { + srs_error("identify decode message failed. ret=%d", ret); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + srs_info("identify client by create stream, play or flash publish."); + return identify_create_stream_client( + dynamic_cast(pkt), stream_id, type, stream_name); + } + if (dynamic_cast(pkt)) { + srs_info("identify client by releaseStream, fmle publish."); + return identify_fmle_publish_client( + dynamic_cast(pkt), type, stream_name); + } + + srs_trace("ignore AMF0/AMF3 command message."); + } + + return ret; +} + +int SrsRtmp::set_chunk_size(int chunk_size) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); + + pkt->chunk_size = chunk_size; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set chunk size message failed. ret=%d", ret); + return ret; + } + srs_info("send set chunk size message success. chunk_size=%d", chunk_size); + + return ret; +} + +int SrsRtmp::start_play(int stream_id) +{ + int ret = ERROR_SUCCESS; + + // StreamBegin + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamBegin; + pkt->event_data = stream_id; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreamBegin) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreamBegin) message success."); + } + + // onStatus(NetStream.Play.Reset) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamReset)); + pkt->data->set(StatusDescription, new SrsAmf0String("Playing and resetting stream.")); + pkt->data->set(StatusDetails, new SrsAmf0String("stream")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Play.Reset) message success."); + } + + // onStatus(NetStream.Play.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started playing stream.")); + pkt->data->set(StatusDetails, new SrsAmf0String("stream")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Play.Reset) message success."); + } + + // |RtmpSampleAccess(false, false) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket(); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret); + return ret; + } + srs_info("send |RtmpSampleAccess(false, false) message success."); + } + + // onStatus(NetStream.Data.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket(); + + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeDataStart)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Data.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Data.Start) message success."); + } + + srs_info("start play success."); + + return ret; +} + +int SrsRtmp::on_play_client_pause(int stream_id, bool is_pause) +{ + int ret = ERROR_SUCCESS; + + if (is_pause) { + // onStatus(NetStream.Pause.Notify) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamPause)); + pkt->data->set(StatusDescription, new SrsAmf0String("Paused stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Pause.Notify) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Pause.Notify) message success."); + } + // StreamEOF + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamEOF; + pkt->event_data = stream_id; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreamEOF) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreamEOF) message success."); + } + } else { + // onStatus(NetStream.Unpause.Notify) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamUnpause)); + pkt->data->set(StatusDescription, new SrsAmf0String("Unpaused stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Unpause.Notify) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Unpause.Notify) message success."); + } + // StreanBegin + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamBegin; + pkt->event_data = stream_id; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreanBegin) message success."); + } + } + + return ret; +} + +int SrsRtmp::start_fmle_publish(int stream_id) +{ + int ret = ERROR_SUCCESS; + + // FCPublish + double fc_publish_tid = 0; + if (true) { + SrsCommonMessage* msg = NULL; + SrsFMLEStartPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("recv FCPublish message failed. ret=%d", ret); + return ret; + } + srs_info("recv FCPublish request message success."); + + SrsAutoFree(SrsCommonMessage, msg, false); + fc_publish_tid = pkt->transaction_id; + } + // FCPublish response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send FCPublish response message failed. ret=%d", ret); + return ret; + } + srs_info("send FCPublish response message success."); + } + + // createStream + double create_stream_tid = 0; + if (true) { + SrsCommonMessage* msg = NULL; + SrsCreateStreamPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("recv createStream message failed. ret=%d", ret); + return ret; + } + srs_info("recv createStream request message success."); + + SrsAutoFree(SrsCommonMessage, msg, false); + create_stream_tid = pkt->transaction_id; + } + // createStream response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send createStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send createStream response message success."); + } + + // publish + if (true) { + SrsCommonMessage* msg = NULL; + SrsPublishPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("recv publish message failed. ret=%d", ret); + return ret; + } + srs_info("recv publish request message success."); + + SrsAutoFree(SrsCommonMessage, msg, false); + } + // publish response onFCPublish(NetStream.Publish.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH; + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onFCPublish(NetStream.Publish.Start) message success."); + } + // publish response onStatus(NetStream.Publish.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Publish.Start) message success."); + } + + srs_info("FMLE publish success."); + + return ret; +} + +int SrsRtmp::fmle_unpublish(int stream_id, double unpublish_tid) +{ + int ret = ERROR_SUCCESS; + + // publish response onFCUnpublish(NetStream.unpublish.Success) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH; + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); + pkt->data->set(StatusDescription, new SrsAmf0String("Stop publishing stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onFCUnpublish(NetStream.unpublish.Success) message failed. ret=%d", ret); + return ret; + } + srs_info("send onFCUnpublish(NetStream.unpublish.Success) message success."); + } + // FCUnpublish response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(unpublish_tid); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send FCUnpublish response message failed. ret=%d", ret); + return ret; + } + srs_info("send FCUnpublish response message success."); + } + // publish response onStatus(NetStream.Unpublish.Success) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); + pkt->data->set(StatusDescription, new SrsAmf0String("Stream is now unpublished")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Unpublish.Success) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Unpublish.Success) message success."); + } + + srs_info("FMLE unpublish success."); + + return ret; +} + +int SrsRtmp::start_flash_publish(int stream_id) +{ + int ret = ERROR_SUCCESS; + + // publish response onStatus(NetStream.Publish.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Publish.Start) message success."); + } + + srs_info("flash publish success."); + + return ret; +} + +int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name) +{ + int ret = ERROR_SUCCESS; + + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send createStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send createStream response message success."); + } + + while (true) { + SrsCommonMessage* msg = NULL; + if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { + srs_error("recv identify client message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsCommonMessage, msg, false); + + if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { + srs_trace("identify ignore messages except " + "AMF0/AMF3 command message. type=%#x", msg->header.message_type); + continue; + } + + if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { + srs_error("identify decode message failed. ret=%d", ret); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + SrsPlayPacket* play = dynamic_cast(pkt); + type = SrsClientPlay; + stream_name = play->stream_name; + srs_trace("identity client type=play, stream_name=%s", stream_name.c_str()); + return ret; + } + if (dynamic_cast(pkt)) { + srs_info("identify client by publish, falsh publish."); + return identify_flash_publish_client( + dynamic_cast(pkt), type, stream_name); + } + + srs_trace("ignore AMF0/AMF3 command message."); + } + + return ret; +} + +int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name) +{ + int ret = ERROR_SUCCESS; + + type = SrsClientFMLEPublish; + stream_name = req->stream_name; + + // releaseStream response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send releaseStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send releaseStream response message success."); + } + + return ret; +} + +int SrsRtmp::identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name) +{ + int ret = ERROR_SUCCESS; + + type = SrsClientFlashPublish; + stream_name = req->stream_name; + + return ret; +} + diff --git a/trunk/src/core/srs_core_rtmp.hpp b/trunk/src/core/srs_core_rtmp.hpp old mode 100644 new mode 100755 index b2ad194fb..01562a11d --- a/trunk/src/core/srs_core_rtmp.hpp +++ b/trunk/src/core/srs_core_rtmp.hpp @@ -1,209 +1,215 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 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_CORE_RTMP_HPP -#define SRS_CORE_RTMP_HPP - -/* -#include -*/ - -#include - -#include - -#include - -class SrsProtocol; -class ISrsMessage; -class SrsCommonMessage; -class SrsCreateStreamPacket; -class SrsFMLEStartPacket; -class SrsPublishPacket; -class SrsSharedPtrMessage; -class SrsOnMetaDataPacket; - -/** -* the original request from client. -*/ -struct SrsRequest -{ - std::string tcUrl; - std::string pageUrl; - std::string swfUrl; - double objectEncoding; - - std::string schema; - std::string vhost; - std::string port; - std::string app; - std::string stream; - - SrsRequest(); - virtual ~SrsRequest(); - - /** - * disconvery vhost/app from tcUrl. - */ - virtual int discovery_app(); - virtual std::string get_stream_url(); - virtual void strip(); -private: - std::string& trim(std::string& str, std::string chs); -}; - -/** -* the response to client. -*/ -struct SrsResponse -{ - int stream_id; - - SrsResponse(); - virtual ~SrsResponse(); -}; - -/** -* the rtmp client type. -*/ -enum SrsClientType -{ - SrsClientUnknown, - SrsClientPlay, - SrsClientFMLEPublish, - SrsClientFlashPublish, -}; - -/** -* implements the client role protocol. -*/ -class SrsRtmpClient -{ -private: - SrsProtocol* protocol; - st_netfd_t stfd; -public: - SrsRtmpClient(st_netfd_t _stfd); - virtual ~SrsRtmpClient(); -public: - virtual void set_recv_timeout(int64_t timeout_us); - virtual void set_send_timeout(int64_t timeout_us); -public: - virtual int handshake(); - virtual int connect_app(std::string app, std::string tc_url); - virtual int create_stream(int& stream_id); - virtual int play(std::string stream, int stream_id); - virtual int publish(std::string stream, int stream_id); -}; - -/** -* the rtmp provices rtmp-command-protocol services, -* a high level protocol, media stream oriented services, -* such as connect to vhost/app, play stream, get audio/video data. -*/ -class SrsRtmp -{ -private: - SrsProtocol* protocol; - st_netfd_t stfd; -public: - SrsRtmp(st_netfd_t client_stfd); - virtual ~SrsRtmp(); -public: - virtual SrsProtocol* get_protocol(); - virtual void set_recv_timeout(int64_t timeout_us); - virtual int64_t get_recv_timeout(); - virtual void set_send_timeout(int64_t timeout_us); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - virtual int get_recv_kbps(); - virtual int get_send_kbps(); - virtual int recv_message(SrsCommonMessage** pmsg); - virtual int send_message(ISrsMessage* msg); -public: - virtual int handshake(); - virtual int connect_app(SrsRequest* req); - virtual int set_window_ack_size(int ack_size); - /** - * @type: The sender can mark this message hard (0), soft (1), or dynamic (2) - * using the Limit type field. - */ - virtual int set_peer_bandwidth(int bandwidth, int type); - virtual int response_connect_app(SrsRequest* req); - virtual int on_bw_done(); - /** - * recv some message to identify the client. - * @stream_id, client will createStream to play or publish by flash, - * the stream_id used to response the createStream request. - * @type, output the client type. - */ - virtual int identify_client(int stream_id, SrsClientType& type, std::string& stream_name); - /** - * set the chunk size when client type identified. - */ - virtual int set_chunk_size(int chunk_size); - /** - * when client type is play, response with packets: - * StreamBegin, - * onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start)., - * |RtmpSampleAccess(false, false), - * onStatus(NetStream.Data.Start). - */ - virtual int start_play(int stream_id); - /** - * when client(type is play) send pause message, - * if is_pause, response the following packets: - * onStatus(NetStream.Pause.Notify) - * StreamEOF - * if not is_pause, response the following packets: - * onStatus(NetStream.Unpause.Notify) - * StreamBegin - */ - virtual int on_play_client_pause(int stream_id, bool is_pause); - /** - * when client type is publish, response with packets: - * releaseStream response - * FCPublish - * FCPublish response - * createStream response - * onFCPublish(NetStream.Publish.Start) - * onStatus(NetStream.Publish.Start) - */ - virtual int start_fmle_publish(int stream_id); - /** - * process the FMLE unpublish event. - * @unpublish_tid the unpublish request transaction id. - */ - virtual int fmle_unpublish(int stream_id, double unpublish_tid); - /** - * when client type is publish, response with packets: - * 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); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 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_CORE_RTMP_HPP +#define SRS_CORE_RTMP_HPP + +/* +#include +*/ + +#include + +#include + +#include + +class SrsProtocol; +class ISrsMessage; +class SrsCommonMessage; +class SrsCreateStreamPacket; +class SrsFMLEStartPacket; +class SrsPublishPacket; +class SrsSharedPtrMessage; +class SrsOnMetaDataPacket; + +/** +* the original request from client. +*/ +struct SrsRequest +{ + std::string tcUrl; + std::string pageUrl; + std::string swfUrl; + double objectEncoding; + + std::string schema; + std::string vhost; + std::string port; + std::string app; + std::string stream; + + SrsRequest(); + virtual ~SrsRequest(); + + /** + * disconvery vhost/app from tcUrl. + */ + virtual int discovery_app(); + virtual std::string get_stream_url(); + virtual void strip(); +private: + std::string& trim(std::string& str, std::string chs); +}; + +/** +* the response to client. +*/ +struct SrsResponse +{ + int stream_id; + + SrsResponse(); + virtual ~SrsResponse(); +}; + +/** +* the rtmp client type. +*/ +enum SrsClientType +{ + SrsClientUnknown, + SrsClientPlay, + SrsClientFMLEPublish, + SrsClientFlashPublish, +}; + +/** +* implements the client role protocol. +*/ +class SrsRtmpClient +{ +private: + SrsProtocol* protocol; + st_netfd_t stfd; +public: + SrsRtmpClient(st_netfd_t _stfd); + virtual ~SrsRtmpClient(); +public: + virtual void set_recv_timeout(int64_t timeout_us); + virtual void set_send_timeout(int64_t timeout_us); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int get_recv_kbps(); + virtual int get_send_kbps(); + virtual int recv_message(SrsCommonMessage** pmsg); + virtual int send_message(ISrsMessage* msg); +public: + virtual int handshake(); + virtual int connect_app(std::string app, std::string tc_url); + virtual int create_stream(int& stream_id); + virtual int play(std::string stream, int stream_id); + virtual int publish(std::string stream, int stream_id); +}; + +/** +* the rtmp provices rtmp-command-protocol services, +* a high level protocol, media stream oriented services, +* such as connect to vhost/app, play stream, get audio/video data. +*/ +class SrsRtmp +{ +private: + SrsProtocol* protocol; + st_netfd_t stfd; +public: + SrsRtmp(st_netfd_t client_stfd); + virtual ~SrsRtmp(); +public: + virtual SrsProtocol* get_protocol(); + virtual void set_recv_timeout(int64_t timeout_us); + virtual int64_t get_recv_timeout(); + virtual void set_send_timeout(int64_t timeout_us); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int get_recv_kbps(); + virtual int get_send_kbps(); + virtual int recv_message(SrsCommonMessage** pmsg); + virtual int send_message(ISrsMessage* msg); +public: + virtual int handshake(); + virtual int connect_app(SrsRequest* req); + virtual int set_window_ack_size(int ack_size); + /** + * @type: The sender can mark this message hard (0), soft (1), or dynamic (2) + * using the Limit type field. + */ + virtual int set_peer_bandwidth(int bandwidth, int type); + virtual int response_connect_app(SrsRequest* req); + virtual int on_bw_done(); + /** + * recv some message to identify the client. + * @stream_id, client will createStream to play or publish by flash, + * the stream_id used to response the createStream request. + * @type, output the client type. + */ + virtual int identify_client(int stream_id, SrsClientType& type, std::string& stream_name); + /** + * set the chunk size when client type identified. + */ + virtual int set_chunk_size(int chunk_size); + /** + * when client type is play, response with packets: + * StreamBegin, + * onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start)., + * |RtmpSampleAccess(false, false), + * onStatus(NetStream.Data.Start). + */ + virtual int start_play(int stream_id); + /** + * when client(type is play) send pause message, + * if is_pause, response the following packets: + * onStatus(NetStream.Pause.Notify) + * StreamEOF + * if not is_pause, response the following packets: + * onStatus(NetStream.Unpause.Notify) + * StreamBegin + */ + virtual int on_play_client_pause(int stream_id, bool is_pause); + /** + * when client type is publish, response with packets: + * releaseStream response + * FCPublish + * FCPublish response + * createStream response + * onFCPublish(NetStream.Publish.Start) + * onStatus(NetStream.Publish.Start) + */ + virtual int start_fmle_publish(int stream_id); + /** + * process the FMLE unpublish event. + * @unpublish_tid the unpublish request transaction id. + */ + virtual int fmle_unpublish(int stream_id, double unpublish_tid); + /** + * when client type is publish, response with packets: + * 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 \ No newline at end of file