From 0e03d019a865217bb286ce4fc7691a19f694b971 Mon Sep 17 00:00:00 2001
From: winlin <winlin@vip.126.com>
Date: Wed, 7 Jan 2015 13:29:30 +0800
Subject: [PATCH] for bug #215, srs rtmp dump support conn args. 2.0.90

---
 trunk/research/librtmp/srs_rtmp_dump.c | 241 +++++++++++++++++++++----
 trunk/src/core/srs_core.hpp            |   2 +-
 trunk/src/libs/srs_librtmp.cpp         |  62 ++++++-
 trunk/src/libs/srs_librtmp.hpp         |  15 +-
 trunk/src/rtmp/srs_protocol_rtmp.cpp   |   8 +-
 trunk/src/rtmp/srs_protocol_stack.cpp  |  11 +-
 6 files changed, 297 insertions(+), 42 deletions(-)

diff --git a/trunk/research/librtmp/srs_rtmp_dump.c b/trunk/research/librtmp/srs_rtmp_dump.c
index b89294f09..0d7aa6e25 100644
--- a/trunk/research/librtmp/srs_rtmp_dump.c
+++ b/trunk/research/librtmp/srs_rtmp_dump.c
@@ -26,34 +26,194 @@ gcc srs_rtmp_dump.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_rtmp_dum
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <assert.h>
 
 #include "../../objs/include/srs_librtmp.h"
 
