diff --git a/README.md b/README.md
index cc8776c0f..f0d7d70fd 100755
--- a/README.md
+++ b/README.md
@@ -242,6 +242,7 @@ Supported operating systems and hardware:
* 2013-10-17, Created.
## History
+* v1.0, 2014-06-28, response the call message with null. 0.9.137
* v1.0, 2014-06-28, fix [#110](https://github.com/winlinvip/simple-rtmp-server/issues/110), thread start segment fault, thread cycle stop destroy thread. 0.9.136
* v1.0, 2014-06-27, fix [#109](https://github.com/winlinvip/simple-rtmp-server/issues/109), fix the system jump time, adjust system startup time. 0.9.135
* v1.0, 2014-06-27, [1.0 mainline5(0.9.134)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline5) released. 41573 lines.
diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp
index 324243d6c..bb37c5b15 100644
--- a/trunk/src/app/srs_app_rtmp_conn.cpp
+++ b/trunk/src/app/srs_app_rtmp_conn.cpp
@@ -51,6 +51,7 @@ using namespace std;
#include
#include
#include
+#include
// when stream is busy, for example, streaming is already
// publishing, when a new client to request to publish,
@@ -852,6 +853,22 @@ int SrsRtmpConn::process_play_control_msg(SrsConsumer* consumer, SrsMessage* msg
return ret;
}
+ // call msg,
+ // support response null first,
+ // @see https://github.com/winlinvip/simple-rtmp-server/issues/106
+ SrsCallPacket* call = dynamic_cast(pkt);
+ if (call) {
+ SrsCallResPacket* res = new SrsCallResPacket(call->transaction_id);
+ res->command_object = SrsAmf0Any::null();
+ res->response = SrsAmf0Any::null();
+ if ((ret = rtmp->send_and_free_packet(res, 0)) != ERROR_SUCCESS) {
+ srs_warn("response call failed. ret=%d", ret);
+ return ret;
+ }
+ return ret;
+ }
+
+ // pause or other msg.
SrsPausePacket* pause = dynamic_cast(pkt);
if (!pause) {
srs_info("ignore all amf0/amf3 command except pause.");
diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp
index d0cf40dee..866a5e990 100644
--- a/trunk/src/app/srs_app_source.cpp
+++ b/trunk/src/app/srs_app_source.cpp
@@ -926,6 +926,10 @@ int SrsSource::on_meta_data(SrsMessage* msg, SrsOnMetaDataPacket* metadata)
metadata->metadata->set("server", SrsAmf0Any::str(RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")"));
metadata->metadata->set("authors", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY_AUTHROS));
+ // version, for example, 1.0.0
+ // add version to metadata, please donot remove it, for debug.
+ metadata->metadata->set("server_version", SrsAmf0Any::str(RTMP_SIG_SRS_VERSION));
+
if ((prop = metadata->metadata->get_property("audiosamplerate")) != NULL) {
if (prop->is_number()) {
sample_rate = (int)prop->to_number();
diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp
index 961e18c16..548c591d2 100644
--- a/trunk/src/core/srs_core.hpp
+++ b/trunk/src/core/srs_core.hpp
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// current release version
#define VERSION_MAJOR "0"
#define VERSION_MINOR "9"
-#define VERSION_REVISION "136"
+#define VERSION_REVISION "137"
#define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION
// server info.
#define RTMP_SIG_SRS_KEY "SRS"
diff --git a/trunk/src/rtmp/srs_protocol_rtmp_stack.cpp b/trunk/src/rtmp/srs_protocol_rtmp_stack.cpp
index 4cb503771..0a1ed1efc 100644
--- a/trunk/src/rtmp/srs_protocol_rtmp_stack.cpp
+++ b/trunk/src/rtmp/srs_protocol_rtmp_stack.cpp
@@ -700,6 +700,10 @@ int SrsProtocol::do_decode_message(SrsMessageHeader& header, SrsStream* stream,
srs_info("decode the AMF0/AMF3 closeStream message.");
*ppacket = packet = new SrsCloseStreamPacket();
return packet->decode(stream);
+ } else if (header.is_amf0_command() || header.is_amf3_command()) {
+ srs_info("decode the AMF0/AMF3 call message.");
+ *ppacket = packet = new SrsCallPacket();
+ return packet->decode(stream);
}
// default packet to drop message.
@@ -1976,6 +1980,202 @@ int SrsConnectAppResPacket::encode_packet(SrsStream* stream)
return ret;
}
+SrsCallPacket::SrsCallPacket()
+{
+ command_name = "";
+ transaction_id = 0;
+ command_object = NULL;
+ arguments = NULL;
+}
+
+SrsCallPacket::~SrsCallPacket()
+{
+ srs_freep(command_object);
+ srs_freep(arguments);
+}
+
+int SrsCallPacket::decode(SrsStream* stream)
+{
+ int ret = ERROR_SUCCESS;
+
+ if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
+ srs_error("amf0 decode call command_name failed. ret=%d", ret);
+ return ret;
+ }
+ if (command_name.empty()) {
+ ret = ERROR_RTMP_AMF0_DECODE;
+ srs_error("amf0 decode call command_name failed. "
+ "command_name=%s, ret=%d", command_name.c_str(), ret);
+ return ret;
+ }
+
+ if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
+ srs_error("amf0 decode call transaction_id failed. ret=%d", ret);
+ return ret;
+ }
+
+ srs_freep(command_object);
+ if ((ret = SrsAmf0Any::discovery(stream, &command_object)) != ERROR_SUCCESS) {
+ srs_error("amf0 discovery call command_object failed. ret=%d", ret);
+ return ret;
+ }
+ if ((ret = command_object->read(stream)) != ERROR_SUCCESS) {
+ srs_error("amf0 decode call command_object failed. ret=%d", ret);
+ return ret;
+ }
+
+ if (!stream->empty()) {
+ srs_freep(arguments);
+ if ((ret = SrsAmf0Any::discovery(stream, &arguments)) != ERROR_SUCCESS) {
+ srs_error("amf0 discovery call arguments failed. ret=%d", ret);
+ return ret;
+ }
+ if ((ret = arguments->read(stream)) != ERROR_SUCCESS) {
+ srs_error("amf0 decode call arguments failed. ret=%d", ret);
+ return ret;
+ }
+ }
+
+ srs_info("amf0 decode call packet success");
+
+ return ret;
+}
+
+int SrsCallPacket::get_perfer_cid()
+{
+ return RTMP_CID_OverConnection;
+}
+
+int SrsCallPacket::get_message_type()
+{
+ return RTMP_MSG_AMF0CommandMessage;
+}
+
+int SrsCallPacket::get_size()
+{
+ int size = 0;
+
+ size += SrsAmf0Size::str(command_name) + SrsAmf0Size::number();
+
+ if (command_object) {
+ size += command_object->total_size();
+ }
+
+ if (arguments) {
+ size += arguments->total_size();
+ }
+
+ return size;
+}
+
+int SrsCallPacket::encode_packet(SrsStream* stream)
+{
+ int ret = ERROR_SUCCESS;
+
+ if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
+ srs_error("encode command_name failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("encode command_name success.");
+
+ if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
+ srs_error("encode transaction_id failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("encode transaction_id success.");
+
+ if (command_object && (ret = command_object->write(stream)) != ERROR_SUCCESS) {
+ srs_error("encode command_object failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("encode command_object success.");
+
+ if (arguments && (ret = arguments->write(stream)) != ERROR_SUCCESS) {
+ srs_error("encode arguments failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("encode arguments success.");
+
+ srs_info("encode create stream request packet success.");
+
+ return ret;
+}
+
+SrsCallResPacket::SrsCallResPacket(double _transaction_id)
+{
+ command_name = RTMP_AMF0_COMMAND_RESULT;
+ transaction_id = _transaction_id;
+ command_object = NULL;
+ response = NULL;
+}
+
+SrsCallResPacket::~SrsCallResPacket()
+{
+ srs_freep(command_object);
+ srs_freep(response);
+}
+
+int SrsCallResPacket::get_perfer_cid()
+{
+ return RTMP_CID_OverConnection;
+}
+
+int SrsCallResPacket::get_message_type()
+{
+ return RTMP_MSG_AMF0CommandMessage;
+}
+
+int SrsCallResPacket::get_size()
+{
+ int size = 0;
+
+ size += SrsAmf0Size::str(command_name) + SrsAmf0Size::number();
+
+ if (command_object) {
+ size += command_object->total_size();
+ }
+
+ if (response) {
+ size += response->total_size();
+ }
+
+ return size;
+}
+
+int SrsCallResPacket::encode_packet(SrsStream* stream)
+{
+ int ret = ERROR_SUCCESS;
+
+ if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
+ srs_error("encode command_name failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("encode command_name success.");
+
+ if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
+ srs_error("encode transaction_id failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("encode transaction_id success.");
+
+ if (command_object && (ret = command_object->write(stream)) != ERROR_SUCCESS) {
+ srs_error("encode command_object failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("encode command_object success.");
+
+ if (response && (ret = response->write(stream)) != ERROR_SUCCESS) {
+ srs_error("encode response failed. ret=%d", ret);
+ return ret;
+ }
+ srs_verbose("encode response success.");
+
+
+ srs_info("encode call response packet success.");
+
+ return ret;
+}
+
SrsCreateStreamPacket::SrsCreateStreamPacket()
{
command_name = RTMP_AMF0_COMMAND_CREATE_STREAM;
diff --git a/trunk/src/rtmp/srs_protocol_rtmp_stack.hpp b/trunk/src/rtmp/srs_protocol_rtmp_stack.hpp
index 928660207..d42448ea9 100644
--- a/trunk/src/rtmp/srs_protocol_rtmp_stack.hpp
+++ b/trunk/src/rtmp/srs_protocol_rtmp_stack.hpp
@@ -533,6 +533,73 @@ protected:
virtual int encode_packet(SrsStream* stream);
};
+/**
+* 4.1.2. Call
+* The call method of the NetConnection object runs remote procedure
+* calls (RPC) at the receiving end. The called RPC name is passed as a
+* parameter to the call command.
+*/
+class SrsCallPacket : public SrsPacket
+{
+protected:
+ virtual const char* get_class_name()
+ {
+ return CLASS_NAME_STRING(SrsCallPacket);
+ }
+public:
+ std::string command_name;
+ double transaction_id;
+ /**
+ * If there exists any command info this
+ * is set, else this is set to null type.
+ */
+ SrsAmf0Any* command_object;
+ // Any optional arguments to be provided
+ SrsAmf0Any* arguments;
+public:
+ SrsCallPacket();
+ virtual ~SrsCallPacket();
+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 SrsCallPacket.
+*/
+class SrsCallResPacket : public SrsPacket
+{
+protected:
+ virtual const char* get_class_name()
+ {
+ return CLASS_NAME_STRING(SrsCallResPacket);
+ }
+public:
+ std::string command_name;
+ double transaction_id;
+ // If there exists any command info this
+ // is set, else this is set to null type.
+ SrsAmf0Any* command_object;
+ // Response from the method that was
+ // called.
+ SrsAmf0Any* response;
+public:
+ SrsCallResPacket(double _transaction_id);
+ virtual ~SrsCallResPacket();
+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
@@ -592,6 +659,7 @@ protected:
virtual int get_size();
virtual int encode_packet(SrsStream* stream);
};
+
/**
* client close stream packet.
*/