diff --git a/trunk/research/players/srs_player/release/srs_player.swf b/trunk/research/players/srs_player/release/srs_player.swf index ab7840f8b..aef340794 100755 Binary files a/trunk/research/players/srs_player/release/srs_player.swf and b/trunk/research/players/srs_player/release/srs_player.swf differ diff --git a/trunk/research/players/srs_player/src/srs_player.as b/trunk/research/players/srs_player/src/srs_player.as index ed1bc3e27..272035a56 100755 --- a/trunk/research/players/srs_player/src/srs_player.as +++ b/trunk/research/players/srs_player/src/srs_player.as @@ -137,7 +137,7 @@ package var ms:NetStream = this.media_stream; if (!ms) { - log("stream is null, ignore timer event."); + //log("stream is null, ignore timer event."); return; } @@ -395,6 +395,26 @@ package } update_context_items(); } + + // reject by server, maybe redirect. + if (evt.info.code == "NetConnection.Connect.Rejected") { + // RTMP 302 redirect. + if (evt.info.hasOwnProperty("ex") && evt.info.ex.code == 302) { + var streamName:String = url.substr(url.lastIndexOf("/") + 1); + url = evt.info.ex.redirect + "/" + streamName; + log("Async RTMP 302 Redirect to: " + url); + + // notify server. + media_conn.call("Redirected", null, evt.info.ex.redirect); + + // do 302. + setTimeout(function(){ + log("Async RTMP 302 Redirected."); + js_call_play(url, _width, _height, buffer_time, max_buffer_time, volume); + }, 1000); + return; + } + } // TODO: FIXME: failed event. if (evt.info.code != "NetConnection.Connect.Success") { diff --git a/trunk/src/kernel/srs_kernel_error.cpp b/trunk/src/kernel/srs_kernel_error.cpp index 093706cef..addfb02d6 100644 --- a/trunk/src/kernel/srs_kernel_error.cpp +++ b/trunk/src/kernel/srs_kernel_error.cpp @@ -26,7 +26,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bool srs_is_system_control_error(int error_code) { return error_code == ERROR_CONTROL_RTMP_CLOSE - || error_code == ERROR_CONTROL_REPUBLISH; + || error_code == ERROR_CONTROL_REPUBLISH + || error_code == ERROR_CONTROL_REDIRECT; } bool srs_is_client_gracefully_close(int error_code) diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index be34492d8..89852d04d 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -161,6 +161,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // system control message, // not an error, but special control logic. +// +// connection is redirect to another server. +#define ERROR_CONTROL_REDIRECT 2997 // sys ctl: rtmp close stream, support replay. #define ERROR_CONTROL_RTMP_CLOSE 2998 // FMLE stop publish and republish. diff --git a/trunk/src/protocol/srs_protocol_utility.cpp b/trunk/src/protocol/srs_protocol_utility.cpp index 96e38fa06..1f76d94fe 100644 --- a/trunk/src/protocol/srs_protocol_utility.cpp +++ b/trunk/src/protocol/srs_protocol_utility.cpp @@ -306,7 +306,9 @@ string srs_generate_rtmp_url(string server, int port, string vhost, string app, ss << "...vhost..." << vhost; } - ss << "/" << stream; + if (!stream.empty()) { + ss << "/" << stream; + } return ss.str(); } diff --git a/trunk/src/protocol/srs_rtmp_stack.cpp b/trunk/src/protocol/srs_rtmp_stack.cpp index ce1e9ed97..b4c43dd53 100644 --- a/trunk/src/protocol/srs_rtmp_stack.cpp +++ b/trunk/src/protocol/srs_rtmp_stack.cpp @@ -2575,6 +2575,57 @@ int SrsRtmpServer::response_connect_app(SrsRequest *req, const char* server_ip) return ret; } +#define SRS_RTMP_REDIRECT_TIMEOUT 3000 +int SrsRtmpServer::redirect(SrsRequest* r, string host, int port, bool& accepted) +{ + int ret = ERROR_SUCCESS; + + if (true) { + string url = srs_generate_rtmp_url(host, port, r->vhost, r->app, ""); + + SrsAmf0Object* ex = SrsAmf0Any::object(); + ex->set("code", SrsAmf0Any::number(302)); + ex->set("redirect", SrsAmf0Any::str(url.c_str())); + + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelError)); + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeConnectRejected)); + pkt->data->set(StatusDescription, SrsAmf0Any::str("RTMP 302 Redirect")); + pkt->data->set("ex", ex); + + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send redirect/rejected message failed. ret=%d", ret); + return ret; + } + srs_info("send redirect/rejected message success."); + } + + // client must response a call message. + // or we never know whether the client is ok to redirect. + protocol->set_recv_timeout(SRS_RTMP_REDIRECT_TIMEOUT * 1000); + if (true) { + SrsCommonMessage* msg = NULL; + SrsCallPacket* pkt = NULL; + if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { + // ignore any error of redirect response. + return ERROR_SUCCESS; + } + SrsAutoFree(SrsCommonMessage, msg); + SrsAutoFree(SrsCallPacket, pkt); + + string message; + if (pkt->arguments && pkt->arguments->is_string()) { + message = pkt->arguments->to_str(); + srs_info("confirm redirected to %s", message.c_str()); + accepted = true; + } + srs_info("get redirect response message"); + } + + return ret; +} + void SrsRtmpServer::response_connect_reject(SrsRequest* /*req*/, const char* desc) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/protocol/srs_rtmp_stack.hpp b/trunk/src/protocol/srs_rtmp_stack.hpp index 18a25cb62..583be52fb 100644 --- a/trunk/src/protocol/srs_rtmp_stack.hpp +++ b/trunk/src/protocol/srs_rtmp_stack.hpp @@ -929,6 +929,12 @@ public: * @param server_ip the ip of server. */ virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL); + /** + * redirect the connection to another rtmp server. + * @param the hostname or ip of target. + * @param whether the client accept the redirect. + */ + virtual int redirect(SrsRequest* r, std::string host, int port, bool& accepted); /** * reject the connect app request. */