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 #include +#include +#include +#include #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 \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 #include +// for srs-librtmp, @see https://github.com/winlinvip/simple-rtmp-server/issues/213 +#ifndef _WIN32 #include +#endif + #include 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) {