+void parse_amf0_object(char* p, srs_amf0_t args)
+{
+    char opvt = 0; // object property value type.
+    const char* opnp = NULL; // object property name ptr.
+    const char* opvp = NULL; // object property value ptr.
+    
+    while (*p) {
+        switch (*p++) {
+            case 'O':
+                while (*p && *p++ != ':') {
+                }
+                if (*p++ == '1') {
+                    printf("amf0 object start\n");
+                } else {
+                    printf("amf0 object end\n");
+                }
+                break;
+            case 'N':
+                opvt = *p++;
+                if (*p++ != ':') {
+                    printf("object property must split by :.\n");
+                    exit(-1);
+                }
+                opnp = p++;
+                while (*p && *p++ != ':') {
+                }
+                p[-1] = 0;
+                opvp = p;
+                printf("amf0 %c property[%s]=%s\n", opvt, opnp, opvp);
+                switch(opvt) {
+                    case 'S':
+                        srs_amf0_object_property_set(args, opnp, srs_amf0_create_string(opvp));
+                        break;
+                    default:
+                        printf("unsupported object property.\n");
+                        exit(-1);
+                }
+                *p=0;
+                break;
+            default:
+                printf("only supports an object arg.\n");
+                exit(-1);
+        }
+    }
+}
+
 int main(int argc, char** argv)
 {
     printf("dump rtmp stream to flv file\n");
     printf("srs(simple-rtmp-server) client librtmp library.\n");
     printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
+    printf("@refer to http://rtmpdump.mplayerhq.hu/rtmpdump.1.html\n");
+    
+    struct option long_options[] = {
+        {"rtmp", required_argument, 0, 'r'},
+        {"flv", required_argument, 0, 'o'},
+        {"swfUrl", required_argument, 0, 's'},
+        {"tcUrl", required_argument, 0, 't'},
+        {"pageUrl", required_argument, 0, 'p'},
+        {"conn", required_argument, 0, 'C'},
+        {"complex", no_argument, 0, 'x'},
+        {"help", no_argument, 0, 'h'},
+        {0, 0, 0, 0}
+    };
+    
+    int show_help = 0;
+    int complex_handshake = 0;
+    const char* rtmp_url = NULL;
+    const char* output_flv = NULL;
+    const char* swfUrl = NULL;
+    const char* tcUrl = NULL;
+    const char* pageUrl = NULL;
+    srs_amf0_t args = NULL;
     
-    if (argc <= 2) {
-        printf("Usage: %s <rtmp_url> <flv_path>\n"
-            "   rtmp_url     RTMP stream url to play\n"
-            "   flv_path     The flv file path to save\n"
+    int opt = 0;
+    int option_index = 0;
+    while((opt = getopt_long(argc, argv, "hxr:o:s:t:p:C:", long_options, &option_index)) != -1){
+        switch(opt){
+            case 'r':
+                rtmp_url = optarg;
+                break;
+            case 'o':
+                output_flv = optarg;
+                break;
+            case 's':
+                swfUrl = optarg;
+                break;
+            case 't':
+                tcUrl = optarg;
+                break;
+            case 'p':
+                pageUrl = optarg;
+                break;
+            case 'C':
+                if (!args) {
+                    args = srs_amf0_create_object();
+                }
+                char* p = (char*)optarg;
+                parse_amf0_object(p, args);
+                break;
+            case 'x':
+                complex_handshake = 1;
+                break;
+            case 'h':
+                show_help = 1;
+                break;
+            default:
+                printf("unsupported opt.\n");
+                exit(-1);
+        }
+    }
+    
+    if (!rtmp_url || show_help) {
+        printf("Usage: %s -r url [-o output] [-s swfUrl] [-t tcUrl] [-p pageUrl] [-C conndata] [--complex] [-h]\n"
+            "Options:\n"
+            "   --rtmp -r url\n"
+            "       URL of the server and media content.\n"
+            "   --flv -o output\n"
+            "       Specify the output file name. If the name is − or is omitted, the stream is written to stdout.\n"
+            "   --complex\n"
+            "       Whether use complex handshake(srs-librtmp with ssl required).\n"
+            "   --swfUrl -s url\n"
+            "       URL of the SWF player for the media. By default no value will be sent.\n"
+            "   --tcUrl -t url\n"
+            "       URL of the target stream. Defaults to rtmp[e]://host[:port]/app/playpath.\n"
+            "   --pageUrl -p url\n"
+            "       URL of the web page in which the media was embedded. By default no value will be sent.\n"
+            "   −−conn −C type:data\n"
+            "       Append arbitrary AMF data to the Connect message. The type must be B for Boolean, N for number, S for string, O for object, or Z for null. For Booleans the data must be either 0 or 1 for FALSE or TRUE, respectively. Likewise for Objects the data must be 0 or 1 to end or begin an object, respectively. Data items in subobjects may be named, by prefixing the type with 'N' and specifying the name before the value, e.g. NB:myFlag:1. This option may be used multiple times to construct arbitrary AMF sequences. E.g.\n"
+            "       −C B:1 −C S:authMe −C O:1 −C NN:code:1.23 −C NS:flag:ok −C O:0\n"
+            "       -C O:1 -C NS:CONN:\" -C B:4Rg9vr0\" -C O:0\n"
+            "       @remark, support a object args only.\n"
+            "   --help -h\n"
+            "       Print a summary of command options.\n"
             "For example:\n"
-            "   %s rtmp://127.0.0.1:1935/live/livestream output.flv\n",
-            argv[0], argv[0]);
+            "   %s -r rtmp://127.0.0.1:1935/live/livestream -o output.flv\n"
+            "   %s -h\n",
+            argv[0], argv[0], argv[0]);
         exit(-1);
     }
     
-    srs_human_trace("rtmp url: %s", argv[1]);
-    srs_human_trace("flv path: %s", argv[2]);
-    srs_rtmp_t rtmp = srs_rtmp_create(argv[1]);
+    srs_human_trace("rtmp url: %s", rtmp_url);
+    srs_human_trace("handshake: %s", (complex_handshake? "complex" : "simple"));
+    srs_human_trace("swfUrl: %s", swfUrl);
+    srs_human_trace("pageUrl: %s", pageUrl);
+    srs_human_trace("tcUrl: %s", tcUrl);
+    if (output_flv) {
+        srs_human_trace("flv output path: %s", output_flv);
+    } else {
+        srs_human_trace("output to console");
+    }
+    
+    srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url);
+    
+    if (__srs_rtmp_dns_resolve(rtmp) != 0) {
+        srs_human_trace("dns resolve failed.");
+        goto rtmp_destroy;
+    }
+    
+    if (__srs_rtmp_connect_server(rtmp) != 0) {
+        srs_human_trace("connect to server failed.");
+        goto rtmp_destroy;
+    }
+    
+    if (complex_handshake) {
+        if (__srs_rtmp_do_complex_handshake(rtmp) != 0) {
+            srs_human_trace("complex handshake failed.");
+            goto rtmp_destroy;
+        }
+        srs_human_trace("do complex handshake success");
+    } else {
+        if (__srs_rtmp_do_simple_handshake(rtmp) != 0) {
+            srs_human_trace("simple handshake failed.");
+            goto rtmp_destroy;
+        }
+        srs_human_trace("do simple handshake success");
+    }
     
-    if (srs_rtmp_handshake(rtmp) != 0) {
-        srs_human_trace("simple handshake failed.");
+    if (srs_rtmp_set_connect_args(rtmp, tcUrl, swfUrl, pageUrl, args) != 0) {
+        srs_human_trace("set connect args failed.");
         goto rtmp_destroy;
     }
-    srs_human_trace("simple handshake success");
     
     if (srs_rtmp_connect_app(rtmp) != 0) {
         srs_human_trace("connect vhost/app failed.");
@@ -67,26 +227,31 @@ int main(int argc, char** argv)
     }
     srs_human_trace("play stream success");
     
-    srs_flv_t flv = srs_flv_open_write(argv[2]);
-    
-    // flv header
-    char header[9];
-    // 3bytes, signature, "FLV",
-    header[0] = 'F';
-    header[1] = 'L';
-    header[2] = 'V';
-    // 1bytes, version, 0x01,
-    header[3] = 0x01;
-    // 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
-    header[4] = 0x03; // audio + video.
-    // 4bytes, dataoffset
-    header[5] = 0x00;
-    header[6] = 0x00;
-    header[7] = 0x00;
-    header[8] = 0x09;
-    if (srs_flv_write_header(flv, header) != 0) {
-        srs_human_trace("write flv header failed.");
-        goto rtmp_destroy;
+    srs_flv_t flv = NULL;
+    if (output_flv) {
+        flv = srs_flv_open_write(output_flv);
+    }
+    
+    if (flv) {
+        // flv header
+        char header[9];
+        // 3bytes, signature, "FLV",
+        header[0] = 'F';
+        header[1] = 'L';
+        header[2] = 'V';
+        // 1bytes, version, 0x01,
+        header[3] = 0x01;
+        // 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
+        header[4] = 0x03; // audio + video.
+        // 4bytes, dataoffset
+        header[5] = 0x00;
+        header[6] = 0x00;
+        header[7] = 0x00;
+        header[8] = 0x09;
+        if (srs_flv_write_header(flv, header) != 0) {
+            srs_human_trace("write flv header failed.");
+            goto rtmp_destroy;
+        }
     }
     
     for (;;) {
@@ -105,9 +270,11 @@ int main(int argc, char** argv)
             goto rtmp_destroy;
         }
         
-        if (srs_flv_write_tag(flv, type, timestamp, data, size) != 0) {
-            srs_human_trace("dump rtmp packet failed.");
-            goto rtmp_destroy;
+        if (flv) {
+            if (srs_flv_write_tag(flv, type, timestamp, data, size) != 0) {
+                srs_human_trace("dump rtmp packet failed.");
+                goto rtmp_destroy;
+            }
         }
         
         free(data);
@@ -115,7 +282,9 @@ int main(int argc, char** argv)
     
 rtmp_destroy:
     srs_rtmp_destroy(rtmp);
-    srs_flv_close(flv);
+    if (flv) {
+        srs_flv_close(flv);
+    }
     srs_human_trace("completed");
     
     return 0;
diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp
index 327630442..3d2a02572 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       2
 #define VERSION_MINOR       0
-#define VERSION_REVISION    89
+#define VERSION_REVISION    90
 // server info.
 #define RTMP_SIG_SRS_KEY "SRS"
 #define RTMP_SIG_SRS_ROLE "origin/edge server"
diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp
index 835909b51..296a8588a 100644
--- a/trunk/src/libs/srs_librtmp.cpp
+++ b/trunk/src/libs/srs_librtmp.cpp
@@ -66,6 +66,9 @@ struct Context
     std::string app;
     std::string stream;
     std::string param;
+
+    // extra request object for connect to server, NULL to ignore.
+    SrsRequest* req;
     
     SrsRtmpClient* rtmp;
     SimpleSocketStream* skt;
@@ -93,12 +96,14 @@ struct Context
     Context() {
         rtmp = NULL;
         skt = NULL;
+        req = NULL;
         stream_id = 0;
         h264_sps_pps_sent = false;
         h264_sps_changed = false;
         h264_pps_changed = false;
     }
     virtual ~Context() {
+        srs_freep(req);
         srs_freep(rtmp);
         srs_freep(skt);
     }
@@ -595,6 +600,53 @@ int __srs_rtmp_connect_server(srs_rtmp_t rtmp)
     return ret;
 }
 
+int __srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp)
+{
+    int ret = ERROR_SUCCESS;
+    
+    srs_assert(rtmp != NULL);
+    Context* context = (Context*)rtmp;
+    
+    srs_assert(context->skt != NULL);
+    
+    // simple handshake
+    srs_freep(context->rtmp);
+    context->rtmp = new SrsRtmpClient(context->skt);
+    
+    if ((ret = context->rtmp->complex_handshake()) != ERROR_SUCCESS) {
+        return ret;
+    }
+    
+    return ret;
+}
+
+int srs_rtmp_set_connect_args(srs_rtmp_t rtmp, 
+    const char* tcUrl, const char* swfUrl, const char* pageUrl, srs_amf0_t args
+) {
+    int ret = ERROR_SUCCESS;
+    
+    srs_assert(rtmp != NULL);
+    Context* context = (Context*)rtmp;
+    
+    srs_freep(context->req);
+    context->req = new SrsRequest();
+    
+    if (args) {
+        context->req->args = (SrsAmf0Object*)args;
+    }
+    if (tcUrl) {
+        context->req->tcUrl = tcUrl;
+    }
+    if (swfUrl) {
+        context->req->swfUrl = swfUrl;
+    }
+    if (pageUrl) {
+        context->req->pageUrl = pageUrl;
+    }
+    
+    return ret;
+}
+
 int __srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp)
 {
     int ret = ERROR_SUCCESS;
@@ -628,7 +680,7 @@ int srs_rtmp_connect_app(srs_rtmp_t rtmp)
     );
     
     if ((ret = context->rtmp->connect_app(
-        context->app, tcUrl, NULL, true)) != ERROR_SUCCESS) 
+        context->app, tcUrl, context->req, true)) != ERROR_SUCCESS) 
     {
         return ret;
     }
