From 765b0a9c011ca115a53fd2ab025daf1ec0a46003 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 11 Jan 2014 19:49:28 +0800 Subject: [PATCH] merge from wenjie, fix the pause bug of jw/flower which actually send closeStream to pause. --- trunk/src/core/srs_core_client.cpp | 1455 +++++++-------- trunk/src/core/srs_core_client.hpp | 178 +- trunk/src/core/srs_core_error.cpp | 53 +- trunk/src/core/srs_core_error.hpp | 308 ++-- trunk/src/core/srs_core_protocol.cpp | 40 + trunk/src/core/srs_core_protocol.hpp | 2480 +++++++++++++------------- trunk/src/core/srs_core_rtmp.cpp | 2446 ++++++++++++------------- trunk/src/core/srs_core_rtmp.hpp | 463 ++--- 8 files changed, 3786 insertions(+), 3637 deletions(-) mode change 100644 => 100755 trunk/src/core/srs_core_client.cpp mode change 100644 => 100755 trunk/src/core/srs_core_client.hpp mode change 100644 => 100755 trunk/src/core/srs_core_error.cpp mode change 100644 => 100755 trunk/src/core/srs_core_error.hpp mode change 100644 => 100755 trunk/src/core/srs_core_protocol.cpp mode change 100644 => 100755 trunk/src/core/srs_core_protocol.hpp mode change 100644 => 100755 trunk/src/core/srs_core_rtmp.cpp mode change 100644 => 100755 trunk/src/core/srs_core_rtmp.hpp 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 3d5dc6c4d..67436ef3b --- a/trunk/src/core/srs_core_client.cpp +++ b/trunk/src/core/srs_core_client.cpp @@ -1,703 +1,752 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 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 - -using namespace std; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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(); -#ifdef SRS_HTTP - http_hooks = new SrsHttpHooks(); -#endif - bandwidth = new SrsBandwidth(); - - config->subscribe(this); -} - -SrsClient::~SrsClient() -{ - config->unsubscribe(this); - - srs_freepa(ip); - srs_freep(req); - srs_freep(res); - srs_freep(rtmp); - srs_freep(refer); -#ifdef SRS_HTTP - srs_freep(http_hooks); -#endif - srs_freep(bandwidth); -} - -// 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()); - - ret = service_cycle(); - on_close(); - - return ret; -} - -int SrsClient::on_reload_vhost_removed(string vhost) -{ - int ret = ERROR_SUCCESS; - - if (req->vhost != vhost) { - return ret; - } - - // if the vhost connected is removed, disconnect the client. - srs_trace("vhost %s removed/disabled, close client url=%s", - vhost.c_str(), req->get_stream_url().c_str()); - - srs_close_stfd(stfd); - - return ret; -} - -int SrsClient::service_cycle() -{ - int ret = ERROR_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"); - - // do bandwidth test if connect to the vhost which is for bandwidth check. - if (config->get_bw_check_enabled(req->vhost)) { - return bandwidth->bandwidth_test(req, stfd, rtmp); - } - - 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 = config->get_chunk_size(req->vhost); - 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); - 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_US); - return ret; - } - - bool enabled_cache = config->get_gop_cache(req->vhost); - srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache); - source->set_cache(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; - } - if ((ret = on_play()) != ERROR_SUCCESS) { - srs_error("http hook on_play failed. ret=%d", ret); - return ret; - } - srs_info("start to play stream %s success", req->stream.c_str()); - ret = playing(source); - on_stop(); - return ret; - } - 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; - } - if ((ret = on_publish()) != ERROR_SUCCESS) { - srs_error("http hook on_publish 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(); - 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; - } - if ((ret = on_publish()) != ERROR_SUCCESS) { - srs_error("http hook on_publish 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(); - 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; - } - - if (!config->get_vhost_enabled(req->vhost)) { - 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(); - } - - 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 = on_connect()) != ERROR_SUCCESS) { - return ret; - } - - 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_US); - - SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER); - - while (true) { - pithy_print.elapse(SRS_PULSE_TIMEOUT_US / 1000); - - // 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("-> time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", - 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)) != 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("<- time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", - 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; -} - -int SrsClient::on_connect() -{ - int ret = ERROR_SUCCESS; - -#ifdef SRS_HTTP - // HTTP: on_connect - SrsConfDirective* on_connect = config->get_vhost_on_connect(req->vhost); - if (!on_connect) { - srs_info("ignore the empty http callback: on_connect"); - return ret; - } - - for (int i = 0; i < (int)on_connect->args.size(); i++) { - std::string url = on_connect->args.at(i); - if ((ret = http_hooks->on_connect(url, connection_id, ip, req)) != ERROR_SUCCESS) { - srs_error("hook client on_connect failed. url=%s, ret=%d", url.c_str(), ret); - return ret; - } - } -#endif - - return ret; -} - -void SrsClient::on_close() -{ -#ifdef SRS_HTTP - // whatever the ret code, notify the api hooks. - // HTTP: on_close - SrsConfDirective* on_close = config->get_vhost_on_close(req->vhost); - if (!on_close) { - srs_info("ignore the empty http callback: on_close"); - return; - } - - for (int i = 0; i < (int)on_close->args.size(); i++) { - std::string url = on_close->args.at(i); - http_hooks->on_close(url, connection_id, ip, req); - } -#endif -} - -int SrsClient::on_publish() -{ - int ret = ERROR_SUCCESS; - -#ifdef SRS_HTTP - // HTTP: on_publish - SrsConfDirective* on_publish = config->get_vhost_on_publish(req->vhost); - if (!on_publish) { - srs_info("ignore the empty http callback: on_publish"); - return ret; - } - - for (int i = 0; i < (int)on_publish->args.size(); i++) { - std::string url = on_publish->args.at(i); - if ((ret = http_hooks->on_publish(url, connection_id, ip, req)) != ERROR_SUCCESS) { - srs_error("hook client on_publish failed. url=%s, ret=%d", url.c_str(), ret); - return ret; - } - } -#endif - - return ret; -} - -void SrsClient::on_unpublish() -{ -#ifdef SRS_HTTP - // whatever the ret code, notify the api hooks. - // HTTP: on_unpublish - SrsConfDirective* on_unpublish = config->get_vhost_on_unpublish(req->vhost); - if (!on_unpublish) { - srs_info("ignore the empty http callback: on_unpublish"); - return; - } - - for (int i = 0; i < (int)on_unpublish->args.size(); i++) { - std::string url = on_unpublish->args.at(i); - http_hooks->on_unpublish(url, connection_id, ip, req); - } -#endif -} - -int SrsClient::on_play() -{ - int ret = ERROR_SUCCESS; - -#ifdef SRS_HTTP - // HTTP: on_play - SrsConfDirective* on_play = config->get_vhost_on_play(req->vhost); - if (!on_play) { - srs_info("ignore the empty http callback: on_play"); - return ret; - } - - for (int i = 0; i < (int)on_play->args.size(); i++) { - std::string url = on_play->args.at(i); - if ((ret = http_hooks->on_play(url, connection_id, ip, req)) != ERROR_SUCCESS) { - srs_error("hook client on_play failed. url=%s, ret=%d", url.c_str(), ret); - return ret; - } - } -#endif - - return ret; -} - -void SrsClient::on_stop() -{ -#ifdef SRS_HTTP - // whatever the ret code, notify the api hooks. - // HTTP: on_stop - SrsConfDirective* on_stop = config->get_vhost_on_stop(req->vhost); - if (!on_stop) { - srs_info("ignore the empty http callback: on_stop"); - return; - } - - for (int i = 0; i < (int)on_stop->args.size(); i++) { - std::string url = on_stop->args.at(i); - http_hooks->on_stop(url, connection_id, ip, req); - } -#endif -} - +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 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 + +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(); +#ifdef SRS_HTTP + http_hooks = new SrsHttpHooks(); +#endif + bandwidth = new SrsBandwidth(); + + config->subscribe(this); +} + +SrsClient::~SrsClient() +{ + config->unsubscribe(this); + + srs_freepa(ip); + srs_freep(req); + srs_freep(res); + srs_freep(rtmp); + srs_freep(refer); +#ifdef SRS_HTTP + srs_freep(http_hooks); +#endif + srs_freep(bandwidth); +} + +// 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()); + + ret = service_cycle(); + on_close(); + + return ret; +} + +int SrsClient::on_reload_vhost_removed(string vhost) +{ + int ret = ERROR_SUCCESS; + + if (req->vhost != vhost) { + return ret; + } + + // if the vhost connected is removed, disconnect the client. + srs_trace("vhost %s removed/disabled, close client url=%s", + vhost.c_str(), req->get_stream_url().c_str()); + + srs_close_stfd(stfd); + + return ret; +} + +int SrsClient::service_cycle() +{ + int ret = ERROR_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"); + + // do bandwidth test if connect to the vhost which is for bandwidth check. + if (config->get_bw_check_enabled(req->vhost)) { + return bandwidth->bandwidth_test(req, stfd, rtmp); + } + + 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"); + + while (true) { + ret = stream_service_cycle(); + + // stream service must terminated with error, never success. + srs_assert(ret != ERROR_SUCCESS); + + // when not system control error, fatal error, return. + if (!srs_is_system_control_error(ret)) { + srs_error("stream service cycle failed. ret=%d", ret); + return ret; + } + + // for "some" system control error, + // logical accept and retry stream service. + if (ret == ERROR_CONTROL_RTMP_CLOSE) { + // set timeout to a larger value, for user paused. + rtmp->set_recv_timeout(SRS_PAUSED_SEND_TIMEOUT_US); + rtmp->set_send_timeout(SRS_PAUSED_SEND_TIMEOUT_US); + + srs_trace("control message(close) accept, retry stream service."); + continue; + } + + // for other system control message, fatal error. + srs_error("control message(%d) reject as error. ret=%d", ret, ret); + return ret; + } + + return ret; +} + +int SrsClient::stream_service_cycle() +{ + int ret = ERROR_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()); + + // client is identified, set the timeout to service timeout. + rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_US); + rtmp->set_send_timeout(SRS_SEND_TIMEOUT_US); + + // set timeout to larger. + int chunk_size = config->get_chunk_size(req->vhost); + 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); + 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_US); + return ret; + } + + bool enabled_cache = config->get_gop_cache(req->vhost); + srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache); + source->set_cache(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; + } + if ((ret = on_play()) != ERROR_SUCCESS) { + srs_error("http hook on_play failed. ret=%d", ret); + return ret; + } + srs_info("start to play stream %s success", req->stream.c_str()); + ret = playing(source); + on_stop(); + return ret; + } + 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; + } + if ((ret = on_publish()) != ERROR_SUCCESS) { + srs_error("http hook on_publish 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(); + 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; + } + if ((ret = on_publish()) != ERROR_SUCCESS) { + srs_error("http hook on_publish 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(); + 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; + } + + if (!config->get_vhost_enabled(req->vhost)) { + 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(); + } + + 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 = on_connect()) != ERROR_SUCCESS) { + return ret; + } + + 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_US); + + SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER); + + while (true) { + pithy_print.elapse(SRS_PULSE_TIMEOUT_US / 1000); + + // 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) { + if (!srs_is_system_control_error(ret)) { + 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("-> time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", + 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)) != 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("<- time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", + 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."); + + SrsCloseStreamPacket* close = dynamic_cast(msg->get_packet()); + if (close) { + ret = ERROR_CONTROL_RTMP_CLOSE; + srs_trace("system control message: rtmp close stream. ret=%d", ret); + return ret; + } + + 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; +} + +int SrsClient::on_connect() +{ + int ret = ERROR_SUCCESS; + +#ifdef SRS_HTTP + // HTTP: on_connect + SrsConfDirective* on_connect = config->get_vhost_on_connect(req->vhost); + if (!on_connect) { + srs_info("ignore the empty http callback: on_connect"); + return ret; + } + + for (int i = 0; i < (int)on_connect->args.size(); i++) { + std::string url = on_connect->args.at(i); + if ((ret = http_hooks->on_connect(url, connection_id, ip, req)) != ERROR_SUCCESS) { + srs_error("hook client on_connect failed. url=%s, ret=%d", url.c_str(), ret); + return ret; + } + } +#endif + + return ret; +} + +void SrsClient::on_close() +{ +#ifdef SRS_HTTP + // whatever the ret code, notify the api hooks. + // HTTP: on_close + SrsConfDirective* on_close = config->get_vhost_on_close(req->vhost); + if (!on_close) { + srs_info("ignore the empty http callback: on_close"); + return; + } + + for (int i = 0; i < (int)on_close->args.size(); i++) { + std::string url = on_close->args.at(i); + http_hooks->on_close(url, connection_id, ip, req); + } +#endif +} + +int SrsClient::on_publish() +{ + int ret = ERROR_SUCCESS; + +#ifdef SRS_HTTP + // HTTP: on_publish + SrsConfDirective* on_publish = config->get_vhost_on_publish(req->vhost); + if (!on_publish) { + srs_info("ignore the empty http callback: on_publish"); + return ret; + } + + for (int i = 0; i < (int)on_publish->args.size(); i++) { + std::string url = on_publish->args.at(i); + if ((ret = http_hooks->on_publish(url, connection_id, ip, req)) != ERROR_SUCCESS) { + srs_error("hook client on_publish failed. url=%s, ret=%d", url.c_str(), ret); + return ret; + } + } +#endif + + return ret; +} + +void SrsClient::on_unpublish() +{ +#ifdef SRS_HTTP + // whatever the ret code, notify the api hooks. + // HTTP: on_unpublish + SrsConfDirective* on_unpublish = config->get_vhost_on_unpublish(req->vhost); + if (!on_unpublish) { + srs_info("ignore the empty http callback: on_unpublish"); + return; + } + + for (int i = 0; i < (int)on_unpublish->args.size(); i++) { + std::string url = on_unpublish->args.at(i); + http_hooks->on_unpublish(url, connection_id, ip, req); + } +#endif +} + +int SrsClient::on_play() +{ + int ret = ERROR_SUCCESS; + +#ifdef SRS_HTTP + // HTTP: on_play + SrsConfDirective* on_play = config->get_vhost_on_play(req->vhost); + if (!on_play) { + srs_info("ignore the empty http callback: on_play"); + return ret; + } + + for (int i = 0; i < (int)on_play->args.size(); i++) { + std::string url = on_play->args.at(i); + if ((ret = http_hooks->on_play(url, connection_id, ip, req)) != ERROR_SUCCESS) { + srs_error("hook client on_play failed. url=%s, ret=%d", url.c_str(), ret); + return ret; + } + } +#endif + + return ret; +} + +void SrsClient::on_stop() +{ +#ifdef SRS_HTTP + // whatever the ret code, notify the api hooks. + // HTTP: on_stop + SrsConfDirective* on_stop = config->get_vhost_on_stop(req->vhost); + if (!on_stop) { + srs_info("ignore the empty http callback: on_stop"); + return; + } + + for (int i = 0; i < (int)on_stop->args.size(); i++) { + std::string url = on_stop->args.at(i); + http_hooks->on_stop(url, connection_id, ip, req); + } +#endif +} + diff --git a/trunk/src/core/srs_core_client.hpp b/trunk/src/core/srs_core_client.hpp old mode 100644 new mode 100755 index 3d93f4123..ddc23eba9 --- a/trunk/src/core/srs_core_client.hpp +++ b/trunk/src/core/srs_core_client.hpp @@ -1,89 +1,91 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 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_CLIENT_HPP -#define SRS_CORE_CLIENT_HPP - -/* -#include -*/ - -#include - -#include -#include - -class SrsRtmp; -class SrsRequest; -class SrsResponse; -class SrsSource; -class SrsRefer; -class SrsConsumer; -class SrsCommonMessage; -#ifdef SRS_HTTP -class SrsHttpHooks; -#endif -class SrsBandwidth; - -/** -* the client provides the main logic control for RTMP clients. -*/ -class SrsClient : public SrsConnection, public ISrsReloadHandler -{ -private: - char* ip; - SrsRequest* req; - SrsResponse* res; - SrsRtmp* rtmp; - SrsRefer* refer; -#ifdef SRS_HTTP - SrsHttpHooks* http_hooks; -#endif - SrsBandwidth* bandwidth; -public: - SrsClient(SrsServer* srs_server, st_netfd_t client_stfd); - virtual ~SrsClient(); -protected: - virtual int do_cycle(); -// interface ISrsReloadHandler -public: - virtual int on_reload_vhost_removed(std::string vhost); -private: - // when valid and connected to vhost/app, service the client. - virtual int service_cycle(); - virtual int check_vhost(); - virtual int playing(SrsSource* source); - virtual int publish(SrsSource* source, bool is_fmle); - virtual int process_publish_message(SrsSource* source, SrsCommonMessage* msg, bool is_fmle); - virtual int get_peer_ip(); - virtual int process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg); -private: - virtual int on_connect(); - virtual void on_close(); - virtual int on_publish(); - virtual void on_unpublish(); - virtual int on_play(); - virtual void on_stop(); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 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_CLIENT_HPP +#define SRS_CORE_CLIENT_HPP + +/* +#include +*/ + +#include + +#include +#include + +class SrsRtmp; +class SrsRequest; +class SrsResponse; +class SrsSource; +class SrsRefer; +class SrsConsumer; +class SrsCommonMessage; +#ifdef SRS_HTTP +class SrsHttpHooks; +#endif +class SrsBandwidth; + +/** +* the client provides the main logic control for RTMP clients. +*/ +class SrsClient : public SrsConnection, public ISrsReloadHandler +{ +private: + char* ip; + SrsRequest* req; + SrsResponse* res; + SrsRtmp* rtmp; + SrsRefer* refer; +#ifdef SRS_HTTP + SrsHttpHooks* http_hooks; +#endif + SrsBandwidth* bandwidth; +public: + SrsClient(SrsServer* srs_server, st_netfd_t client_stfd); + virtual ~SrsClient(); +protected: + virtual int do_cycle(); +// interface ISrsReloadHandler +public: + virtual int on_reload_vhost_removed(std::string vhost); +private: + // when valid and connected to vhost/app, service the client. + virtual int service_cycle(); + // stream(play/publish) service cycle, identify client first. + virtual int stream_service_cycle(); + virtual int check_vhost(); + virtual int playing(SrsSource* source); + virtual int publish(SrsSource* source, bool is_fmle); + virtual int process_publish_message(SrsSource* source, SrsCommonMessage* msg, bool is_fmle); + virtual int get_peer_ip(); + virtual int process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg); +private: + virtual int on_connect(); + virtual void on_close(); + virtual int on_publish(); + virtual void on_unpublish(); + virtual int on_play(); + virtual void on_stop(); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_error.cpp b/trunk/src/core/srs_core_error.cpp old mode 100644 new mode 100755 index 3163d0036..0c8e39b11 --- a/trunk/src/core/srs_core_error.cpp +++ b/trunk/src/core/srs_core_error.cpp @@ -1,24 +1,29 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 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 +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 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 + +bool srs_is_system_control_error(int error_code) +{ + return error_code == ERROR_CONTROL_RTMP_CLOSE; +} diff --git a/trunk/src/core/srs_core_error.hpp b/trunk/src/core/srs_core_error.hpp old mode 100644 new mode 100755 index 587491579..e916cdfd1 --- a/trunk/src/core/srs_core_error.hpp +++ b/trunk/src/core/srs_core_error.hpp @@ -1,150 +1,160 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 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_ERROR_HPP -#define SRS_CORE_ERROR_HPP - -/* -#include -*/ - -#include - -#define ERROR_SUCCESS 0 - -#define ERROR_ST_SET_EPOLL 100 -#define ERROR_ST_INITIALIZE 101 -#define ERROR_ST_OPEN_SOCKET 102 -#define ERROR_ST_CREATE_LISTEN_THREAD 103 -#define ERROR_ST_CREATE_CYCLE_THREAD 104 -#define ERROR_ST_CONNECT 105 - -#define ERROR_SOCKET_CREATE 200 -#define ERROR_SOCKET_SETREUSE 201 -#define ERROR_SOCKET_BIND 202 -#define ERROR_SOCKET_LISTEN 203 -#define ERROR_SOCKET_CLOSED 204 -#define ERROR_SOCKET_GET_PEER_NAME 205 -#define ERROR_SOCKET_GET_PEER_IP 206 -#define ERROR_SOCKET_READ 207 -#define ERROR_SOCKET_READ_FULLY 208 -#define ERROR_SOCKET_WRITE 209 -#define ERROR_SOCKET_WAIT 210 -#define ERROR_SOCKET_TIMEOUT 211 -#define ERROR_SOCKET_GET_LOCAL_IP 212 - -#define ERROR_RTMP_PLAIN_REQUIRED 300 -#define ERROR_RTMP_CHUNK_START 301 -#define ERROR_RTMP_MSG_INVLIAD_SIZE 302 -#define ERROR_RTMP_AMF0_DECODE 303 -#define ERROR_RTMP_AMF0_INVALID 304 -#define ERROR_RTMP_REQ_CONNECT 305 -#define ERROR_RTMP_REQ_TCURL 306 -#define ERROR_RTMP_MESSAGE_DECODE 307 -#define ERROR_RTMP_MESSAGE_ENCODE 308 -#define ERROR_RTMP_AMF0_ENCODE 309 -#define ERROR_RTMP_CHUNK_SIZE 310 -#define ERROR_RTMP_TRY_SIMPLE_HS 311 -#define ERROR_RTMP_CH_SCHEMA 312 -#define ERROR_RTMP_PACKET_SIZE 313 -#define ERROR_RTMP_VHOST_NOT_FOUND 314 -#define ERROR_RTMP_ACCESS_DENIED 315 -#define ERROR_RTMP_HANDSHAKE 316 -#define ERROR_RTMP_NO_REQUEST 317 - -#define ERROR_SYSTEM_STREAM_INIT 400 -#define ERROR_SYSTEM_PACKET_INVALID 401 -#define ERROR_SYSTEM_CLIENT_INVALID 402 -#define ERROR_SYSTEM_ASSERT_FAILED 403 -#define ERROR_SYSTEM_SIZE_NEGATIVE 404 -#define ERROR_SYSTEM_CONFIG_INVALID 405 -#define ERROR_SYSTEM_CONFIG_DIRECTIVE 406 -#define ERROR_SYSTEM_CONFIG_BLOCK_START 407 -#define ERROR_SYSTEM_CONFIG_BLOCK_END 408 -#define ERROR_SYSTEM_CONFIG_EOF 409 -#define ERROR_SYSTEM_STREAM_BUSY 410 -#define ERROR_SYSTEM_IP_INVALID 411 -#define ERROR_SYSTEM_FORWARD_LOOP 412 -#define ERROR_SYSTEM_WAITPID 413 -#define ERROR_SYSTEM_BANDWIDTH_KEY 414 -#define ERROR_SYSTEM_BANDWIDTH_DENIED 415 - -// see librtmp. -// failed when open ssl create the dh -#define ERROR_OpenSslCreateDH 500 -// failed when open ssl create the Private key. -#define ERROR_OpenSslCreateP 501 -// when open ssl create G. -#define ERROR_OpenSslCreateG 502 -// when open ssl parse P1024 -#define ERROR_OpenSslParseP1024 503 -// when open ssl set G -#define ERROR_OpenSslSetG 504 -// when open ssl generate DHKeys -#define ERROR_OpenSslGenerateDHKeys 505 -// when open ssl share key already computed. -#define ERROR_OpenSslShareKeyComputed 506 -// when open ssl get shared key size. -#define ERROR_OpenSslGetSharedKeySize 507 -// when open ssl get peer public key. -#define ERROR_OpenSslGetPeerPublicKey 508 -// when open ssl compute shared key. -#define ERROR_OpenSslComputeSharedKey 509 -// when open ssl is invalid DH state. -#define ERROR_OpenSslInvalidDHState 510 -// when open ssl copy key -#define ERROR_OpenSslCopyKey 511 -// when open ssl sha256 digest key invalid size. -#define ERROR_OpenSslSha256DigestSize 512 - -#define ERROR_HLS_METADATA 600 -#define ERROR_HLS_DECODE_ERROR 601 -#define ERROR_HLS_CREATE_DIR 602 -#define ERROR_HLS_OPEN_FAILED 603 -#define ERROR_HLS_WRITE_FAILED 604 -#define ERROR_HLS_AAC_FRAME_LENGTH 605 -#define ERROR_HLS_AVC_SAMPLE_SIZE 606 - -#define ERROR_ENCODER_VCODEC 700 -#define ERROR_ENCODER_OUTPUT 701 -#define ERROR_ENCODER_ACHANNELS 702 -#define ERROR_ENCODER_ASAMPLE_RATE 703 -#define ERROR_ENCODER_ABITRATE 704 -#define ERROR_ENCODER_ACODEC 705 -#define ERROR_ENCODER_VPRESET 706 -#define ERROR_ENCODER_VPROFILE 707 -#define ERROR_ENCODER_VTHREADS 708 -#define ERROR_ENCODER_VHEIGHT 709 -#define ERROR_ENCODER_VWIDTH 710 -#define ERROR_ENCODER_VFPS 711 -#define ERROR_ENCODER_VBITRATE 712 -#define ERROR_ENCODER_FORK 713 -#define ERROR_ENCODER_LOOP 714 -#define ERROR_ENCODER_OPEN 715 -#define ERROR_ENCODER_DUP2 716 - -#define ERROR_HTTP_PARSE_URI 800 -#define ERROR_HTTP_DATA_INVLIAD 801 -#define ERROR_HTTP_PARSE_HEADER 802 - +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 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_ERROR_HPP +#define SRS_CORE_ERROR_HPP + +/* +#include +*/ + +#include + +#define ERROR_SUCCESS 0 + +#define ERROR_ST_SET_EPOLL 100 +#define ERROR_ST_INITIALIZE 101 +#define ERROR_ST_OPEN_SOCKET 102 +#define ERROR_ST_CREATE_LISTEN_THREAD 103 +#define ERROR_ST_CREATE_CYCLE_THREAD 104 +#define ERROR_ST_CONNECT 105 + +#define ERROR_SOCKET_CREATE 200 +#define ERROR_SOCKET_SETREUSE 201 +#define ERROR_SOCKET_BIND 202 +#define ERROR_SOCKET_LISTEN 203 +#define ERROR_SOCKET_CLOSED 204 +#define ERROR_SOCKET_GET_PEER_NAME 205 +#define ERROR_SOCKET_GET_PEER_IP 206 +#define ERROR_SOCKET_READ 207 +#define ERROR_SOCKET_READ_FULLY 208 +#define ERROR_SOCKET_WRITE 209 +#define ERROR_SOCKET_WAIT 210 +#define ERROR_SOCKET_TIMEOUT 211 +#define ERROR_SOCKET_GET_LOCAL_IP 212 + +#define ERROR_RTMP_PLAIN_REQUIRED 300 +#define ERROR_RTMP_CHUNK_START 301 +#define ERROR_RTMP_MSG_INVLIAD_SIZE 302 +#define ERROR_RTMP_AMF0_DECODE 303 +#define ERROR_RTMP_AMF0_INVALID 304 +#define ERROR_RTMP_REQ_CONNECT 305 +#define ERROR_RTMP_REQ_TCURL 306 +#define ERROR_RTMP_MESSAGE_DECODE 307 +#define ERROR_RTMP_MESSAGE_ENCODE 308 +#define ERROR_RTMP_AMF0_ENCODE 309 +#define ERROR_RTMP_CHUNK_SIZE 310 +#define ERROR_RTMP_TRY_SIMPLE_HS 311 +#define ERROR_RTMP_CH_SCHEMA 312 +#define ERROR_RTMP_PACKET_SIZE 313 +#define ERROR_RTMP_VHOST_NOT_FOUND 314 +#define ERROR_RTMP_ACCESS_DENIED 315 +#define ERROR_RTMP_HANDSHAKE 316 +#define ERROR_RTMP_NO_REQUEST 317 + +#define ERROR_SYSTEM_STREAM_INIT 400 +#define ERROR_SYSTEM_PACKET_INVALID 401 +#define ERROR_SYSTEM_CLIENT_INVALID 402 +#define ERROR_SYSTEM_ASSERT_FAILED 403 +#define ERROR_SYSTEM_SIZE_NEGATIVE 404 +#define ERROR_SYSTEM_CONFIG_INVALID 405 +#define ERROR_SYSTEM_CONFIG_DIRECTIVE 406 +#define ERROR_SYSTEM_CONFIG_BLOCK_START 407 +#define ERROR_SYSTEM_CONFIG_BLOCK_END 408 +#define ERROR_SYSTEM_CONFIG_EOF 409 +#define ERROR_SYSTEM_STREAM_BUSY 410 +#define ERROR_SYSTEM_IP_INVALID 411 +#define ERROR_SYSTEM_FORWARD_LOOP 412 +#define ERROR_SYSTEM_WAITPID 413 +#define ERROR_SYSTEM_BANDWIDTH_KEY 414 +#define ERROR_SYSTEM_BANDWIDTH_DENIED 415 + +// see librtmp. +// failed when open ssl create the dh +#define ERROR_OpenSslCreateDH 500 +// failed when open ssl create the Private key. +#define ERROR_OpenSslCreateP 501 +// when open ssl create G. +#define ERROR_OpenSslCreateG 502 +// when open ssl parse P1024 +#define ERROR_OpenSslParseP1024 503 +// when open ssl set G +#define ERROR_OpenSslSetG 504 +// when open ssl generate DHKeys +#define ERROR_OpenSslGenerateDHKeys 505 +// when open ssl share key already computed. +#define ERROR_OpenSslShareKeyComputed 506 +// when open ssl get shared key size. +#define ERROR_OpenSslGetSharedKeySize 507 +// when open ssl get peer public key. +#define ERROR_OpenSslGetPeerPublicKey 508 +// when open ssl compute shared key. +#define ERROR_OpenSslComputeSharedKey 509 +// when open ssl is invalid DH state. +#define ERROR_OpenSslInvalidDHState 510 +// when open ssl copy key +#define ERROR_OpenSslCopyKey 511 +// when open ssl sha256 digest key invalid size. +#define ERROR_OpenSslSha256DigestSize 512 + +#define ERROR_HLS_METADATA 600 +#define ERROR_HLS_DECODE_ERROR 601 +#define ERROR_HLS_CREATE_DIR 602 +#define ERROR_HLS_OPEN_FAILED 603 +#define ERROR_HLS_WRITE_FAILED 604 +#define ERROR_HLS_AAC_FRAME_LENGTH 605 +#define ERROR_HLS_AVC_SAMPLE_SIZE 606 + +#define ERROR_ENCODER_VCODEC 700 +#define ERROR_ENCODER_OUTPUT 701 +#define ERROR_ENCODER_ACHANNELS 702 +#define ERROR_ENCODER_ASAMPLE_RATE 703 +#define ERROR_ENCODER_ABITRATE 704 +#define ERROR_ENCODER_ACODEC 705 +#define ERROR_ENCODER_VPRESET 706 +#define ERROR_ENCODER_VPROFILE 707 +#define ERROR_ENCODER_VTHREADS 708 +#define ERROR_ENCODER_VHEIGHT 709 +#define ERROR_ENCODER_VWIDTH 710 +#define ERROR_ENCODER_VFPS 711 +#define ERROR_ENCODER_VBITRATE 712 +#define ERROR_ENCODER_FORK 713 +#define ERROR_ENCODER_LOOP 714 +#define ERROR_ENCODER_OPEN 715 +#define ERROR_ENCODER_DUP2 716 + +#define ERROR_HTTP_PARSE_URI 800 +#define ERROR_HTTP_DATA_INVLIAD 801 +#define ERROR_HTTP_PARSE_HEADER 802 + +// system control message, +// not an error, but special control logic. +// sys ctl: rtmp close stream, support replay. +#define ERROR_CONTROL_RTMP_CLOSE 900 + +/** +* whether the error code is an system control error. +*/ +extern bool srs_is_system_control_error(int error_code); + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_protocol.cpp b/trunk/src/core/srs_core_protocol.cpp old mode 100644 new mode 100755 index 92e150065..5989df707 --- a/trunk/src/core/srs_core_protocol.cpp +++ b/trunk/src/core/srs_core_protocol.cpp @@ -196,6 +196,7 @@ messages. */ #define RTMP_AMF0_COMMAND_CONNECT "connect" #define RTMP_AMF0_COMMAND_CREATE_STREAM "createStream" +#define RTMP_AMF0_COMMAND_CLOSE_STREAM "closeStream" #define RTMP_AMF0_COMMAND_PLAY "play" #define RTMP_AMF0_COMMAND_PAUSE "pause" #define RTMP_AMF0_COMMAND_ON_BW_DONE "onBWDone" @@ -1363,6 +1364,10 @@ int SrsCommonMessage::decode_packet(SrsProtocol* protocol) srs_info("decode the AMF0/AMF3 band width check message."); packet = new SrsBandwidthPacket(); return packet->decode(stream); + } else if (command == RTMP_AMF0_COMMAND_CLOSE_STREAM) { + srs_info("decode the AMF0/AMF3 closeStream message."); + packet = new SrsCloseStreamPacket(); + return packet->decode(stream); } // default packet to drop message. @@ -2064,6 +2069,41 @@ int SrsCreateStreamResPacket::encode_packet(SrsStream* stream) return ret; } +SrsCloseStreamPacket::SrsCloseStreamPacket() +{ + command_name = RTMP_AMF0_COMMAND_CLOSE_STREAM; + transaction_id = 0; + command_object = new SrsAmf0Null(); +} + +SrsCloseStreamPacket::~SrsCloseStreamPacket() +{ + srs_freep(command_object); +} + +int SrsCloseStreamPacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode closeStream command_name failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode closeStream transaction_id failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode closeStream command_object failed. ret=%d", ret); + return ret; + } + srs_info("amf0 decode closeStream packet success"); + + return ret; +} + SrsFMLEStartPacket::SrsFMLEStartPacket() { command_name = RTMP_AMF0_COMMAND_CREATE_STREAM; diff --git a/trunk/src/core/srs_core_protocol.hpp b/trunk/src/core/srs_core_protocol.hpp old mode 100644 new mode 100755 index ca6602c67..2c26475f5 --- a/trunk/src/core/srs_core_protocol.hpp +++ b/trunk/src/core/srs_core_protocol.hpp @@ -1,1226 +1,1256 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 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_PROTOCOL_HPP -#define SRS_CORE_PROTOCOL_HPP - -/* -#include -*/ - -#include - -#include -#include - -#include -#include - -// the following is the timeout for rtmp protocol, -// to avoid death connection. - -// when got a messae header, there must be some data, -// increase recv timeout to got an entire message. -#define SRS_MIN_RECV_TIMEOUT_US 60*1000*1000L - -// the timeout to wait for client control message, -// if timeout, we generally ignore and send the data to client, -// generally, it's the pulse time for data seding. -#define SRS_PULSE_TIMEOUT_US 200*1000L - -// the timeout to wait client data, -// if timeout, close the connection. -#define SRS_SEND_TIMEOUT_US 30*1000*1000L - -// the timeout to send data to client, -// if timeout, close the connection. -#define SRS_RECV_TIMEOUT_US 30*1000*1000L - -// when stream is busy, for example, streaming is already -// publishing, when a new client to request to publish, -// sleep a while and close the connection. -#define SRS_STREAM_BUSY_SLEEP_US 3*1000*1000L - -// when error, forwarder sleep for a while and retry. -#define SRS_FORWARDER_SLEEP_US 3*1000*1000L - -// when error, encoder sleep for a while and retry. -#define SRS_ENCODER_SLEEP_US 3*1000*1000L - -class SrsSocket; -class SrsBuffer; -class SrsPacket; -class SrsStream; -class SrsCommonMessage; -class SrsChunkStream; -class SrsAmf0Object; -class SrsAmf0Null; -class SrsAmf0Undefined; -class ISrsMessage; - -// convert class name to string. -#define CLASS_NAME_STRING(className) #className - -/** -* max rtmp header size: -* 1bytes basic header, -* 11bytes message header, -* 4bytes timestamp header, -* that is, 1+11+4=16bytes. -*/ -#define RTMP_MAX_FMT0_HEADER_SIZE 16 -/** -* max rtmp header size: -* 1bytes basic header, -* 4bytes timestamp header, -* that is, 1+4=5bytes. -*/ -#define RTMP_MAX_FMT3_HEADER_SIZE 5 - -/** -* the protocol provides the rtmp-message-protocol services, -* to recv RTMP message from RTMP chunk stream, -* and to send out RTMP message over RTMP chunk stream. -*/ -class SrsProtocol -{ -private: - struct AckWindowSize - { - int ack_window_size; - int64_t acked_size; - - AckWindowSize(); - }; -// peer in/out -private: - st_netfd_t stfd; - SrsSocket* skt; - char* pp; - /** - * requests sent out, used to build the response. - * key: transactionId - * value: the request command name - */ - std::map requests; -// peer in -private: - std::map chunk_streams; - SrsBuffer* buffer; - int32_t in_chunk_size; - AckWindowSize in_ack_size; -// peer out -private: - char out_header_fmt0[RTMP_MAX_FMT0_HEADER_SIZE]; - char out_header_fmt3[RTMP_MAX_FMT3_HEADER_SIZE]; - int32_t out_chunk_size; -public: - SrsProtocol(st_netfd_t client_stfd); - virtual ~SrsProtocol(); -public: - std::string get_request_name(double transcationId); - /** - * set the timeout in us. - * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. - */ - 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_send_timeout(); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - virtual int get_recv_kbps(); - virtual int get_send_kbps(); - /** - * recv a message with raw/undecoded payload from peer. - * the payload is not decoded, use srs_rtmp_expect_message if requires - * specifies message. - * @pmsg, user must free it. NULL if not success. - * @remark, only when success, user can use and must free the pmsg. - */ - virtual int recv_message(SrsCommonMessage** pmsg); - /** - * send out message with encoded payload to peer. - * use the message encode method to encode to payload, - * then sendout over socket. - * @msg this method will free it whatever return value. - */ - virtual int send_message(ISrsMessage* msg); -private: - /** - * when recv message, update the context. - */ - virtual int on_recv_message(SrsCommonMessage* msg); - virtual int response_acknowledgement_message(); - virtual int response_ping_message(int32_t timestamp); - /** - * when message sentout, update the context. - */ - virtual int on_send_message(ISrsMessage* msg); - /** - * try to recv interlaced message from peer, - * return error if error occur and nerver set the pmsg, - * return success and pmsg set to NULL if no entire message got, - * return success and pmsg set to entire message if got one. - */ - virtual int recv_interlaced_message(SrsCommonMessage** pmsg); - /** - * read the chunk basic header(fmt, cid) from chunk stream. - * user can discovery a SrsChunkStream by cid. - * @bh_size return the chunk basic header size, to remove the used bytes when finished. - */ - virtual int read_basic_header(char& fmt, int& cid, int& bh_size); - /** - * read the chunk message header(timestamp, payload_length, message_type, stream_id) - * from chunk stream and save to SrsChunkStream. - * @mh_size return the chunk message header size, to remove the used bytes when finished. - */ - virtual int read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size); - /** - * read the chunk payload, remove the used bytes in buffer, - * if got entire message, set the pmsg. - * @payload_size read size in this roundtrip, generally a chunk size or left message size. - */ - virtual int read_message_payload(SrsChunkStream* chunk, int bh_size, int mh_size, int& payload_size, SrsCommonMessage** pmsg); -}; - -/** -* 4.1. Message Header -*/ -struct SrsMessageHeader -{ - /** - * One byte field to represent the message type. A range of type IDs - * (1-7) are reserved for protocol control messages. - */ - int8_t message_type; - /** - * Three-byte field that represents the size of the payload in bytes. - * It is set in big-endian format. - */ - int32_t payload_length; - /** - * Three-byte field that contains a timestamp delta of the message. - * The 4 bytes are packed in the big-endian order. - * @remark, only used for decoding message from chunk stream. - */ - int32_t timestamp_delta; - /** - * Three-byte field that identifies the stream of the message. These - * bytes are set in big-endian format. - */ - int32_t stream_id; - - /** - * Four-byte field that contains a timestamp of the message. - * The 4 bytes are packed in the big-endian order. - * @remark, used as calc timestamp when decode and encode time. - * @remark, we use 64bits for large time for jitter detect and hls. - */ - int64_t timestamp; - - SrsMessageHeader(); - virtual ~SrsMessageHeader(); - - bool is_audio(); - bool is_video(); - bool is_amf0_command(); - bool is_amf0_data(); - bool is_amf3_command(); - bool is_amf3_data(); - bool is_window_ackledgement_size(); - bool is_set_chunk_size(); - bool is_user_control_message(); -}; - -/** -* incoming chunk stream maybe interlaced, -* use the chunk stream to cache the input RTMP chunk streams. -*/ -class SrsChunkStream -{ -public: - /** - * represents the basic header fmt, - * which used to identify the variant message header type. - */ - char fmt; - /** - * represents the basic header cid, - * which is the chunk stream id. - */ - int cid; - /** - * cached message header - */ - SrsMessageHeader header; - /** - * whether the chunk message header has extended timestamp. - */ - bool extended_timestamp; - /** - * partially read message. - */ - SrsCommonMessage* msg; - /** - * decoded msg count, to identify whether the chunk stream is fresh. - */ - int64_t msg_count; -public: - SrsChunkStream(int _cid); - virtual ~SrsChunkStream(); -}; - -/** -* message to output. -*/ -class ISrsMessage -{ -// 4.1. Message Header -public: - SrsMessageHeader header; -// 4.2. Message Payload -public: - /** - * The other part which is the payload is the actual data that is - * contained in the message. For example, it could be some audio samples - * or compressed video data. The payload format and interpretation are - * beyond the scope of this document. - */ - int32_t size; - int8_t* payload; -public: - ISrsMessage(); - virtual ~ISrsMessage(); -public: - /** - * whether message canbe decoded. - * only update the context when message canbe decoded. - */ - virtual bool can_decode() = 0; -/** -* encode functions. -*/ -public: - /** - * get the perfered cid(chunk stream id) which sendout over. - */ - virtual int get_perfer_cid() = 0; - /** - * encode the packet to message payload bytes. - * @remark there exists empty packet, so maybe the payload is NULL. - */ - virtual int encode_packet() = 0; -}; - -/** -* common RTMP message defines in rtmp.part2.Message-Formats.pdf. -* cannbe parse and decode. -*/ -class SrsCommonMessage : public ISrsMessage -{ -private: - typedef ISrsMessage super; - disable_default_copy(SrsCommonMessage); -// decoded message payload. -private: - SrsStream* stream; - SrsPacket* packet; -public: - SrsCommonMessage(); - virtual ~SrsCommonMessage(); -public: - virtual bool can_decode(); -/** -* decode functions. -*/ -public: - /** - * decode packet from message payload. - */ - // TODO: use protocol to decode it. - virtual int decode_packet(SrsProtocol* protocol); - /** - * get the decoded packet which decoded by decode_packet(). - * @remark, user never free the pkt, the message will auto free it. - */ - virtual SrsPacket* get_packet(); -/** -* encode functions. -*/ -public: - /** - * get the perfered cid(chunk stream id) which sendout over. - */ - virtual int get_perfer_cid(); - /** - * set the encoded packet to encode_packet() to payload. - * @stream_id, the id of stream which is created by createStream. - * @remark, user never free the pkt, the message will auto free it. - * @return message itself. - */ - // TODO: refine the send methods. - virtual SrsCommonMessage* set_packet(SrsPacket* pkt, int stream_id); - /** - * encode the packet to message payload bytes. - * @remark there exists empty packet, so maybe the payload is NULL. - */ - virtual int encode_packet(); -}; - -/** -* shared ptr message. -* for audio/video/data message that need less memory copy. -* and only for output. -*/ -class SrsSharedPtrMessage : public ISrsMessage -{ -private: - typedef ISrsMessage super; -private: - struct SrsSharedPtr - { - char* payload; - int size; - int perfer_cid; - int shared_count; - - SrsSharedPtr(); - virtual ~SrsSharedPtr(); - }; - SrsSharedPtr* ptr; -public: - SrsSharedPtrMessage(); - virtual ~SrsSharedPtrMessage(); -public: - virtual bool can_decode(); -public: - /** - * set the shared payload. - * we will detach the payload of source, - * so ensure donot use it before. - */ - virtual int initialize(SrsCommonMessage* source); - /** - * set the shared payload. - * we will use the payload, donot use the payload of source. - */ - virtual int initialize(SrsCommonMessage* source, char* payload, int size); - virtual SrsSharedPtrMessage* copy(); -public: - /** - * get the perfered cid(chunk stream id) which sendout over. - */ - virtual int get_perfer_cid(); - /** - * ignored. - * for shared message, nothing should be done. - * use initialize() to set the data. - */ - virtual int encode_packet(); -}; - -/** -* the decoded message payload. -* @remark we seperate the packet from message, -* for the packet focus on logic and domain data, -* the message bind to the protocol and focus on protocol, such as header. -* we can merge the message and packet, using OOAD hierachy, packet extends from message, -* it's better for me to use components -- the message use the packet as payload. -*/ -class SrsPacket -{ -protected: - /** - * subpacket must override to provide the right class name. - */ - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsPacket); - } -public: - SrsPacket(); - virtual ~SrsPacket(); -/** -* decode functions. -*/ -public: - /** - * subpacket must override to decode packet from stream. - * @remark never invoke the super.decode, it always failed. - */ - virtual int decode(SrsStream* stream); -/** -* encode functions. -*/ -public: - virtual int get_perfer_cid(); - virtual int get_payload_length(); -public: - /** - * subpacket must override to provide the right message type. - */ - virtual int get_message_type(); - /** - * the subpacket can override this encode, - * for example, video and audio will directly set the payload withou memory copy, - * other packet which need to serialize/encode to bytes by override the - * get_size and encode_packet. - */ - virtual int encode(int& size, char*& payload); -protected: - /** - * subpacket can override to calc the packet size. - */ - virtual int get_size(); - /** - * subpacket can override to encode the payload to stream. - * @remark never invoke the super.encode_packet, it always failed. - */ - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 4.1.1. connect -* The client sends the connect command to the server to request -* connection to a server application instance. -*/ -class SrsConnectAppPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsConnectAppPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Object* command_object; -public: - SrsConnectAppPacket(); - virtual ~SrsConnectAppPacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; -/** -* response for SrsConnectAppPacket. -*/ -class SrsConnectAppResPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsConnectAppResPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Object* props; - SrsAmf0Object* info; -public: - SrsConnectAppResPacket(); - virtual ~SrsConnectAppResPacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 4.1.3. createStream -* The client sends this command to the server to create a logical -* channel for message communication The publishing of audio, video, and -* metadata is carried out over stream channel created using the -* createStream command. -*/ -class SrsCreateStreamPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsCreateStreamPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; -public: - SrsCreateStreamPacket(); - virtual ~SrsCreateStreamPacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; -/** -* response for SrsCreateStreamPacket. -*/ -class SrsCreateStreamResPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsCreateStreamResPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - double stream_id; -public: - SrsCreateStreamResPacket(double _transaction_id, double _stream_id); - virtual ~SrsCreateStreamResPacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* FMLE start publish: ReleaseStream/PublishStream -*/ -class SrsFMLEStartPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsFMLEStartPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - std::string stream_name; -public: - SrsFMLEStartPacket(); - virtual ~SrsFMLEStartPacket(); -public: - virtual int decode(SrsStream* stream); -}; -/** -* response for SrsFMLEStartPacket. -*/ -class SrsFMLEStartResPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsFMLEStartResPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - SrsAmf0Undefined* args; -public: - SrsFMLEStartResPacket(double _transaction_id); - virtual ~SrsFMLEStartResPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* FMLE/flash publish -* 4.2.6. Publish -* The client sends the publish command to publish a named stream to the -* server. Using this name, any client can play this stream and receive -* the published audio, video, and data messages. -*/ -class SrsPublishPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsPublishPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - std::string stream_name; - // optional, default to live. - std::string type; -public: - SrsPublishPacket(); - virtual ~SrsPublishPacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 4.2.8. pause -* The client sends the pause command to tell the server to pause or -* start playing. -*/ -class SrsPausePacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsPausePacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - bool is_pause; - double time_ms; -public: - SrsPausePacket(); - virtual ~SrsPausePacket(); -public: - virtual int decode(SrsStream* stream); -}; - -/** -* 4.2.1. play -* The client sends this command to the server to play a stream. -*/ -class SrsPlayPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsPlayPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - std::string stream_name; - double start; - double duration; - bool reset; -public: - SrsPlayPacket(); - virtual ~SrsPlayPacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; -/** -* response for SrsPlayPacket. -* @remark, user must set the stream_id in header. -*/ -class SrsPlayResPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsPlayResPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - SrsAmf0Object* desc; -public: - SrsPlayResPacket(); - virtual ~SrsPlayResPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* when bandwidth test done, notice client. -*/ -class SrsOnBWDonePacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsOnBWDonePacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* args; -public: - SrsOnBWDonePacket(); - virtual ~SrsOnBWDonePacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* onStatus command, AMF0 Call -* @remark, user must set the stream_id by SrsMessage.set_packet(). -*/ -class SrsOnStatusCallPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsOnStatusCallPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* args; - SrsAmf0Object* data; -public: - SrsOnStatusCallPacket(); - virtual ~SrsOnStatusCallPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* the special packet for the bandwidth test. -* actually, it's a SrsOnStatusCallPacket, but -* 1. encode with data field, to send data to client. -* 2. decode ignore the data field, donot care. -*/ -class SrsBandwidthPacket : public SrsPacket -{ -private: - typedef SrsPacket super; - disable_default_copy(SrsBandwidthPacket); -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsBandwidthPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* args; - SrsAmf0Object* data; -public: - SrsBandwidthPacket(); - virtual ~SrsBandwidthPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -public: - virtual int decode(SrsStream* stream); -public: - virtual bool is_starting_play(); - virtual bool is_stopped_play(); - virtual bool is_starting_publish(); - virtual bool is_stopped_publish(); - virtual bool is_flash_final(); - static SrsBandwidthPacket* create_finish(); - static SrsBandwidthPacket* create_start_play(); - static SrsBandwidthPacket* create_playing(); - static SrsBandwidthPacket* create_stop_play(); - static SrsBandwidthPacket* create_start_publish(); - static SrsBandwidthPacket* create_stop_publish(); -private: - virtual SrsBandwidthPacket* set_command(std::string command); -}; - -/** -* onStatus data, AMF0 Data -* @remark, user must set the stream_id by SrsMessage.set_packet(). -*/ -class SrsOnStatusDataPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsOnStatusDataPacket); - } -public: - std::string command_name; - SrsAmf0Object* data; -public: - SrsOnStatusDataPacket(); - virtual ~SrsOnStatusDataPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* AMF0Data RtmpSampleAccess -* @remark, user must set the stream_id by SrsMessage.set_packet(). -*/ -class SrsSampleAccessPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsSampleAccessPacket); - } -public: - std::string command_name; - bool video_sample_access; - bool audio_sample_access; -public: - SrsSampleAccessPacket(); - virtual ~SrsSampleAccessPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* the stream metadata. -* FMLE: @setDataFrame -* others: onMetaData -*/ -class SrsOnMetaDataPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsOnMetaDataPacket); - } -public: - std::string name; - SrsAmf0Object* metadata; -public: - SrsOnMetaDataPacket(); - virtual ~SrsOnMetaDataPacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 5.5. Window Acknowledgement Size (5) -* The client or the server sends this message to inform the peer which -* window size to use when sending acknowledgment. -*/ -class SrsSetWindowAckSizePacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsSetWindowAckSizePacket); - } -public: - int32_t ackowledgement_window_size; -public: - SrsSetWindowAckSizePacket(); - virtual ~SrsSetWindowAckSizePacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 5.3. Acknowledgement (3) -* The client or the server sends the acknowledgment to the peer after -* receiving bytes equal to the window size. -*/ -class SrsAcknowledgementPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsAcknowledgementPacket); - } -public: - int32_t sequence_number; -public: - SrsAcknowledgementPacket(); - virtual ~SrsAcknowledgementPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 7.1. Set Chunk Size -* Protocol control message 1, Set Chunk Size, is used to notify the -* peer about the new maximum chunk size. -*/ -class SrsSetChunkSizePacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsSetChunkSizePacket); - } -public: - int32_t chunk_size; -public: - SrsSetChunkSizePacket(); - virtual ~SrsSetChunkSizePacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 5.6. Set Peer Bandwidth (6) -* The client or the server sends this message to update the output -* bandwidth of the peer. -*/ -class SrsSetPeerBandwidthPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsSetPeerBandwidthPacket); - } -public: - int32_t bandwidth; - int8_t type; -public: - SrsSetPeerBandwidthPacket(); - virtual ~SrsSetPeerBandwidthPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -// 3.7. User Control message -enum SrcPCUCEventType -{ - // generally, 4bytes event-data - SrcPCUCStreamBegin = 0x00, - SrcPCUCStreamEOF = 0x01, - SrcPCUCStreamDry = 0x02, - SrcPCUCSetBufferLength = 0x03, // 8bytes event-data - SrcPCUCStreamIsRecorded = 0x04, - SrcPCUCPingRequest = 0x06, - SrcPCUCPingResponse = 0x07, -}; - -/** -* for the EventData is 4bytes. -* Stream Begin(=0) 4-bytes stream ID -* Stream EOF(=1) 4-bytes stream ID -* StreamDry(=2) 4-bytes stream ID -* SetBufferLength(=3) 8-bytes 4bytes stream ID, 4bytes buffer length. -* StreamIsRecorded(=4) 4-bytes stream ID -* PingRequest(=6) 4-bytes timestamp local server time -* PingResponse(=7) 4-bytes timestamp received ping request. -* -* 3.7. User Control message -* +------------------------------+------------------------- -* | Event Type ( 2- bytes ) | Event Data -* +------------------------------+------------------------- -* Figure 5 Pay load for the ‘User Control Message’. -*/ -class SrsUserControlPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsUserControlPacket); - } -public: - // @see: SrcPCUCEventType - int16_t event_type; - int32_t event_data; - /** - * 4bytes if event_type is SetBufferLength; otherwise 0. - */ - int32_t extra_data; -public: - SrsUserControlPacket(); - virtual ~SrsUserControlPacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* expect a specified message, drop others util got specified one. -* @pmsg, user must free it. NULL if not success. -* @ppacket, store in the pmsg, user must never free it. NULL if not success. -* @remark, only when success, user can use and must free the pmsg/ppacket. -*/ -template -int srs_rtmp_expect_message(SrsProtocol* protocol, SrsCommonMessage** pmsg, T** ppacket) -{ - *pmsg = NULL; - *ppacket = NULL; - - int ret = ERROR_SUCCESS; - - while (true) { - SrsCommonMessage* msg = NULL; - if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("recv message failed. ret=%d", ret); - return ret; - } - srs_verbose("recv message success."); - - if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { - delete msg; - srs_error("decode message failed. ret=%d", ret); - return ret; - } - - T* pkt = dynamic_cast(msg->get_packet()); - if (!pkt) { - delete msg; - srs_trace("drop message(type=%d, size=%d, time=%"PRId64", sid=%d).", - msg->header.message_type, msg->header.payload_length, - msg->header.timestamp, msg->header.stream_id); - continue; - } - - *pmsg = msg; - *ppacket = pkt; - break; - } - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 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_PROTOCOL_HPP +#define SRS_CORE_PROTOCOL_HPP + +/* +#include +*/ + +#include + +#include +#include + +#include +#include + +// the following is the timeout for rtmp protocol, +// to avoid death connection. + +// when got a messae header, there must be some data, +// increase recv timeout to got an entire message. +#define SRS_MIN_RECV_TIMEOUT_US 60*1000*1000L + +// the timeout to wait for client control message, +// if timeout, we generally ignore and send the data to client, +// generally, it's the pulse time for data seding. +#define SRS_PULSE_TIMEOUT_US 200*1000L + +// the timeout to wait client data, +// if timeout, close the connection. +#define SRS_SEND_TIMEOUT_US 30*1000*1000L + +// the timeout to send data to client, +// if timeout, close the connection. +#define SRS_RECV_TIMEOUT_US 30*1000*1000L + +// the timeout to wait client data, when client paused +// if timeout, close the connection. +#define SRS_PAUSED_SEND_TIMEOUT_US 30*60*1000*1000L + +// the timeout to send data to client, when client paused +// if timeout, close the connection. +#define SRS_PAUSED_RECV_TIMEOUT_US 30*60*1000*1000L + +// when stream is busy, for example, streaming is already +// publishing, when a new client to request to publish, +// sleep a while and close the connection. +#define SRS_STREAM_BUSY_SLEEP_US 3*1000*1000L + +// when error, forwarder sleep for a while and retry. +#define SRS_FORWARDER_SLEEP_US 3*1000*1000L + +// when error, encoder sleep for a while and retry. +#define SRS_ENCODER_SLEEP_US 3*1000*1000L + +class SrsSocket; +class SrsBuffer; +class SrsPacket; +class SrsStream; +class SrsCommonMessage; +class SrsChunkStream; +class SrsAmf0Object; +class SrsAmf0Null; +class SrsAmf0Undefined; +class ISrsMessage; + +// convert class name to string. +#define CLASS_NAME_STRING(className) #className + +/** +* max rtmp header size: +* 1bytes basic header, +* 11bytes message header, +* 4bytes timestamp header, +* that is, 1+11+4=16bytes. +*/ +#define RTMP_MAX_FMT0_HEADER_SIZE 16 +/** +* max rtmp header size: +* 1bytes basic header, +* 4bytes timestamp header, +* that is, 1+4=5bytes. +*/ +#define RTMP_MAX_FMT3_HEADER_SIZE 5 + +/** +* the protocol provides the rtmp-message-protocol services, +* to recv RTMP message from RTMP chunk stream, +* and to send out RTMP message over RTMP chunk stream. +*/ +class SrsProtocol +{ +private: + struct AckWindowSize + { + int ack_window_size; + int64_t acked_size; + + AckWindowSize(); + }; +// peer in/out +private: + st_netfd_t stfd; + SrsSocket* skt; + char* pp; + /** + * requests sent out, used to build the response. + * key: transactionId + * value: the request command name + */ + std::map requests; +// peer in +private: + std::map chunk_streams; + SrsBuffer* buffer; + int32_t in_chunk_size; + AckWindowSize in_ack_size; +// peer out +private: + char out_header_fmt0[RTMP_MAX_FMT0_HEADER_SIZE]; + char out_header_fmt3[RTMP_MAX_FMT3_HEADER_SIZE]; + int32_t out_chunk_size; +public: + SrsProtocol(st_netfd_t client_stfd); + virtual ~SrsProtocol(); +public: + std::string get_request_name(double transcationId); + /** + * set the timeout in us. + * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. + */ + 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_send_timeout(); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int get_recv_kbps(); + virtual int get_send_kbps(); + /** + * recv a message with raw/undecoded payload from peer. + * the payload is not decoded, use srs_rtmp_expect_message if requires + * specifies message. + * @pmsg, user must free it. NULL if not success. + * @remark, only when success, user can use and must free the pmsg. + */ + virtual int recv_message(SrsCommonMessage** pmsg); + /** + * send out message with encoded payload to peer. + * use the message encode method to encode to payload, + * then sendout over socket. + * @msg this method will free it whatever return value. + */ + virtual int send_message(ISrsMessage* msg); +private: + /** + * when recv message, update the context. + */ + virtual int on_recv_message(SrsCommonMessage* msg); + virtual int response_acknowledgement_message(); + virtual int response_ping_message(int32_t timestamp); + /** + * when message sentout, update the context. + */ + virtual int on_send_message(ISrsMessage* msg); + /** + * try to recv interlaced message from peer, + * return error if error occur and nerver set the pmsg, + * return success and pmsg set to NULL if no entire message got, + * return success and pmsg set to entire message if got one. + */ + virtual int recv_interlaced_message(SrsCommonMessage** pmsg); + /** + * read the chunk basic header(fmt, cid) from chunk stream. + * user can discovery a SrsChunkStream by cid. + * @bh_size return the chunk basic header size, to remove the used bytes when finished. + */ + virtual int read_basic_header(char& fmt, int& cid, int& bh_size); + /** + * read the chunk message header(timestamp, payload_length, message_type, stream_id) + * from chunk stream and save to SrsChunkStream. + * @mh_size return the chunk message header size, to remove the used bytes when finished. + */ + virtual int read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size); + /** + * read the chunk payload, remove the used bytes in buffer, + * if got entire message, set the pmsg. + * @payload_size read size in this roundtrip, generally a chunk size or left message size. + */ + virtual int read_message_payload(SrsChunkStream* chunk, int bh_size, int mh_size, int& payload_size, SrsCommonMessage** pmsg); +}; + +/** +* 4.1. Message Header +*/ +struct SrsMessageHeader +{ + /** + * One byte field to represent the message type. A range of type IDs + * (1-7) are reserved for protocol control messages. + */ + int8_t message_type; + /** + * Three-byte field that represents the size of the payload in bytes. + * It is set in big-endian format. + */ + int32_t payload_length; + /** + * Three-byte field that contains a timestamp delta of the message. + * The 4 bytes are packed in the big-endian order. + * @remark, only used for decoding message from chunk stream. + */ + int32_t timestamp_delta; + /** + * Three-byte field that identifies the stream of the message. These + * bytes are set in big-endian format. + */ + int32_t stream_id; + + /** + * Four-byte field that contains a timestamp of the message. + * The 4 bytes are packed in the big-endian order. + * @remark, used as calc timestamp when decode and encode time. + * @remark, we use 64bits for large time for jitter detect and hls. + */ + int64_t timestamp; + + SrsMessageHeader(); + virtual ~SrsMessageHeader(); + + bool is_audio(); + bool is_video(); + bool is_amf0_command(); + bool is_amf0_data(); + bool is_amf3_command(); + bool is_amf3_data(); + bool is_window_ackledgement_size(); + bool is_set_chunk_size(); + bool is_user_control_message(); +}; + +/** +* incoming chunk stream maybe interlaced, +* use the chunk stream to cache the input RTMP chunk streams. +*/ +class SrsChunkStream +{ +public: + /** + * represents the basic header fmt, + * which used to identify the variant message header type. + */ + char fmt; + /** + * represents the basic header cid, + * which is the chunk stream id. + */ + int cid; + /** + * cached message header + */ + SrsMessageHeader header; + /** + * whether the chunk message header has extended timestamp. + */ + bool extended_timestamp; + /** + * partially read message. + */ + SrsCommonMessage* msg; + /** + * decoded msg count, to identify whether the chunk stream is fresh. + */ + int64_t msg_count; +public: + SrsChunkStream(int _cid); + virtual ~SrsChunkStream(); +}; + +/** +* message to output. +*/ +class ISrsMessage +{ +// 4.1. Message Header +public: + SrsMessageHeader header; +// 4.2. Message Payload +public: + /** + * The other part which is the payload is the actual data that is + * contained in the message. For example, it could be some audio samples + * or compressed video data. The payload format and interpretation are + * beyond the scope of this document. + */ + int32_t size; + int8_t* payload; +public: + ISrsMessage(); + virtual ~ISrsMessage(); +public: + /** + * whether message canbe decoded. + * only update the context when message canbe decoded. + */ + virtual bool can_decode() = 0; +/** +* encode functions. +*/ +public: + /** + * get the perfered cid(chunk stream id) which sendout over. + */ + virtual int get_perfer_cid() = 0; + /** + * encode the packet to message payload bytes. + * @remark there exists empty packet, so maybe the payload is NULL. + */ + virtual int encode_packet() = 0; +}; + +/** +* common RTMP message defines in rtmp.part2.Message-Formats.pdf. +* cannbe parse and decode. +*/ +class SrsCommonMessage : public ISrsMessage +{ +private: + typedef ISrsMessage super; + disable_default_copy(SrsCommonMessage); +// decoded message payload. +private: + SrsStream* stream; + SrsPacket* packet; +public: + SrsCommonMessage(); + virtual ~SrsCommonMessage(); +public: + virtual bool can_decode(); +/** +* decode functions. +*/ +public: + /** + * decode packet from message payload. + */ + // TODO: use protocol to decode it. + virtual int decode_packet(SrsProtocol* protocol); + /** + * get the decoded packet which decoded by decode_packet(). + * @remark, user never free the pkt, the message will auto free it. + */ + virtual SrsPacket* get_packet(); +/** +* encode functions. +*/ +public: + /** + * get the perfered cid(chunk stream id) which sendout over. + */ + virtual int get_perfer_cid(); + /** + * set the encoded packet to encode_packet() to payload. + * @stream_id, the id of stream which is created by createStream. + * @remark, user never free the pkt, the message will auto free it. + * @return message itself. + */ + // TODO: refine the send methods. + virtual SrsCommonMessage* set_packet(SrsPacket* pkt, int stream_id); + /** + * encode the packet to message payload bytes. + * @remark there exists empty packet, so maybe the payload is NULL. + */ + virtual int encode_packet(); +}; + +/** +* shared ptr message. +* for audio/video/data message that need less memory copy. +* and only for output. +*/ +class SrsSharedPtrMessage : public ISrsMessage +{ +private: + typedef ISrsMessage super; +private: + struct SrsSharedPtr + { + char* payload; + int size; + int perfer_cid; + int shared_count; + + SrsSharedPtr(); + virtual ~SrsSharedPtr(); + }; + SrsSharedPtr* ptr; +public: + SrsSharedPtrMessage(); + virtual ~SrsSharedPtrMessage(); +public: + virtual bool can_decode(); +public: + /** + * set the shared payload. + * we will detach the payload of source, + * so ensure donot use it before. + */ + virtual int initialize(SrsCommonMessage* source); + /** + * set the shared payload. + * we will use the payload, donot use the payload of source. + */ + virtual int initialize(SrsCommonMessage* source, char* payload, int size); + virtual SrsSharedPtrMessage* copy(); +public: + /** + * get the perfered cid(chunk stream id) which sendout over. + */ + virtual int get_perfer_cid(); + /** + * ignored. + * for shared message, nothing should be done. + * use initialize() to set the data. + */ + virtual int encode_packet(); +}; + +/** +* the decoded message payload. +* @remark we seperate the packet from message, +* for the packet focus on logic and domain data, +* the message bind to the protocol and focus on protocol, such as header. +* we can merge the message and packet, using OOAD hierachy, packet extends from message, +* it's better for me to use components -- the message use the packet as payload. +*/ +class SrsPacket +{ +protected: + /** + * subpacket must override to provide the right class name. + */ + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsPacket); + } +public: + SrsPacket(); + virtual ~SrsPacket(); +/** +* decode functions. +*/ +public: + /** + * subpacket must override to decode packet from stream. + * @remark never invoke the super.decode, it always failed. + */ + virtual int decode(SrsStream* stream); +/** +* encode functions. +*/ +public: + virtual int get_perfer_cid(); + virtual int get_payload_length(); +public: + /** + * subpacket must override to provide the right message type. + */ + virtual int get_message_type(); + /** + * the subpacket can override this encode, + * for example, video and audio will directly set the payload withou memory copy, + * other packet which need to serialize/encode to bytes by override the + * get_size and encode_packet. + */ + virtual int encode(int& size, char*& payload); +protected: + /** + * subpacket can override to calc the packet size. + */ + virtual int get_size(); + /** + * subpacket can override to encode the payload to stream. + * @remark never invoke the super.encode_packet, it always failed. + */ + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 4.1.1. connect +* The client sends the connect command to the server to request +* connection to a server application instance. +*/ +class SrsConnectAppPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsConnectAppPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Object* command_object; +public: + SrsConnectAppPacket(); + virtual ~SrsConnectAppPacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; +/** +* response for SrsConnectAppPacket. +*/ +class SrsConnectAppResPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsConnectAppResPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Object* props; + SrsAmf0Object* info; +public: + SrsConnectAppResPacket(); + virtual ~SrsConnectAppResPacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 4.1.3. createStream +* The client sends this command to the server to create a logical +* channel for message communication The publishing of audio, video, and +* metadata is carried out over stream channel created using the +* createStream command. +*/ +class SrsCreateStreamPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsCreateStreamPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; +public: + SrsCreateStreamPacket(); + virtual ~SrsCreateStreamPacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; +/** +* response for SrsCreateStreamPacket. +*/ +class SrsCreateStreamResPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsCreateStreamResPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + double stream_id; +public: + SrsCreateStreamResPacket(double _transaction_id, double _stream_id); + virtual ~SrsCreateStreamResPacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; +/** +* client close stream packet. +*/ +class SrsCloseStreamPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsCloseStreamPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; +public: + SrsCloseStreamPacket(); + virtual ~SrsCloseStreamPacket(); +public: + virtual int decode(SrsStream* stream); +}; + +/** +* FMLE start publish: ReleaseStream/PublishStream +*/ +class SrsFMLEStartPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsFMLEStartPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + std::string stream_name; +public: + SrsFMLEStartPacket(); + virtual ~SrsFMLEStartPacket(); +public: + virtual int decode(SrsStream* stream); +}; +/** +* response for SrsFMLEStartPacket. +*/ +class SrsFMLEStartResPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsFMLEStartResPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + SrsAmf0Undefined* args; +public: + SrsFMLEStartResPacket(double _transaction_id); + virtual ~SrsFMLEStartResPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* FMLE/flash publish +* 4.2.6. Publish +* The client sends the publish command to publish a named stream to the +* server. Using this name, any client can play this stream and receive +* the published audio, video, and data messages. +*/ +class SrsPublishPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsPublishPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + std::string stream_name; + // optional, default to live. + std::string type; +public: + SrsPublishPacket(); + virtual ~SrsPublishPacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 4.2.8. pause +* The client sends the pause command to tell the server to pause or +* start playing. +*/ +class SrsPausePacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsPausePacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + bool is_pause; + double time_ms; +public: + SrsPausePacket(); + virtual ~SrsPausePacket(); +public: + virtual int decode(SrsStream* stream); +}; + +/** +* 4.2.1. play +* The client sends this command to the server to play a stream. +*/ +class SrsPlayPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsPlayPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + std::string stream_name; + double start; + double duration; + bool reset; +public: + SrsPlayPacket(); + virtual ~SrsPlayPacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; +/** +* response for SrsPlayPacket. +* @remark, user must set the stream_id in header. +*/ +class SrsPlayResPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsPlayResPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + SrsAmf0Object* desc; +public: + SrsPlayResPacket(); + virtual ~SrsPlayResPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* when bandwidth test done, notice client. +*/ +class SrsOnBWDonePacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsOnBWDonePacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* args; +public: + SrsOnBWDonePacket(); + virtual ~SrsOnBWDonePacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* onStatus command, AMF0 Call +* @remark, user must set the stream_id by SrsMessage.set_packet(). +*/ +class SrsOnStatusCallPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsOnStatusCallPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* args; + SrsAmf0Object* data; +public: + SrsOnStatusCallPacket(); + virtual ~SrsOnStatusCallPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* the special packet for the bandwidth test. +* actually, it's a SrsOnStatusCallPacket, but +* 1. encode with data field, to send data to client. +* 2. decode ignore the data field, donot care. +*/ +class SrsBandwidthPacket : public SrsPacket +{ +private: + typedef SrsPacket super; + disable_default_copy(SrsBandwidthPacket); +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsBandwidthPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* args; + SrsAmf0Object* data; +public: + SrsBandwidthPacket(); + virtual ~SrsBandwidthPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +public: + virtual int decode(SrsStream* stream); +public: + virtual bool is_starting_play(); + virtual bool is_stopped_play(); + virtual bool is_starting_publish(); + virtual bool is_stopped_publish(); + virtual bool is_flash_final(); + static SrsBandwidthPacket* create_finish(); + static SrsBandwidthPacket* create_start_play(); + static SrsBandwidthPacket* create_playing(); + static SrsBandwidthPacket* create_stop_play(); + static SrsBandwidthPacket* create_start_publish(); + static SrsBandwidthPacket* create_stop_publish(); +private: + virtual SrsBandwidthPacket* set_command(std::string command); +}; + +/** +* onStatus data, AMF0 Data +* @remark, user must set the stream_id by SrsMessage.set_packet(). +*/ +class SrsOnStatusDataPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsOnStatusDataPacket); + } +public: + std::string command_name; + SrsAmf0Object* data; +public: + SrsOnStatusDataPacket(); + virtual ~SrsOnStatusDataPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* AMF0Data RtmpSampleAccess +* @remark, user must set the stream_id by SrsMessage.set_packet(). +*/ +class SrsSampleAccessPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsSampleAccessPacket); + } +public: + std::string command_name; + bool video_sample_access; + bool audio_sample_access; +public: + SrsSampleAccessPacket(); + virtual ~SrsSampleAccessPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* the stream metadata. +* FMLE: @setDataFrame +* others: onMetaData +*/ +class SrsOnMetaDataPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsOnMetaDataPacket); + } +public: + std::string name; + SrsAmf0Object* metadata; +public: + SrsOnMetaDataPacket(); + virtual ~SrsOnMetaDataPacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 5.5. Window Acknowledgement Size (5) +* The client or the server sends this message to inform the peer which +* window size to use when sending acknowledgment. +*/ +class SrsSetWindowAckSizePacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsSetWindowAckSizePacket); + } +public: + int32_t ackowledgement_window_size; +public: + SrsSetWindowAckSizePacket(); + virtual ~SrsSetWindowAckSizePacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 5.3. Acknowledgement (3) +* The client or the server sends the acknowledgment to the peer after +* receiving bytes equal to the window size. +*/ +class SrsAcknowledgementPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsAcknowledgementPacket); + } +public: + int32_t sequence_number; +public: + SrsAcknowledgementPacket(); + virtual ~SrsAcknowledgementPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 7.1. Set Chunk Size +* Protocol control message 1, Set Chunk Size, is used to notify the +* peer about the new maximum chunk size. +*/ +class SrsSetChunkSizePacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsSetChunkSizePacket); + } +public: + int32_t chunk_size; +public: + SrsSetChunkSizePacket(); + virtual ~SrsSetChunkSizePacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 5.6. Set Peer Bandwidth (6) +* The client or the server sends this message to update the output +* bandwidth of the peer. +*/ +class SrsSetPeerBandwidthPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsSetPeerBandwidthPacket); + } +public: + int32_t bandwidth; + int8_t type; +public: + SrsSetPeerBandwidthPacket(); + virtual ~SrsSetPeerBandwidthPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +// 3.7. User Control message +enum SrcPCUCEventType +{ + // generally, 4bytes event-data + SrcPCUCStreamBegin = 0x00, + SrcPCUCStreamEOF = 0x01, + SrcPCUCStreamDry = 0x02, + SrcPCUCSetBufferLength = 0x03, // 8bytes event-data + SrcPCUCStreamIsRecorded = 0x04, + SrcPCUCPingRequest = 0x06, + SrcPCUCPingResponse = 0x07, +}; + +/** +* for the EventData is 4bytes. +* Stream Begin(=0) 4-bytes stream ID +* Stream EOF(=1) 4-bytes stream ID +* StreamDry(=2) 4-bytes stream ID +* SetBufferLength(=3) 8-bytes 4bytes stream ID, 4bytes buffer length. +* StreamIsRecorded(=4) 4-bytes stream ID +* PingRequest(=6) 4-bytes timestamp local server time +* PingResponse(=7) 4-bytes timestamp received ping request. +* +* 3.7. User Control message +* +------------------------------+------------------------- +* | Event Type ( 2- bytes ) | Event Data +* +------------------------------+------------------------- +* Figure 5 Pay load for the ‘User Control Message’. +*/ +class SrsUserControlPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsUserControlPacket); + } +public: + // @see: SrcPCUCEventType + int16_t event_type; + int32_t event_data; + /** + * 4bytes if event_type is SetBufferLength; otherwise 0. + */ + int32_t extra_data; +public: + SrsUserControlPacket(); + virtual ~SrsUserControlPacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* expect a specified message, drop others util got specified one. +* @pmsg, user must free it. NULL if not success. +* @ppacket, store in the pmsg, user must never free it. NULL if not success. +* @remark, only when success, user can use and must free the pmsg/ppacket. +*/ +template +int srs_rtmp_expect_message(SrsProtocol* protocol, SrsCommonMessage** pmsg, T** ppacket) +{ + *pmsg = NULL; + *ppacket = NULL; + + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { + srs_error("recv message failed. ret=%d", ret); + return ret; + } + srs_verbose("recv message success."); + + if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { + delete msg; + srs_error("decode message failed. ret=%d", ret); + return ret; + } + + T* pkt = dynamic_cast(msg->get_packet()); + if (!pkt) { + delete msg; + srs_trace("drop message(type=%d, size=%d, time=%"PRId64", sid=%d).", + msg->header.message_type, msg->header.payload_length, + msg->header.timestamp, msg->header.stream_id); + continue; + } + + *pmsg = msg; + *ppacket = pkt; + break; + } + + return ret; +} + #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 bf67fb9fc..0910d7530 --- a/trunk/src/core/srs_core_rtmp.cpp +++ b/trunk/src/core/srs_core_rtmp.cpp @@ -1,1218 +1,1228 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 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 - -using namespace std; - -/** -* 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" -// status error -#define StatusLevelError "error" -// code value -#define StatusCodeConnectSuccess "NetConnection.Connect.Success" -#define StatusCodeConnectRejected "NetConnection.Connect.Rejected" -#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() -{ -} - -SrsRequest* SrsRequest::copy() -{ - SrsRequest* cp = new SrsRequest(); - - cp->app = app; - cp->objectEncoding = objectEncoding; - cp->pageUrl = pageUrl; - cp->port = port; - cp->schema = schema; - cp->stream = stream; - cp->swfUrl = swfUrl; - cp->tcUrl = tcUrl; - cp->vhost = vhost; - - return cp; -} - -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 = RTMP_DEFAULT_PORTS; - 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_vhost_resolve(vhost, app); - strip(); - - // resolve the vhost from config - SrsConfDirective* parsed_vhost = config->get_vhost(vhost); - if (parsed_vhost) { - vhost = parsed_vhost->arg0(); - } - - // TODO: discovery the params of vhost. - - 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; -} - -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(string& str, 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); - - skt.set_recv_timeout(protocol->get_recv_timeout()); - skt.set_send_timeout(protocol->get_send_timeout()); - - 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(string app, 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(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; - } - } - - // SetChunkSize - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); - - pkt->chunk_size = SRS_CONF_DEFAULT_CHUNK_SIZE; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send set chunk size failed. " - "stream=%s, chunk_size=%d, ret=%d", - stream.c_str(), SRS_CONF_DEFAULT_CHUNK_SIZE, ret); - return ret; - } - } - - return ret; -} - -int SrsRtmpClient::publish(string stream, int stream_id) -{ - int ret = ERROR_SUCCESS; - - // SetChunkSize - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); - - pkt->chunk_size = SRS_CONF_DEFAULT_CHUNK_SIZE; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send set chunk size failed. " - "stream=%s, chunk_size=%d, ret=%d", - stream.c_str(), SRS_CONF_DEFAULT_CHUNK_SIZE, ret); - return ret; - } - } - - // 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_send_timeout() -{ - return protocol->get_send_timeout(); -} - -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); - - skt.set_recv_timeout(protocol->get_recv_timeout()); - skt.set_send_timeout(protocol->get_send_timeout()); - - 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, const char* server_ip) -{ - 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_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); - 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)); - data->set("srs_primary_authors", new SrsAmf0String(RTMP_SIG_SRS_PRIMARY_AUTHROS)); - - if (server_ip) { - data->set("srs_server_ip", new SrsAmf0String(server_ip)); - } - - 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; -} - -void SrsRtmp::response_connect_reject(SrsRequest *req, const char* desc) -{ - int ret = ERROR_SUCCESS; - - SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); - pkt->command_name = "_error"; - pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError)); - pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected)); - pkt->props->set(StatusDescription, new SrsAmf0String(desc)); - //pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); - - SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0); - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send connect app response rejected message failed. ret=%d", ret); - return; - } - srs_info("send connect app response rejected message success."); - - return; -} - -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, 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, 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, string& stream_name) -{ - int ret = ERROR_SUCCESS; - - type = SrsClientFlashPublish; - stream_name = req->stream_name; - - return ret; -} +/* +The MIT License (MIT) + +Copyright (c) 2013-2014 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 + +using namespace std; + +/** +* 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" +// status error +#define StatusLevelError "error" +// code value +#define StatusCodeConnectSuccess "NetConnection.Connect.Success" +#define StatusCodeConnectRejected "NetConnection.Connect.Rejected" +#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() +{ +} + +SrsRequest* SrsRequest::copy() +{ + SrsRequest* cp = new SrsRequest(); + + cp->app = app; + cp->objectEncoding = objectEncoding; + cp->pageUrl = pageUrl; + cp->port = port; + cp->schema = schema; + cp->stream = stream; + cp->swfUrl = swfUrl; + cp->tcUrl = tcUrl; + cp->vhost = vhost; + + return cp; +} + +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 = RTMP_DEFAULT_PORTS; + 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_vhost_resolve(vhost, app); + strip(); + + // resolve the vhost from config + SrsConfDirective* parsed_vhost = config->get_vhost(vhost); + if (parsed_vhost) { + vhost = parsed_vhost->arg0(); + } + + // TODO: discovery the params of vhost. + + 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; +} + +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"); +} + +string& SrsRequest::trim(string& str, 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); + + skt.set_recv_timeout(protocol->get_recv_timeout()); + skt.set_send_timeout(protocol->get_send_timeout()); + + 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(string app, 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(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; + } + } + + // SetChunkSize + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); + + pkt->chunk_size = SRS_CONF_DEFAULT_CHUNK_SIZE; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set chunk size failed. " + "stream=%s, chunk_size=%d, ret=%d", + stream.c_str(), SRS_CONF_DEFAULT_CHUNK_SIZE, ret); + return ret; + } + } + + return ret; +} + +int SrsRtmpClient::publish(string stream, int stream_id) +{ + int ret = ERROR_SUCCESS; + + // SetChunkSize + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); + + pkt->chunk_size = SRS_CONF_DEFAULT_CHUNK_SIZE; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set chunk size failed. " + "stream=%s, chunk_size=%d, ret=%d", + stream.c_str(), SRS_CONF_DEFAULT_CHUNK_SIZE, ret); + return ret; + } + } + + // 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_send_timeout() +{ + return protocol->get_send_timeout(); +} + +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); + + skt.set_recv_timeout(protocol->get_recv_timeout()); + skt.set_send_timeout(protocol->get_send_timeout()); + + 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, const char* server_ip) +{ + 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_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); + 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)); + data->set("srs_primary_authors", new SrsAmf0String(RTMP_SIG_SRS_PRIMARY_AUTHROS)); + + if (server_ip) { + data->set("srs_server_ip", new SrsAmf0String(server_ip)); + } + + 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; +} + +void SrsRtmp::response_connect_reject(SrsRequest *req, const char* desc) +{ + int ret = ERROR_SUCCESS; + + SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); + pkt->command_name = "_error"; + pkt->props->set(StatusLevel, new SrsAmf0String(StatusLevelError)); + pkt->props->set(StatusCode, new SrsAmf0String(StatusCodeConnectRejected)); + pkt->props->set(StatusDescription, new SrsAmf0String(desc)); + //pkt->props->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); + + SrsCommonMessage* msg = (new SrsCommonMessage())->set_packet(pkt, 0); + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send connect app response rejected message failed. ret=%d", ret); + return; + } + srs_info("send connect app response rejected message success."); + + return; +} + +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, 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); + } + if (dynamic_cast(pkt)) { + srs_info("level0 identify client by play."); + return identify_play_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, 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)) { + srs_info("level1 identify client by play."); + return identify_play_client(dynamic_cast(pkt), type, stream_name); + } + 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, 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, string& stream_name) +{ + int ret = ERROR_SUCCESS; + + type = SrsClientFlashPublish; + stream_name = req->stream_name; + + return ret; +} + +int SrsRtmp::identify_play_client(SrsPlayPacket* req, SrsClientType& type, string& stream_name) +{ + int ret = ERROR_SUCCESS; + + type = SrsClientPlay; + stream_name = req->stream_name; + + srs_trace("identity client type=play, stream_name=%s", stream_name.c_str()); + + 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 54b0948af..247f91590 --- a/trunk/src/core/srs_core_rtmp.hpp +++ b/trunk/src/core/srs_core_rtmp.hpp @@ -1,231 +1,234 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013-2014 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 - -class SrsProtocol; -class ISrsMessage; -class SrsCommonMessage; -class SrsCreateStreamPacket; -class SrsFMLEStartPacket; -class SrsPublishPacket; -class SrsSharedPtrMessage; -class SrsOnMetaDataPacket; - -/** -* the original request from client. -*/ -struct SrsRequest -{ - /** - * tcUrl: rtmp://request_vhost:port/app/stream - * support pass vhost in query string, such as: - * rtmp://ip:port/app?vhost=request_vhost/stream - * rtmp://ip:port/app...vhost...request_vhost/stream - */ - 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(); - - /** - * deep copy the request, for source to use it to support reload, - * for when initialize the source, the request is valid, - * when reload it, the request maybe invalid, so need to copy it. - */ - virtual SrsRequest* copy(); - - /** - * 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 -{ -protected: - 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_send_timeout(); - 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); - /** - * @param server_ip the ip of server. - */ - virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL); - virtual void response_connect_reject(SrsRequest* req, const char* desc); - 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-2014 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 + +class SrsProtocol; +class ISrsMessage; +class SrsCommonMessage; +class SrsCreateStreamPacket; +class SrsFMLEStartPacket; +class SrsPublishPacket; +class SrsSharedPtrMessage; +class SrsOnMetaDataPacket; +class SrsPlayPacket; + +/** +* the original request from client. +*/ +struct SrsRequest +{ + /** + * tcUrl: rtmp://request_vhost:port/app/stream + * support pass vhost in query string, such as: + * rtmp://ip:port/app?vhost=request_vhost/stream + * rtmp://ip:port/app...vhost...request_vhost/stream + */ + 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(); + + /** + * deep copy the request, for source to use it to support reload, + * for when initialize the source, the request is valid, + * when reload it, the request maybe invalid, so need to copy it. + */ + virtual SrsRequest* copy(); + + /** + * 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 +{ +protected: + 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_send_timeout(); + 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); + /** + * @param server_ip the ip of server. + */ + virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL); + virtual void response_connect_reject(SrsRequest* req, const char* desc); + 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); +private: + virtual int identify_play_client(SrsPlayPacket* req, SrsClientType& type, std::string& stream_name); +}; + #endif \ No newline at end of file