@@ -1725,6 +1777,11 @@ srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed)
     return amf0;
 }
 
+srs_amf0_t srs_amf0_create_string(const char* value)
+{
+    return SrsAmf0Any::str(value);
+}
+
 srs_amf0_t srs_amf0_create_number(srs_amf0_number value)
 {
     return SrsAmf0Any::number(value);
@@ -2374,6 +2431,9 @@ int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int
     
     u_int32_t pts;
     if (srs_utils_parse_timestamp(timestamp, type, data, size, &pts) != 0) {
+        srs_human_trace("Video packet type=%s, dts=%d, pts=%d, size=%d, DecodeError", 
+            srs_human_flv_tag_type2string(type), timestamp, pts, size
+        );
         return ret;
     }
     
diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp
index c80c9efb9..c2e365407 100644
--- a/trunk/src/libs/srs_librtmp.hpp
+++ b/trunk/src/libs/srs_librtmp.hpp
@@ -86,6 +86,7 @@ extern int srs_version_revision();
 *************************************************************/
 // the RTMP handler.
 typedef void* srs_rtmp_t;
+typedef void* srs_amf0_t;
 
 /**
 * create/destroy a rtmp protocol stack.
@@ -142,6 +143,18 @@ extern int __srs_rtmp_dns_resolve(srs_rtmp_t rtmp);
 extern int __srs_rtmp_connect_server(srs_rtmp_t rtmp);
 // do simple handshake over socket.
 extern int __srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp);
+// do complex handshake over socket.
+extern int __srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp);
+
+/**
+* set the args of connect packet for rtmp.
+* @param args, the extra amf0 object args.
+* @remark, all params can be NULL to ignore.
+* @remark, user should never free the args for we directly use it.
+*/
+extern int srs_rtmp_set_connect_args(srs_rtmp_t rtmp, 
+    const char* tcUrl, const char* swfUrl, const char* pageUrl, srs_amf0_t args
+);
 
 /**
 * connect to rtmp vhost/app
@@ -546,7 +559,6 @@ extern srs_bool srs_flv_is_keyframe(char* data, int32_t size);
 **************************************************************
 *************************************************************/
 /* the output handler. */
-typedef void* srs_amf0_t;
 typedef double srs_amf0_number;
 /**
 * parse amf0 from data.
@@ -555,6 +567,7 @@ typedef double srs_amf0_number;
 * @remark user must free the parsed or created object by srs_amf0_free.
 */
 extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed);
+extern srs_amf0_t srs_amf0_create_string(const char* value);
 extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value);
 extern srs_amf0_t srs_amf0_create_ecma_array();
 extern srs_amf0_t srs_amf0_create_strict_array();
diff --git a/trunk/src/rtmp/srs_protocol_rtmp.cpp b/trunk/src/rtmp/srs_protocol_rtmp.cpp
index 713910bf9..7e44ec671 100644
--- a/trunk/src/rtmp/srs_protocol_rtmp.cpp
+++ b/trunk/src/rtmp/srs_protocol_rtmp.cpp
@@ -469,13 +469,17 @@ int SrsRtmpClient::connect_app2(
         SrsConnectAppPacket* pkt = new SrsConnectAppPacket();
         
         pkt->command_object->set("app", SrsAmf0Any::str(app.c_str()));
-        pkt->command_object->set("flashVer", SrsAmf0Any::str("WIN 12,0,0,41"));
+        pkt->command_object->set("flashVer", SrsAmf0Any::str("WIN 15,0,0,239"));
         if (req) {
             pkt->command_object->set("swfUrl", SrsAmf0Any::str(req->swfUrl.c_str()));
         } else {
             pkt->command_object->set("swfUrl", SrsAmf0Any::str());
         }
-        pkt->command_object->set("tcUrl", SrsAmf0Any::str(tc_url.c_str()));
+        if (req && req->tcUrl != "") {
+            pkt->command_object->set("tcUrl", SrsAmf0Any::str(req->tcUrl.c_str()));
+        } else {
+            pkt->command_object->set("tcUrl", SrsAmf0Any::str(tc_url.c_str()));
+        }
         pkt->command_object->set("fpad", SrsAmf0Any::boolean(false));
         pkt->command_object->set("capabilities", SrsAmf0Any::number(239));
         pkt->command_object->set("audioCodecs", SrsAmf0Any::number(3575));
diff --git a/trunk/src/rtmp/srs_protocol_stack.cpp b/trunk/src/rtmp/srs_protocol_stack.cpp
index 63461e6e1..c850e1401 100644
--- a/trunk/src/rtmp/srs_protocol_stack.cpp
+++ b/trunk/src/rtmp/srs_protocol_stack.cpp
@@ -31,7 +31,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <srs_protocol_buffer.hpp>
 #include <srs_protocol_utility.hpp>
 
+// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213
+#ifndef _WIN32
 #include <unistd.h>
+#endif
+
 #include <stdlib.h>
 using namespace std;
 
@@ -868,9 +872,14 @@ int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs)
 int SrsProtocol::do_iovs_send(iovec* iovs, int size)
 {
     int ret = ERROR_SUCCESS;
-    
+
     // the limits of writev iovs.
+    // for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213
+#ifndef _WIN32
     static int limits = sysconf(_SC_IOV_MAX);
+#else
+    static int limits = 1024;
+#endif
     
     // send in a time.
     if (size < limits) {